欢迎访问个人网络日志知行空间
致谢:参考自
PROJECT(TEST_CPP)
MESSAGE(STATUS "Source Dir: " ${PROJECT_SOURCE_DIR})
MESSAGE(STATUS "Source Dir: " ${TEST_CPP_SOURCE_DIR})
MESSAGE(STATUS "Binary Dir: " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "Binary Dir: " ${TEST_CPP_BINARY_DIR})
SET(SRC_LIST main.cpp)
ADD_EXECUTABLE(main ${SRC_LIST})
PROJECT
指令,PROJECT(projectname [CXX] [C] [Java])
_BINARY_DIR
和_SOURCE_DIR
cmake
系统也预定义了PROJECT_BINARY_DIR
和PROJECT_SOURCE_DIR
PROJECT_SOURCE_DIR
,避免修改工程名称导致错误SET
指令,SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
,SET 指令可以用来显式的定义变量。
MESSAGE 指令
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
SATUS ,输出前缀为 -- 的信息
, FATAL_ERROR,立即终止所有 cmake 过程.
ADD_EXECUTABLE
的指令,格式:ADD_EXECUTABLE(main ${SRC_LIST})
${}
来引用变量,这是 cmake
的变量应用方式,但是,有一些例外,比
如在 IF 控制语句,变量是直接使用变量名引用,而不需要${}
指令是大小写无关的,参数和变量是大小写相关的
工程目录
├── CMakeLists.txt
├── COPYRIGHT.md
├── doc
├── README.md
├── run.sh
└── src
├── CMakeLists.txt
└── main.cpp
src
文件中的CMakeLists.txt
ADD_EXECUTABLE(main main.c)
project
中的CMakeLists.txt
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
构建完成后,会发现生成的目标文件 main
位于 build/bin 目录中
ADD_SUBDIRECTORY 指令
格式:ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
换个地方保存目标二进制
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
build/bin
和库的输出路径为 build/lib
ADD_EXECUTABLE
或ADD_LIBRARY
,如果需要改变目标存放路径,就在哪里加入上述的定义。INSTALL命令和CMAKE_INSTALL_PREFIX
变量
INSTALL
指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION ]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT ]
[OPTIONAL]
] [...])
TARGETS
后面跟的就是我们通过 ADD_EXECUTABLE
或者 ADD_LIBRARY
定义的目标文件,可能是可执行二进制、动态库、静态库。
目标类型就相对应的有三种:ARCHIVE 特指静态库
,LIBRARY 特指动态库
,RUNTIME特指可执行目标二进制
。
DESTINATION
定义了安装的路径,如果路径以/
开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX
其实就无效了。
安装普通文件:默认权限644
INSTALL(FILES files... DESTINATION
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT ]
[RENAME ] [OPTIONAL])
非目标文件的可执行程序安装(比如脚本之类): 与普通文件安装的区别是,默认权限755
INSTALL(PROGRAMS files... DESTINATION
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT ]
[RENAME ] [OPTIONAL])
安装目录:
INSTALL(DIRECTORY dirs... DESTINATION
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT ]
[[PATTERN | REGEX ]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
DIRECTORY 后面连接的是所在 Source 目录的相对路径,abc
和 abc/
有很大的区别,一个是把abc
文件夹安装到DESTINATION
路径下,一个是将abc/
目录下的所有文件及目录安装到DEST``路径下。PATTERN
用于使用正则表达式进行过滤,PERMISSIONS
用于指定 PATTERN
过滤后的文件权限。
安装时 CMAKE 脚本的执行:
INSTALL([[SCRIPT ] [CODE ]] [...])
SCRIPT
参数用于在安装时调用 cmake
脚本文件(也就是
文件)
CODE
参数用于执行 CMAKE
指令,必须以双引号括起来。比如:
INSTALL(CODE "MESSAGE(\"Sample install message.\")")
CMAKE_INSTALL_PREFIX 的默认定义是/usr/local
ADD_LIBRARY
命令
ADD_LIBRARY(libname
[SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
SHARED
动态库,STATIC
静态库。
EXCLUDE_FROM_ALL
参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手
工构建。
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
根据以上命令,会发现静态库没有被创建,这是因为一个 target
是不能重名的,所以,静态库构建指令无效。
可以修改静态库的名字,但按照一般的习惯,静态库名字跟动态库名字应该是一致的,这样就需要用到另外一个指令,SET_TARGET_PROPERTIES
,其基本语法为:
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1 prop2 value2 ...)
添加一句
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
就可以同时得到 libhello.so/libhello.a 两个库了。
# cmake 在构建一个新的 target 时,会尝试清理掉其他使用这个名字的库,为了回避这个问题,做如下配置
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT
1)
# 动态库版本号
# VERSION 指代动态库版本,SOVERSION 指代 API 版本。
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
INSTALL共享库和头文件,
INSTALL(TARGETS hello hello_staticLIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
静态库要使用 ARCHIVE
关键字。
设置头文件路径
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径
中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的
后面,你可以通过两种方式来进行控制搜索路径添加的方式:
CMAKE_INCLUDE_DIRECTORIES_BEFORE
,通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面AFTER
或者 BEFORE
参数,也可以控制是追加还是置前设置库文件路径,引入两个新的指令
LINK_DIRECTORIES
和 TARGET_LINK_LIBRARIES
.
TARGET_LINK_LIBRARIES(target library1 library2 ...)
为 target 添加需要链接的共享库。
CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH这两个是环境变量而不是 cmake
变量。使用方法是要在 bash
中用 export
或者在 csh
中使用 set
命令设置或者CMAKE_INCLUDE_PATH=/home/include cmake ..
等方式。
FIND_PATH 用来在指定路径中搜索文件名,比如:
FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)
SET(ENV_PATH $ENV{PATH})
MESSAGE(STATUS ${ENV_PATH})
SET(ENV{变量名} 值)
CMAKE_MAJOR_VERSION
,CMAKE 主版本号,比如 2.4.6 中的 2CMAKE_MINOR_VERSION
,CMAKE 次版本号,比如 2.4.6 中的 4CMAKE_PATCH_VERSION
,CMAKE 补丁等级,比如 2.4.6 中的 6CMAKE_SYSTEM
系统名称CMAKE_SYSTEM_NAME
不包含版本的系统名CMAKE_SYSTEM_VERSION
系统版本CMAKE_SYSTEM_PROCESSOR
处理器名称UNIX
在所有的类 UNIX
平台为 TRUE
,包括 OS X
和 cygwin
WIN32
在所有的 win32
平台为 TRUE
,包括 cygwin
MESSAGE("CMake Major Version: " ${CMAKE_MAJOR_VERSION})
MESSAGE("CMake Minor Version: " ${CMAKE_MINOR_VERSION})
MESSAGE("CMake Patch Version: " ${CMAKE_PATCH_VERSION})
MESSAGE("CMake System: " ${CMAKE_SYSTEM})
MESSAGE("CMake System Name: " ${CMAKE_SYSTEM_NAME})
MESSAGE("CMake System Version: " ${CMAKE_SYSTEM_VERSION})
MESSAGE("CMake System Processor: " ${CMAKE_SYSTEM_PROCESSOR})
MESSAGE("UNIX: " ${UNIX})
MESSAGE("WIN32: " ${WIN32})
# CMake Major Version: 3
# CMake Minor Version: 16
# CMake Patch Version: 3
# CMake System: Linux-5.15.0-56-generic
# CMake System Name: Linux
# CMake System Version: 5.15.0-56-generic
# CMake System Processor: x86_64
# UNIX: 1
# WIN32:
BUILD_SHARED_LIBS
,这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY
并没有指定库类型的情况下,默认编译生成的库都是静态库。如果 SET(BUILD_SHARED_LIBS ON)
后,默认生成的为动态库。CMAKE_C_FLAGS
:设置 C
编译选项,也可以通过指令 ADD_DEFINITIONS()
添加。CMAKE_CXX_FLAGS
设置 C++
编译选项,也可以通过指令 ADD_DEFINITIONS()
添加。向C/C++
编译器添加-D
定义,比如:
OPTION(ENABLE_DEBUG "Build the project using macro" OFF)
IF(ENABLE_DEBUG)
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC)
ENDIF(ENABLE_DEBUG)
如果代码中定义了
#ifdef ENABLE_DEBUG
#endif
这个代码块就会生效。
如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变
量设置。
ADD_DEPENDENCIES
,定义 target
依赖的其他 target
,确保在编译本 target
之前,其他的 target
已经被构建。ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来
自动构建源文件列表。
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
EXEC_PROGRAM
在 CMakeLists.txt
处理过程中执行命令,并不会在生成的 Makefile
中执行。具体语法
为:
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS ]
[OUTPUT_VARIABLE ]
[RETURN_VALUE ])
用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过
OUTPUT_VARIABLE
和 RETURN_VALUE
分别定义两个变量.
这个指令可以帮助你在 CMakeLists.txt
处理过程中支持任何命令,比如根据系统情况去
修改代码文件等等。
例如:在 src
目录执行 ls
命令,并把结果和返回值存下来
EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE LS_RVALUE)
IF(not LS_RVALUE)
MESSAGE(STATUS "ls result: " ${LS_OUTPUT})
ENDIF(not LS_RVALUE)
FILE 指令
FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB expressions]...) variable [RELATIVE path] [globbing]...)
FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expressions]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)
用来载入 CMakeLists.txt
文件,也用于载入预定义的 cmake
模块。
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
OPTIONAL
参数的作用是文件不存在也不会产生错误。可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH
中搜索这个模块并载入。载入的内容将在处理到 INCLUDE
语句是直接执行。
FIND_
系列指令主要包含一下指令:
FIND_FILE( name1 path1 path2 ...)
VAR
变量代表找到的文件全路径,包含文件名。
FIND_LIBRARY( name1 path1 path2 ...)
VAR
变量表示找到的库全路径,包含库文件名。
FIND_PATH( name1 path1 path2 ...)
VAR
变量代表包含这个文件的路径。
FIND_PROGRAM( name1 path1 path2 ...)
VAR
变量代表包含这个程序的全路径。
FIND_PACKAGE( [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])
用来调用预定义在 CMAKE_MODULE_PATH
下的 Find
模块,你也可以自己定义 Find
模块,通过 SET(CMAKE_MODULE_PATH dir)
将其放入工程的某个目录中供工程使用。
例如
在编译的时候需要使用curl
库,需要添加 curl
的头文件路径和库文件:
INCLUDE_DIRECTORIES
和 TARGET_LINK_LIBRARIES
指令添加INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
cmake
提供的 FindCURL
模块FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)
系统预定义的 Find
模块,使用方法一般如上例所示,每一个模块都会定义以下几个变量
_FOUND
,来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译_INCLUDE_DIR or _INCLUDES
_LIBRARY or TARGET_LINK_LIBRARIES
自定义cmake模块
如前面,自定义共享库,并安装到指定目录,当想在cmake
中通过find_package
命令找到并使用时,需要自定义Find
模块。
自定义cmake/FindHELLO.cmake
模块:
FIND_PATH(HELLO_INCLUDE_DIR hello.h /media/lx/data/code/test_cpp/hello/hello/include)
FIND_LIBRARY(HELLO_LIBRARY hello /media/lx/data/code/test_cpp/hello/hello/lib)
IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
SET(HELLO_FOUND TRUE)
ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
IF (HELLO_FOUND)
IF (NOT HELLO_FIND_QUIETLY)
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
ENDIF (NOT HELLO_FIND_QUIETLY)
ELSE (HELLO_FOUND)
IF (HELLO_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find hello library")
ENDIF (HELLO_FIND_REQUIRED)
ENDIF (HELLO_FOUND)
FIND_PATH
和FIND_LIBRARY
后面所跟的是头文件和库文件的安装路径。
# FIND_PACKAGE命令格式
FIND_PACKAGE( [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])
FIND_PACKAGE
的QUIET
参数,对应与我们编写的 FindHELLO
中的 HELLO_FIND_QUIETLY
,如果不指定这个参数,就会执行:
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
REQUIRED
参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于FindHELLO.cmake
模块中的 HELLO_FIND_REQUIRED
变量。
同样,在上面的模块中定义了 HELLO_FOUND
,
HELLO_INCLUDE_DIR
,HELLO_LIBRARY
变量供开发者在 FIND_PACKAGE
指令中使用。
IF(var) # 如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或 _NOTFOUND 时,表达式为真。
IF(NOT var ) # 与上述条件相反
IF(var1 AND var2) # 当两个变量都为真是为真
IF(var1 OR var2) # 当两个变量其中一个为真时为真
IF(COMMAND cmd) # 当给定的 cmd 确实是命令并可以调用是为真
IF(EXISTS dir/file) # 当目录名或者文件名存在时为真
IF(file1 IS_NEWER_THAN file2)# 当 file1 比 file2 新,或者 file1/file2 其中有一个不存在时为真
IF(IS_DIRECTORY dirname) # 当 dirname 是目录时,为真
# 当给定的变量或者字符串能够匹配正则表达式 `regex` 时为真
IF(variable MATCHES regex)
IF(string MATCHES regex)
# 数字比较表达式
IF(variable LESS number)
IF(string LESS number)
IF(variable GREATER number)
IF(string GREATER number)
IF(variable EQUAL number)
IF(string EQUAL number)
# 按照字母序的排列进行比较
IF(variable STRLESS string)
IF(string STRLESS string)
IF(variable STRGREATER string)
IF(string STRGREATER string)
IF(variable STREQUAL string)
IF(string STREQUAL string)
# 如果变量被定义,为真
IF(DEFINED variable)
WHILE 指令
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
真假判断条件可以参考 IF
指令。
FOREACH
FOREACH
指令的使用方法有三种形式
列表
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
如:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
范围
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
从 0 到 total 以1为步进
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
范围和步进
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从 start
开始到 stop
结束,以 step
为步进。
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
以上代码汇总示例工程见:https://gitee.com/lx_r/cmake_turorials
- https://github.com/gavinliu6/CMake-Practice-zh-CN