目录
- add_library
- add_custom_command
- add_custom_target
- add_dependencies
- add_subdirectory
- add_compile_definitions
- configure_file
- cmake_host_system_information
- file
- get_cmake_property
- 条件控制if
- get_filename_component
- include
- include_guard
- list
- macro
- message
- project
- set
- set_source_files_properties
- get_source_file_property
- set_target_properties
- set_tests_properties/set_property
- string
- target_compile_definitions
- target_include_directories
- target_link_libraries
- target_link_directories
- target_sources
- try_compile
- 控制流
本文主要收录了CMake中的语法(如条件语句,控制流foreach,while等),以及在其他博客中未收集到的常用命令(command)。
可以使用
--help-command <command>
命令行显示CMake内置命令的打印文档
CMake语言是不区分大小写的,但是参数区分大小写
局部变量不想在当前范围之外使用,可以在名称前加下划线,如 _var
add_library
普通库
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
- 参数说明:
- name:库名,在整个project中必须是唯一的;实际生成的库名会根据平台约定创建,如
lib<name>.a
(UNIX)或者<name>.lib
(Win) - 关键字参数:
- STATIC:用于创建静态库,即编译文件的打包存档,以便在链接其他目标时使用
- SHARED:用于创建动态库,即可以动态链接,并在运行时加载的库
- OBJECT:可将给定add_library的列表中的源码编译到目标文件,不将它们归档到静态库中,也不能将它们链接到共享对象中。如果需要一次性创建静态库和动态库,那么使用对象库尤其有用。——没有用过,待验证
- MODULE:又为DSO组。与SHARED库不同,它们不链接到项目中的任何目标,不过可以进行动态加载。该参数可以用于构建运行时插件。——没有用过,待验证
- source用来指定要编译的头文件和cpp文件,或者可以不用指定,在后面通过target_link_directories和target_sources指定
- name:库名,在整个project中必须是唯一的;实际生成的库名会根据平台约定创建,如
add_custom_command
添加一个自定义的构建命令。在构建时运行
第一种签名:添加一个自定义命令,会生成输出
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]] # 自定义命令的依赖项
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir] # 指定在哪个目录下运行COMMAND
[COMMENT comment] # 在构建时打印的消息
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
使用时有哪些限制?
第二种签名:为目标如library或者可执行文件添加一个自定义命令。这对于在构建目标之前或之后执行一些操作。只有在构建目标的时候执行,如果目标已经构建好了,命令 将不会执行
add_custom_command(TARGET <target>
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
add_custom_target
添加自定义的构建目标,这个目标不会有任何输出文件。使用add_dependencies()命令为其他targets添加依赖或被依赖。
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[JOB_POOL job_pool]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])
参数说明:
- ALL:表明这个目标应当被添加到默认的构建目标中以便每次会运行——?
add_dependencies
add_dependencies(<target> [<target-dependency>]...)
添加两个顶级target之间的依赖。顶级target就是使用 add_executable()
, add_library(), or add_custom_target()
命令创建的target
add_subdirectory
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
添加一个要构建的build子目录
add_compile_definitions
add_compile_definitions(<definition> ...)
添加预编译指令。参数可能会使用生成器表达式。注意,add_definitions被废弃
configure_file
configure_file(<input> <output>
[FILE_PERMISSIONS <permissions>...]
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
将input文件拷贝到output文件,input中形如@var@ (@开头和结尾的变量) 或者${VAR}的变量将会被在CMakeLists.txt中的变量 var 替换后,输出到output文件中。
参数说明:
- @ONLY:严格按照 @var@ 来替换
cmake_host_system_information
cmake_host_system_information(RESULT <variable> QUERY <key> ...)
查询运行CMake的主机系统的系统信息,一个多多个key可以被提供给用来查询对应的消息,查询出来的结果list存储在variable中。key可以是以下值:
Key | 说明 |
---|---|
NUMBER_OF_LOGICAL_CORES | Number of logical cores |
NUMBER_OF_PHYSICAL_CORES | Number of physical cores |
HOSTNAME | Hostname |
FQDN | Fully qualified domain name |
TOTAL_VIRTUAL_MEMORY | Total virtual memory in MiB 1 |
AVAILABLE_VIRTUAL_MEMORY | Available virtual memory in MiB 1 |
TOTAL_PHYSICAL_MEMORY | Total physical memory in MiB 1 |
AVAILABLE_PHYSICAL_MEMORY | Available physical memory in MiB 1 |
IS_64BIT | One if processor is 64Bit |
HAS_FPU | One if processor has floating point unit |
HAS_MMX | One if processor supports MMX instructions |
HAS_MMX_PLUS | One if processor supports Ext. MMX instructions |
HAS_SSE | One if processor supports SSE instructions |
HAS_SSE2 | One if processor supports SSE2 instructions |
HAS_SSE_FP | One if processor supports SSE FP instructions |
HAS_SSE_MMX | One if processor supports SSE MMX instructions |
HAS_AMD_3DNOW | One if processor supports 3DNow instructions |
HAS_AMD_3DNOW_PLUS | One if processor supports 3DNow+ instructions |
HAS_IA64 | One if IA64 processor emulating x86 |
HAS_SERIAL_NUMBER | One if processor has serial number |
PROCESSOR_SERIAL_NUMBER | Processor serial number |
PROCESSOR_NAME | Human readable processor name |
PROCESSOR_DESCRIPTION | Human readable full processor description |
OS_NAME | See CMAKE_HOST_SYSTEM_NAME |
OS_RELEASE | The OS sub-type e.g. on Windows Professional |
OS_VERSION | The OS build ID |
OS_PLATFORM | See CMAKE_HOST_SYSTEM_PROCESSOR |
file
可以替代configure_file,file允许将生成器表达式作为配置文件的一部分,但是file都会更新输出文件,这将强制重新构建依赖于该输出的所有目标。
读
file(READ <filename> <variable> [OFFSET <offset>] [LIMIT <max-in>] [HEX])
从filename中读取文件并存储到variable中。可选项是从偏移offset中开始,最多读取max-in个字节
写
文件系统
file(GLOB <variable>
[LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
[<globbing-expressions>...])
file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
[LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
[<globbing-expressions>...])
生成一个按照<globbing-expressions>
匹配的文件列表,存放到变量variable中。
GLOB:指定目录下查找文件
GLOB_RECURSE 会递归查找文件
路径转换
file(RELATIVE_PATH <variable> <directory> <file>)
file必须是一个绝对路径,计算directory相对于file的路径,将结果存到variable中。如:file(RELATIVE_PATH aaa /home/gg/iLearning/code/C++/ /home/zee001-w/gg/code/CMake/practice/main.cpp)
, aaa的值为:../CMake/practice/main.cpp
file(TO_NATIVE_PATH "<path>" <variable>) / file(TO_CMAKE_PATH "<path>" <variable>)
cmake风格的路径<path>
转换成平台相关的风格如在wind上是"\"
,在其他平台上是"/"
;TO_CMAKE_PATH相反。
get_cmake_property
获取cmake的全局属性 get_cmake_property(<var> <property>)
属性值property存储在变量 var 中。如果没有找到该属性,var将被设置为 NOTFOUND
条件控制if
if(<condition>)
<commands>
elseif(<condition>) # 可选,可重复
<commands>
else() # 可选,不能重复
<commands>
endif() # 必须有
- 基本表达式
if(<constant>)
如果constant是非0数值,ON,YES,TRUE,Y
则为true,如果是0,OFF,NO,FALSE,N,IGNORE,NOTFOUND,空字符串,以-NOTFOUND为后缀
,则为false - 逻辑运算符
if(NOT <condition>)
:condition为false则为true
if(<cond1> AND <cond2>)
:cond1和cond2都为true则为true
if(<cond1> OR <cond2>)
:cond1和cond2有一个为true则为true
get_filename_component
get_filename_component(<var> <FileName> <mode> [CACHE])
获取 FileName的组成部分,如它的父目录,后缀名,去掉目录后的文件名等。将结果保存在var中。
mode为以下:
DIRECTORY = Directory without file name
NAME = File name without directory
EXT = File name longest extension (.b.c from d/a.b.c)
NAME_WE = File name with neither the directory nor the longest extension
LAST_EXT = File name last extension (.c from d/a.b.c)
NAME_WLE = File name with neither the directory nor the last extension
PATH = Legacy alias for DIRECTORY (use for CMake <= 2.8.11)
示例:
get_filename_component(file_path ${CMAKE_CURRENT_SOURCE_DIR}/install/bin/test_install NAME)
get_filename_component(dir_path ${CMAKE_CURRENT_SOURCE_DIR}/install/bin/test_install DIRECTORY)
file_path 为:test_install, dir_path 为:/home/xx/iLearning/code/CMake/practice/install/bin
include
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>] [NO_POLICY_SCOPE])
加载和运行file或module
会指示CMake搜索 ${CMAKE_MODULE_PATH}
, 查找名为 colors.cmake的模块
include_guard
include_guard([DIRECTORY|GLOBAL])
类似C中的#pragma once,只包含一次,可选的参数为:
- DIRECTORY:应用于当前目录
- GLOBAL:应用于全局
list
list命令对列表进行操作,有四种用法。
- 读
list(LENGTH <list> <out-var>)
返回list的长度,是这个list的变量名
- 查
- 改
list(APPEND <list> [<element>...])
将element中的内容添加到list中
macro
macro(<name> [<arg1> ...])
<commands>
endmacro()
message
- 普通信息
message([<mode>] "message text" ...)
,mode为以下:- FATAL_ERROR:报cmake错误,停止编译和生成
- SEND_ERROR:报cmake错误,
- WARNING:cmake告警,编译继续进行
- DEPRECATION:如果设置了CMAKE_ERROR_DEPRECATED or CMAKE_WARN_DEPRECATED这两个变量,cmake汇报废弃错误或告警,否则没有任何信息
- STATUS:最常用的mode,用来输出信息
- 检查
message(<checkState> "message text" ...)
project
设置project名,在cmake最开始的时候使用
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...])
这个命令调用后,会自动设置以下变量:
PROJECT_NAME,CMAKE_PROJECT_NAME :值为project命令的第一个参数
PROJECT_SOURCE_DIR, _SOURCE_DIR:代码路径
PROJECT_BINARY_DIR, _BINARY_DIR:编译生成的二进制文件路径
示例:
project(cplusplus_test)
message(STATUS "project name is: ${PROJECT_NAME}")
message(STATUS "project name is: ${CMAKE_PROJECT_NAME}")
message(STATUS "PROJECT_SOURCE_DIR is: ${PROJECT_SOURCE_DIR}")
message(STATUS "<PROJECT-NAME>_SOURCE_DIR is: ${cplusplus_test_SOURCE_DIR}")
message(STATUS "PROJECT_BINARY_DIR is: ${PROJECT_BINARY_DIR}")
message(STATUS "cplusplus_test_BINARY_DIR is: ${cplusplus_test_BINARY_DIR}")
输出:
CMake中,C++是默认的编程语言。不过,我们还是建议使用LANGUAGES选项在project命令中显式地声明项目的语言。
set
设置变量值,如果如果不指定值,这个变量将会被取消,成为未定义的变量,效果同unset
。
set一个空和unset验证如下:
set(hello "good")
include(CMakePrintHelpers)
cmake_print_variables(hello)
set(hello)
cmake_print_variables(hello)
set(world "morning")
cmake_print_variables(world)
unset(world)
cmake_print_variables(world)
输出:
从输出可以看出,set不指定值和unset一个变量效果一样,都会将变量置为空。
如果有多个值,将会连接起来(用";"分隔)作为一个整体赋值给变量。
如下例所示:
set(VAR0 1 2 3 4)
set(VAR1 "hello" "good" "evening")
message(STATUS "var0 is: ${VAR0}, var1 is: ${VAR1}")
设置普通变量
set(<variable> <value>... [PARENT_SCOPE])
PARENT_SCOPE是在函数中用来修改父作用域的变量的。
设置缓存变量
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
默认不会覆盖已存在的缓存变量,通过可选参数FORCE可以强制重写。
- type必须是以下之一:
- BOOL:值为ON/OFF
- FILEPATH:文件路径
- PATH:文件所在目录的路径
- STRING:一行文本
- INTERNAL:文本,主要在运行过程中存储变量,不对外展示。
docstring
:一段总结性文本,其实就是说明或者提示信息
如果缓存变量之前没有设置,或者使用了FORCE,这个缓存变量会被设置为给定值。
缓存变量会存储在CMakeCache.txt文件中。示例:
set(VAR2 "hello" CACHE BOOL "it is my set BOOL test" FORCE)
set(VAR3 "good" CACHE FILEPATH "it is my set FILEPATH test" FORCE)
set(VAR4 "study" CACHE PATH "it is my set PATH test" FORCE)
set(VAR5 "beautiful" CACHE STRING "it is my set STRING test" FORCE)
set(VAR6 "perfect" CACHE INTERNAL "it is my set INTERNAL test" FORCE)
foreach(var VAR2 VAR3 VAR4 VAR5 VAR6)
message(STATUS "var is ${${var}}")
endforeach()
输出如下:
CMakeCache.txt中会生成如下内容:
//it is my set BOOL test
VAR2:BOOL=ON
//it is my set FILEPATH test
VAR3:FILEPATH=good
//it is my set PATH test
VAR4:PATH=study
//it is my set STRING test
VAR5:STRING=beautiful
......
########################
# INTERNAL cache entries
########################
//it is my set INTERNAL test
VAR6:INTERNAL=perfect
从验证结果来看,type值并没有强制作用,只作为给读者的提示。
设置环境变量
set(ENV{<variable>} [<value>])
设置环境变量,随后调用$ENV{<variable>}
会返回新值。这个命令设置的环境变量仅仅在当前CMake进程中有效。如果没有给value值,调用 ENV{<variable>}
或者 if <value>
将会是空字符串。value之后如果还有参数将会被忽略,并给出告警。
set_source_files_properties
set_source_files_properties(<files> ...
[DIRECTORY <dirs> ...]
[TARGET_DIRECTORY <targets> ...]
PROPERTIES <prop1> <value1>
[<prop2> <value2>] ...)
与给目标文件设置属性类似,设置文件属性。文件在CMake中也有属性,允许对构建系统进行非常细粒度的控制。源文件的可用属性列表点击这里。属性默认只在同一目录下的目标文件有效,可用通过以下两个目录设置可见范围:
- DIRECTORY 指定属性的可见范围
- TARGET_DIRECTORY 指定在哪些目标文件下可见
- TODO:设置文件属性的作用是啥
get_source_file_property
get_source_file_property(<variable> <file>
[DIRECTORY <dir> | TARGET_DIRECTORY <target>]
<property>)
获取给定文件<file>
的属性<property>
,将其存到<variable>
中
set_target_properties
设置生成目标的属性,设置的属性可以通过 get_property()
和 get_target_property()
这两个命令获取。cmake中所有的目标属性看这里
set_target_properties(target1 target2 ...
PROPERTIES
prop1 value1 prop2 value2 ...)
为target1,target2,… 设置属性,如
set_target_properties(
<target>
PROPERTIES
CXX_STANDARD 14
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE 1
)
常用的属性有以下:
-
POSITION_INDEPENDENT_CODE:设置生成位置无关代码所需的编译器标志 ?
-
SOVERSION:版本号,这个只对共享库有效,如果生成的是STATIC会报错,如值为2.0后生成如下:
-
OUTPUT_NAME:用来为生成的可执行文件或库文件设置文件名。这个属性会把add_executable/add_library的target名覆盖掉,生成由这个属性指定的名字。
-
DEBUG_POSTFIX:如果以Debug配置构建项目,则将_d后缀添加到生成的库(动态或静态)或可执行文件,其他如Release不会添加后缀。
-
PUBLIC_HEADER:这个属性要来设置头文件列表,用该属性指定的头文件在安装时可以通过install命令的PUBLIC_HEADER安装到指定位置
-
SKIP_BUILD_RPATH :告诉CMake是否生成适当的RPATH,以便能够在构建树中运行可执行文件——还不知道啥作用
-
BUILD_WITH_INSTALL_RPATH
-
CXX_VISIBILITY_PRESET 值为hidden将隐藏所有符号 ?当使用GNU编译器时,这将为目标添加-fvisibility=hidden标志
-
VISIBILITY_INLINES_HIDDEN 1:这将隐藏内联函数的符号,如果使用GNU编译器,这对应于-fvisibility-inlines-hidden
set_tests_properties/set_property
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
set_property(<GLOBAL |
DIRECTORY [<dir>] |
TARGET [<target1> ...] |
SOURCE [<src1> ...]
[DIRECTORY <dirs> ...] |
[TARGET_DIRECTORY <targets> ...]
INSTALL [<file1> ...] |
TEST [<test1> ...] |
CACHE [<entry1> ...] >
[APPEND] [APPEND_STRING]
PROPERTY <name> [<value1> ...])
可以设置超时时间
string
字符串的各种操作
Search and Replace
string(REPLACE <match-string> <replace-string> <out-var> <input>...)
用replace_string替换在input中出现的match_string,存储在out-var中
操作
string(APPEND <string_variable> [<input>...])
将所有input里的字符串添加到字符串变量中
string(TOUPPER <str> <output_variable>)
将字符串str转换为大写
Generation
string(TIMESTAMP <output_variable> [<format_string>] [UTC])
将当前日期或时间写入字符串 <output_variable> 中。可选的UTC标志要求时间为UTC时间而非本地时间。如果没有指定<format_string>,默认为:
%Y-%m-%dT%H:%M:%S for local time.
%Y-%m-%dT%H:%M:%SZ for UTC.
format_string格式
%%: A literal percent sign (%).
%d: 日(01-31).
%H: 时(00-23).
%I: The hour on a 12-hour clock (01-12).
%j: The day of the current year (001-366).
%m: The month of the current year (01-12).
%b: Abbreviated month name (e.g. Oct).
%B: Full month name (e.g. October).
%M: 分(00-59).
%s: Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
%S: The second of the current minute,60 represents a leap second. (00-60)
%U: The week number of the current year (00-53).
%w: The day of the current week. 0 is Sunday. (0-6)
%a: Abbreviated weekday name (e.g. Fri).
%A: Full weekday name (e.g. Friday).
%y: The last two digits of the current year (00-99)
%Y: The current year.
target_compile_definitions
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]
)
target 必须用add_executable 或者 add_library 先创建好。
可以定义在预处理阶段使用的命令。三个限定符:
- PRIVATE:编译定义将只应用于给定的目标,而不应用于其他目标
- INTERFACE:对给定目标的编译定义将只应用于使用它的目标
- PUBLIC:编译定义将应用于给定目标和使用它的所有其他目标
target_include_directories
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
添加头文件目录,注意不是具体的头文件,指定到具体头文件无法正确查找到。
target_link_libraries
target_link_libraries(
<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...
)
lib库的查找路径是什么?
这种<NAMESPACE>::<TARGET>
写法表明这两个库可能是通过find_package
得到,或者在项目的其他地方已经定义。
target_link_directories
target_link_directories(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
target_sources
target_sources(<target> # target必须是由add_executable() or add_library() or add_custom_target()已经创建好的
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
可以指定多个source路径,要指定到具体文件
try_compile
编译所有项目
try_compile(<resultVar> <bindir> <srcdir>
<projectName> [<targetName>] [CMAKE_FLAGS <flags>...]
[OUTPUT_VARIABLE <var>])
控制流
foreach
foreach(xxx) ... endforeach(
) 可用于变量列表上,表示重复特定任务,四种使用方式:
- foreach(loop_var arg1 arg2 …):提供循环列表和显式项列表
- foreach(loop_var range total) 或 foreach(loop_var range start top [step]) 通过指定一个范围,可以对整数进行循环
- foreach(loop_var IN LISTS [list1…]) :对列表值变量的循环
- foreach(loop_var IN ITEMS [item1 …]) :对变量的循环都可以结合break使用,以便从循环中跳出。