1.编写 CMake 配置/文件 CMakeLists.txt 。
2.执行命令 cmake PATH
或者 ccmake PATH
生成 Makefile(ccmake 和 cmake 的区别在于前者提供了一个交互式的界面)。其中, PATH 是 CMakeLists.txt 所在的目录。
3.使用 make
命令进行编译。
cmake指令不区分大小写
SET(<variable> [<value>] [CACHE TYPE DOCSTRING [FORCE]]);
向C/C++编译器添加-D定义,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC)
,参数之间用空格分割。
如果你的代码中定义了#ifdef ENABLE_DEBUG #endif
,这个代码块就会生效。
如果要添加其他的编译器开关,可以通过CMAKE_C_FLAGS
变量和CMAKE_CXX_FLAGS
变
量设置。
定义target依赖的其他target,确保在编译本target之前,其他的target已经被构
建。
ADD_DEPENDENCIES(<target-name> <depend-target1> <depend-target2> ...)
基本语法是:
AUX_SOURCE_DIRECTORY(<dir> VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前cmake还不能自动发现新添加的源文件。
比如
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
你也可以通过后面提到的FOREACH
指令来处理这个LIST
AUX_SOURCE_DIRECTORY
命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:
AUX_SOURCE_DIRECTORY(<dir> <variable>)
add_subdirectory(<source_dir> [<binary_dir>] [EXCLUDE_FROM_ALL])
命令 ADD_SUBDIRECTORY
指明本项目包含一个子目录
,这样
目录下的 CMakeLists.txt 文件和源代码也会被处理,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL
参数的含义是将这个目录从编译过程中排除,比如,工程的example,可能就需要工程构建完成后,再进入example目录单独进行构建。
add_subdirectory
指令(不论是否指定编译输出目录),我们都可以通过set
指令重新定义EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
变量来指定最终的目标二进制的位置(指最终生成的可执行文件或者最终的共享库,不包含编译生成的中间文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
和PROJECT_BINARY_DIR
变量,他们指的编译发生的当前目录,如果是内部编译,就相当于PROJECT_SOURCE_DIR
也就是工程代码所在目录,如果是外部编译,指的是外部编译所在目录,也就是build目录。
用来载入CMakeLists.txt文件,也用于载入预定义的cmake模块.
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
OPTIONAL
参数的作用是文件不存在也不会产生错误。
你可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH
中搜
索这个模块并载入。
载入的内容将在处理到INCLUDE
语句是直接执行。
FIND_系列指令主要包含一下指令:
FIND_FILE(<VAR> name1 path1 path2 ...)
VAR变量代表找到的文件全路径,包含文件名 FIND_LIBRARY(<VAR> name1 path1 path2 ...)
VAR变量表示找到的库全路径,包含库文件名 FIND_PATH(<VAR> name1 path1 path2 ...)
VAR变量代表包含这个文件的路径。 FIND_PROGRAM(<VAR> name1 path1 path2 ...)
VAR变量代表包含这个程序的全路径。FIND_LIBRARY示例:
FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)
Module mode
在这个模式,CMake搜索形如Find
的文件,首先在路径CMAKE_MODULE_PATH
下搜索,然后在CMake installation提供的Find Modules中寻找。文件如果找到,就会被CMake读取并执行,来寻找软件包,检查版本信息和产生需要的信息。你也可以自己定义Find
模块,通过SET(CMAKE_MODULE_PATH dir)
将其放入工程的某个目录中供工程使用。
只有find_packge
基本语法命令支持Module mode。
Config mode
在这个模式,CMake搜索
或
。如果版本被指定,则CMake会搜索形如
或
的文件。
基本语法命令和完整语法命令都支持Config mode。
FetchContent redirection mode
New in version 3.24: find_package()
的调用可以被重定向到FetchContent
模块提供的软件包内部。
如果使用基本语法命令,find_packge()
会首先以Module mode 搜索,如果未找到则再以Config mode搜索。如果使用完整语法命令,则只会以Config mode搜索。
find_package查找路径对应的环境变量如下:
_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER])
用来调用预定义在CMAKE_MODULE_PATH
下的Find
模块,我们在后面的章节会详细介绍FIND_PACKAGE的使用方法和Find模块的编写。
find_package(<PackageName> [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[NO_DEFAULT_PATH]
[NO_PACKAGE_ROOT_PATH]
[NO_CMAKE_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_PACKAGE_REGISTRY]
[NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
[NO_CMAKE_SYSTEM_PATH]
[NO_CMAKE_INSTALL_PREFIX]
[NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH])
CONFIG
与NO_MODULE
选项是等价的。名为
的缓存条目被创建用来保存configuration file存在的目录。
该模块可以通过ExternalProject模块支持的任何方法在配置阶段填充内容。ExternalProjectproject_add()
在构建阶段下载所需的内容,而FetchContent
模块使需要的内容立即可用,从而允许在配置阶段就能够在类似add_subdirectory()
,include()
或file()
的操作中使用需要的内容。
FetchContent_Declare(
<name>
<contentOptions>...
[SYSTEM]
[OVERRIDE_FIND_PACKAGE |
FIND_PACKAGE_ARGS args...]
)
选项可以是任何ExternalProject_Add()可以理解的download,update,patch选项。
在多数情况下,
In most cases,
是一系列定义下载方式和方式特定的信息比如 commit tag或者archive hash。
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
FetchContent_Declare(
myCompanyIcons
URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)
FetchContent_Declare(
myCompanyCertificates
SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs
SVN_REVISION -r12345
)
New in version 3.14.
FetchContent_MakeAvailable(<name1> [<name2>...])
该命令确保在返回时所有列出的依赖项可用。对于每个依赖项,都必须有一个FetchContent_Declare()
调用。
文件操作指令,基本语法为:
FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB variable [RELATIVE path] [globbing
expressions]...)
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)
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo1)
# 指定生成目标
add_executable(<exe_name> ${EXE_SRCS})
install(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]]
[...])
参数中的TARGETS
后面跟的就是我们通过add_executable
或者add_library
定义的目标文件,可能是可执行二进制、动态库、静态库。
目标类型也就相对应的有三种,ARCHIVE
特指静态库,LIBRARY
特指动态库,RUNTIME
特指可执行目标二进制。
DESTINATION
定义了安装的路径,如果路径以/
开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX
其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX
来定义安装路径,就要写成相对路径,即不要以/
开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/
普通文件的安装:
install(FILES files... 知道知道
DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果默认不定义权限PERMISSIONS
,安装后的权限为:
OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ
,即644权限。
非目标文件的可执行程序安装(比如脚本之类):
install(PROGRAMS files ...
DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
跟上面的FILES指令使用方法一样,唯一的不同是安装后权限为:
OWNER_EXECUTE, GROUP_EXECUTE,和WORLD_EXECUTE
,即755权限
目录的安装:
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
这里主要介绍其中的DIRECTORY
、PATTERN
以及PERMISSIONS
参数。
DIRECTORY
后面连接的是所在Source目录的相对路径,但务必注意:abc和abc/有很大的区别。
如果目录名不以/结尾,那么这个目录将被安装为目标路径下的abc,如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
PATTERN
用于使用正则表达式进行过滤,PERMISSIONS
用于指定PATTERN
过滤后的文件权限。
我们来看一个例子:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)
这条指令的执行结果是:
将icons
目录安装到
,将scripts/
中的内容安装到
不包含目录名为CVS的目录,对于scripts/*
文件指定权限为OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ
基本语法为:
IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)
另外一个指令是ELSEIF,总体把握一个原则,凡是出现IF的地方一定要有对应的
ENDIF.出现ELSEIF的地方,ENDIF是可选的。
表达式的使用方法如下:
_NOTFOUND
时,表达式为真。IF("hello" MATCHES "ell")
MESSAGE("true")
ENDIF("hello" MATCHES "ell")
数字比较表达式
按照字母序的排列进行比较
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
这时候就可以写成:
IF(WIN32)
ELSE()
ENDIF()
WHILE指令的语法是:
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
其真假判断条件可以参考IF指令。
FOREACH指令的使用方法有三种形式:
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
像我们前面使用的AUX_SOURCE_DIRECTORY
的例子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)
#输出
0
1
2
3
4
5
6
7
8
9
10
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从start开始到stop结束,以step为步进,FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
#输出
5
8
11
14
这个指令需要注意的是,直到遇到ENDFOREACH
指令,整个语句块才会得到真正的执行。
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:
#cmakedefine USE_MYMATH
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。
...
#ifdef USE_MYMATH
#include "math/MathFunction.h"
#else
#include
#endif
...
通过set(LIBRARY_OUTPUT_PATH <路径>)
来指定共享库输出的位置。
add_library(name [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL] <source1 source2 ... sourceN>)
cmake系统会自动为你生成libname.X
类型有三种:
EXCLUDE_FROM_ALL参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。
name
作为target
是不能重名的,这意味着,我们无法使用add_library
同时构建同名的静态库和动态库:
add_library(hello STATIC ${LIBHELLO_SRC})
add_library(hello SHARED ${LIBHELLO_SRC})
我们想要名字相同的静态库和动态库,因为target名称是唯一的,所以,我们肯定不能通过ADD_LIBRARY
指令来实现了。这时候我们需要用到另外一个指令:
SET_TARGET_PROPERTIES,其基本语法是:
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本。
add_library(hello SHARED ${LIBHELLO_SRC})
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
但是这样也不能同时得到libhello.so/libhello.a两个库。可以发现libhello.a
已经构建完成,位于build/lib
目录中,但是libhello.so
去消失了。这个问题的原因是:cmake在构建一
个新的target时,会尝试清理掉其他使用这个名字的库,因为,在构建libhello.a时,就会清理掉libhello.so.
为了回避这个问题,需要再次使用set_target_properties
定义
CLEAN_DIRECT_OUTPUT
属性。
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
这时候,我们再次进行构建,会发现build/lib目录中同时生成了libhello.so
和libhello.a
为了实现动态库版本号,我们仍然需要使用set_target_properties
指令。
具体使用方法如下:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION指代动态库版本,SOVERSION指代API版本。
include_directories([AFTER|BEFORE] [SYSTEM] <dir1> <dir2> ...)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的
后面,你可以通过两种方式来进行控制搜索路径添加的方式:
我们现在需要完成的任务是将目标文件链接到libhello,这里我们需要引入两个新的指令
link_directories
和target_link_library
link_directories
的全部语法是:
link_directories(<directory1> <directory2> ...)
这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径。
target_link_libraries
的全部语法是:
target_link_libraries(<target> <library1>
<debug | optimized> <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)
这里我们没有指定路径,但是,cmake仍然可以帮我们找到hello.h存放的路径,就是因
为我们设置了环境变量CMAKE_INCLUDE_PATH
。
如果你不使用FIND_PATH
,CMAKE_INCLUDE_PATH
变量的设置是没有作用的,你不能指
望它会直接为编译器命令添加参数-I
。
以此为例,CMAKE_LIBRARY_PATH
可以用在FIND_LIBRARY
中。
同样,因为这些变量直接为FIND_指令所使用,所以所有使用FIND_指令的cmake模块都会受益。
使用${}
进行变量的引用。在IF等语句中,是直接使用变量名而不通过${}
取值
主要有隐式定义和显式定义两种,前面举了一个隐式定义的例子,就是PROJECT
指令,他会隐式的定义
和
两个变量。
显式定义的例子我们前面也提到了,使用SET
指令,就可以构建一个自定义变量了。
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
_BINARY_DIR
PROJECT_BINARY_DIR
跟其他指令稍有区别,现在,你可以理解为他们是一致的。CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
_SOURCE_DIR
CMAKE_BINARY_DIR
等变量一致。PROJECT_SOURCE_DIR
跟其他指令稍有区别,现在,你可以理解为他们是一致的。CMAKE_CURRENT_SOURCE_DIR
CMAKE_CURRRENT_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR
一致,如果是out-of-source编译,他指的是target编译目录。ADD_SUBDIRECTORY(src bin)
可以更改这个变量的值。SET(EXECUTABLE_OUTPUT_PATH <新路径>)
并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。CMAKE_CURRENT_LIST_FILE
CMAKE_CURRENT_LIST_LINE
CMAKE_MODULE_PATH
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
这时候你就可以通过INCLUDE指令来调用自己的模块了。EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
PROJECT_NAME
PROJECT
指令定义的项目名称。使用$ENV{NAME}
指令就可以调用系统的环境变量了。
比如
MESSAGE(STATUS “HOME dir: $ENV{HOME}”)
设置环境变量的方式是:
SET(ENV{变量名} 值)
CMAKE_INCLUDE_CURRENT_DIR
自动添加CMAKE_CURRENT_BINARY_DIR
和CMAKE_CURRENT_SOURCE_DIR
到当前处理的CMakeLists.txt。相当于在每个CMakeLists.txt加入:
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
将工程提供的头文件目录始终至于系统头文件目录的前面,当你定义的头文件确实跟系统发
生冲突时可以提供一些帮助。
CMAKE_INCLUDE_PATH
和CMAKE_LIBRARY_PATH
我们在上一节已经提及。
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 _LIBRARIES
你可以通过
来判断模块是否被找到,如果没有找到,按照工程的需要关闭某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。
如果
为真,则将
加入INCLUDE_DIRECTORIES
,
将
加入TARGET_LINK_LIBRARIES
中。
FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/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_PACKAGE指令:
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets...]])
QUIET参数,对应与我们编写的FindHELLO.cmake
中的HELLO_FIND_QUIETLY
,如果不指定
这个参数,就会执行:
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
REQUIRED参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于FindHELLO.cmake
模块中的HELLO_FIND_REQUIRED
变量。
FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
ADD_EXECUTABLE(hello main.c)
INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)
为了能够让工程找到FindHELLO.cmake模块(存放在工程中的cmake目录)
我们在主工程文件CMakeLists.txt中加入:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
function(<name> [<arg1> ...])
<commands>
endfunction()
在函数体内,用${arg1}
来取出参数值。
除了正式参数外,还可以使用额外的预置变量:
ARGC
传递到该函数的参数数量ARGV0
,ARGV1
,ARGV2
,…它们将具有传递的参数的实际值。这有助于通过可选参数创建功能ARGV
保留了传递给该function的所有参数的列表,ARGN
保留了最后一个预期参数后的参数列表。return([PROPAGATE <var-name>...])
当此命令出现在被included的文件中( 通过include()
或find_package()
),它会导致当前文件的处理停止,并将控制返回到包含它的文件。
如果return()
在函数中被调用了,控制权被返还给函数的调用者。
此选项在父目录或函数调用者范围中设置或消除指定变量。
宏用来记录一串命令,以便以后调用
macro(<name> [<arg1> ...])
<commands>
endmacro()
macro(foo)
<commands>
endmacro()
foo()
Foo()
FOO()
cmake_language(CALL foo)
在function
中ARGN
, ARGC
, ARGV
and ARGV0
, ARGV1
, … 是真正的变量,在macro
中, 它们是替代字符串。
宏macro()
与function()
不同,它直接将代码就地展开,两者就如同C中的预处理和函数的关系,因此macro()
不能处理return()
。
由于在macro中ARGN, ARGC, ARGV, ARGV0
不是变量,所以无法使用这样的语句
if(ARGV1) # ARGV1 is not a variable
if(DEFINED ARGV2) # ARGV2 is not a variable
if(ARGC GREATER 2) # ARGC is not a variable
foreach(loop_var IN LISTS ARGN) # ARGN is not a variable
正确方式:
if(${ARGV1})
if(${ARGC} GREATER 2)
set(list_var "${ARGN}")
foreach(loop_var IN LISTS list_var)
block([SCOPE_FOR [POLICIES] [VARIABLES] ] [PROPAGATE <var-name>...])
<commands>
endblock()
block()与endblock()之间的所有命令均不会被调用,直到遇到endblock(),block里的代码在请求的域内被调用,执行完代码后block创建的域被销毁。
list(LENGTH <list> <output variable>)
Returns the list’s length.
list(GET <list> <element index> [<element index> ...] <output variable>)
Returns the list of elements specified by indices from the list.
list(JOIN <list> <glue> <output variable>)
Returns a string joining all list’s elements using the glue string.
list(SUBLIST <list> <begin> <length> <output variable>)
Returns a sublist of the given list. If
is 0, an empty list will be returned. If
is -1 or the list is smaller than
then the remaining elements of the list starting at
will be returned.
list(FIND <list> <value> <output variable>)
Returns the index of the element specified in the list or -1 if it wasn’t found.
list(APPEND <list> [<element> ...])
Appends elements to the list. If no variable named
exists in the current scope its value is treated as empty and the elements are appended to that empty list.
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])
list(REVERSE <list>)
Reverses the contents of the list in-place.
list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
Sorts the list in-place alphabetically.
Use the COMPARE keyword to select the comparison method for sorting. The
option should be one of:
STRING
: Sorts a list of strings alphabetically. This is the default behavior if the COMPARE option is not given.
FILE_BASENAME
: Sorts a list of pathnames of files by their basenames.
NATURAL
: Sorts a list of strings using natural order (see strverscmp(3) manual), i.e. such that contiguous digits are compared as whole numbers. For example: the following list 10.0 1.1 2.1 8.0 2.0 3.1 will be sorted as 1.1 2.0 2.1 3.1 8.0 10.0 if the NATURAL
comparison is selected where it will be sorted as 1.1 10.0 2.0 2.1 3.1 8.0 with the STRING comparison.
Use the CASE
keyword to select a case sensitive or case insensitive sort mode. The
option should be one of:
SENSITIVE: List items are sorted in a case-sensitive manner. This is the default behavior if the CASE option is not given.
INSENSITIVE: List items are sorted case insensitively. The order of items which differ only by upper/lowercase is not specified.
To control the sort order, the ORDER
keyword can be given. The
option should be one of:
ASCENDING: Sorts the list in ascending order. This is the default behavior when the ORDER option is not given.
DESCENDING: Sorts the list in descending order.
string(FIND <string> <substring> <output_variable> [REVERSE])
Return the position where the given
was found in the supplied
. If the REVERSE flag was used, the command will search for the position of the last occurrence of the specified
. If the
is not found, a position of -1 is returned.
The string(FIND) subcommand treats all strings as ASCII-only characters. The index stored in
will also be counted in bytes, so strings containing multi-byte characters may lead to unexpected results.
string(REPLACE <match_string>
<replace_string> <output_variable>
<input> [<input>...])
Replace all occurrences of
in the with
.
string(REGEX MATCH <regular_expression>
<output_variable> <input> [<input>...])
Match the
once and store the match in the
. All arguments are concatenated before matching. Regular expressions are specified in the subsection just below.
string(REGEX MATCHALL <regular_expression>
<output_variable> <input> [<input>...])
Match the
as many times as possible and store the matches in the
as a list. All arguments are concatenated before matching.
string(REGEX REPLACE <regular_expression>
<replacement_expression> <output_variable>
<input> [<input>...])
Match the
as many times as possible and substitute the
for the match in the output. All arguments are concatenated before matching.
The
may refer to parenthesis-delimited subexpressions of the match using \1
, \2
, …, \9
. Note that two backslashes (\\1
) are required in CMake code to get a backslash through argument parsing.
^
句首
$
句尾
.
匹配任意单个字符
\
匹配单个字符
[ ]
匹配任意方括号内的字符
[^ ]
匹配任意不再方括号内的字符
-
在方括号中指定范围,例如 [a-f]
代表[abcdef]
*
匹配前一个模式零或更多次
+
匹配前一个模式一或更多次
?
匹配前一个模式零或一次
|
匹配|
两侧的任一模式
()
像数学运算中的括号一样,可以用来改变运算优先级
string(APPEND <string_variable> [<input>...])
Append all the arguments to the string.
string(PREPEND <string_variable> [<input>...])
Prepend all the arguments to the string.
string(CONCAT <output_variable> [<input>...])
Concatenate all the arguments together and store the result in the named
.
string(JOIN <glue> <output_variable> [<input>...])
Join all the arguments together using the
string and store the result in the named
.
string(TOLOWER <string> <output_variable>)
string(TOUPPER <string> <output_variable>)
Convert
to lower/upper characters.
string(LENGTH )
Store in an
a given string’s length in bytes. Note that this means if
contains multi-byte characters, the result stored in
will not be the number of characters.
string(SUBSTRING <string> <begin> <length> <output_variable>)
Store in an
a substring of a given
. If
is -1 the remainder of the string starting at
will be returned.
Both
and
are counted in bytes, so care must be exercised if
could contain multi-byte characters.
string(STRIP <string> <output_variable>)
删除头和尾的空格
string(REPEAT <string> <count> <output_variable>)
Produce the output string as the input
repeated
times.
string(COMPARE LESS <string1> <string2> <output_variable>)
string(COMPARE GREATER <string1> <string2> <output_variable>)
string(COMPARE EQUAL <string1> <string2> <output_variable>)
string(COMPARE NOTEQUAL <string1> <string2> <output_variable>)
string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)
Compare the strings and store true or false in the
.