【CMake】常用指令
CMake
的指令是大小写不敏感的。
1、cmake_minimum_required
指定CMake
工具的具体版本,使用格式如下:
cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])
可选参数<policy_max>
是在3.12版本开始支持的。
<min>
和<policy_max>
支持的CMake
版本格式如下:major.minor[.patch[.tweak]]
,同时...
是字面量,不能省略。
当运行版本低于<min>
时,CMake
工具将会报错。当低于3.12版本时,...<policy_max>
将会被忽略。FATAL_ERROR
选项在2.6及以上版本会被忽略。在2.4及以下版本则会打印错误而不是告警。
这个命令会将CMAKE_MINIMUM_REQUIRED_VERSION
变量设置为<min>
。
2、project
设置项目名字。
在cmake_minimum_required
执行该命令。
语法如下:
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_SOURCE_DIR
,<PROJECT-NAME>_SOURCE_DIR
: 该项目的源代码绝对路径。PROJECT_BINARY_DIR
,<PROJECT-NAME>_BINARY_DIR
: 该项目的二进制绝对路径。PROJECT_IS_TOP_LEVEL
,<PROJECT-NAME>_IS_TOP_LEVEL
: 3.21版本新增特性,指明当前项目是否是顶级项目。
可选参数:
VERSION <version>
只有在CMP0048
策略被设置为NEW
时,该特性才会生效。
<version>
参数是由非负整数组成。最多有以下4部分:<major>[.<minor>[.<patch>[.<tweak]]]
,同时会设置以下变量:
PROJECT_VERSION
,<PROJECT-NAME>_VERSION
PROJECT_VERSION_MAJOR
,<PROJECT-NAME>_VERSION_MAJOR
PROJECT_VERSION_MINOR
,<PROJECT-NAME>_VERSION_MINOR
PROJECT_VERSION_PATCH
,<PROJECT-NAME>_VERSION_PATCH
PROJECT_VERSION_TWEAK
,<PROJECT-NAME>_VERSION_TWEAK
3.12新增特性:如果是顶层目录还会设置CMAKE_PROJECT_VERSION
。
DESCRIPTION <project-description-string>
3.9版本新增特性
设置以下变量:
PROJECT_DESCRIPTION
,<PROJECT-NAME>_DESCRIPTION
<project-description-string>
需要足够简短,不能太长。当处于顶层目录时,同时会设置CMAKE_PROJECT_DESCRIPTION
变量。
HOMEPAGE_URL <url-string>
设置以下变量:
PROJECT_HOMEPAGE_URL
,<PROJECT-NAME>_HOMEPAGE_URL
设置主页链接。当处于顶级目录时,同时会设置CMAKE_PROJECT_HOMEPAGE_URL
。
LANGUAGES <language-name>...
指定项目使用的编程语言。支持以下语言:C
, CXX
, CUDA
, OBJC
, OBJCXX
, Fortran
, HIP
, ISPC
, ASM
。如果未指定语言,默认使用C
和CXX
。可以将语言设置为NONE
, 或者使用LANGUAGES
关键字,但是指定空列表,可以跳过启用任何语言。使用空格分隔不同语言。
3. add_executable
3.1 正常可执行文件
添加可执行文件到项目中。
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
<name>
表示可执行文件目标名字,需要保证在项目中唯一。实际构建出来的可执行文件名根据不同的平台有所区别。如<name>.exe
或<name>
。
3.1版本新增特性:source
参数可以使用生成器表达式,语法$<...>
。
3.11版本新增特性:source
参数可以被忽略,但后续需要使用target_sources
命令添加。
默认情况下,可执行文件会在源代码树对应的构建树中生成。可以使用RUNTIME_OUTPUT_DIRECTORY
属性修改目标文件的输出路径。可以使用OUTPUT_NAME
属性修改最终文件的文件名,如果不指定,默认使用目标名。
设置WIN32
属性后,对应的WIN32_EXECUTABLE
属性也会被同时设置。
设置MACOSX_BUNDLE
属性后,对应的MACOSX_BUNDLE
属性也会被设置。
设置EXCLUDE_FROM_ALL
属性后,对应的EXCLUDE_FROM_ALL
属性也会被设置。
3.2 被导入可执行文件
add_executable(<name> IMPORTED [GLOBAL])
该命令用于导入项目之外的可执行文件。该target的IMPORTED
属性会被设置为true,同时该target
没有对应的规则用于生成。该target的作用域是本级及以下目录,同时可以使用GLOBAL
属性扩展可见性。被导入可执行文件相关的一些细节通过IMPORTED_
开头的属性指定。最重要的属性就是IMPORTED_LOCATION
,该属性指定可执行文件的路径。
3.3 别名target
add_executable(<name> ALIAS <target>)
为指定的target创建别名。<target>
不能是别名。
别名可以用于读取信息,并在自定义命令和目标中使用。别名不可以用于设置属性,所以在set_property
、set_target_properties
、target_link_libraries
等。
4. set
设置正常、缓存或环境变量
4.1 设置正常变量
set(<variable> <value>... [PARENT_SCOPE])
在当前函数或目录中设置或取消设置<variable>
。
如果<value>...
至少有一个参数,则将变量设置为指定值;如果没给出值,则取消设置变量,等效于unset(<variable>)
。
如果设置了PARENT_SCOPE
参数,那么就会设置上级作用域。每个新的目录和function()
命令都会创建一个新的作用域。block()
函数也会创建作用域。
set(PARENT_SCOPE)
会将变量值设置到父目录、调用函数或上级作用域。同时,变量在当前作用域中的值保持不变,PARENT_SCOPE
只是会改变上级作用域的值,并不会修改当前作用域的值。
block(PROPAGATE)
和return(PROPAGATE)
的作用等效于set(PARENT_SCOPE)
和unset(PARENT_SCOPE)
。
注意:
cmake在计算${var}变量引用的时候,会先从正常变量中获取,如果正常变量找不到会从缓存条目中查找。因此,当把一个正常变量unset之后,会导致先前隐藏的缓存条目暴露出来。例如,正常变量中有个叫name的变量,缓存中也有个name条目,当unset(name)之后,下次再查找变量时,会把缓存中的值返回。
如果不想暴露缓存中的值,就不要使用unset命令,而用set(<variable> "")代替。
4.2 设置缓存条目
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
因为缓存值旨在提供用户可设置的值,因此默认情况下,不会覆盖现有缓存项。可以使用FORCE
选项,强制覆盖现有缓存项。
<type>
必须是以下值之一:
- BOOL:布尔值ON/OFF
- FILEPATH:文件路径
- PATH:文件目录
- STRING:字符串
- INTERNAL:文本,会覆盖现有缓存
<docstring>
是缓存项的简短说明。
缓存项是可能没有设置type
的(通过cmake命令行设置变量 -D<var>=<value>
)。在这种情况下,set
命令会向缓存项设置一个type
。如果type
是PATH
或者FILEPATH
,而且<value>
是相对路径,那么set
命令会相对于当前工作目录,将相对路径转为绝对路径。
4.3 设置环境变量
set(ENV{<variable>} [<value>])
设置环境变量,随后使用$ENV{<variable>}
访问环境变量。
这个命令只会影响当前CMake进程,不会影响调用cmake的进程,也不会影响系统环境变量,以及后续的构建和测试进程。
如果没有给出<value>
,或者<value>
是空字符串,这个命令会清楚环境变量的所有值。<value>
后续参数会被忽略掉。
5. configure_file
configure_file(<input> <output>
[NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
FILE_PERMISSIONS <permissions>...]
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
将<input>
复制到<output>
,中间再做一些转换,例如变量替换之类的。
参数解释如下:
<input>
:输入文件路径,如果是相对路径,则基于CMAKE_CURRENT_SOURCE_DIR
计算文件路径。<output>
: 输出文件路径或输出目录,如果是相对路径,则基于CMAKE_CURRENT_BINARY_DIR
计算路径。如果输出目录已存在,则用与输入文件相同的文件名输出文件,如果目录不存在,则创建目录。NO_SOURCE_PERMISSIONS
: 输出文件不使用输入文件的权限,默认权限为644。USE_SOURCE_PERMISSIONS
: 输出文件使用输入文件的权限,默认配置项。FILE_PERMISSIONS <permissions>...
: 自定义输出文件权限。COPYONLY
: 仅复制,不做任何修改。不能和NEWLINE_STYLE
参数一起使用。ESCAPE_QUOTES
: 用反斜杠转义引号。@ONLY
: 限制变量替换形式为@VAR@
,不使用${VAR}
的形式。NEWLINE_STYLE <style>
: 指定输出文件的换行样式,UNIX
和LF
指定\n
,DOS
、WIN32
和CRLF
指定\r\n
。
转换
在输入文件中的变量引用格式有以下3种:@VAR@
、${VAR}
、CACHE{VAR}
。环境变量引用格式如下:$ENV{VAR}
。如果变量未定义,则会使用空字符串替换。
输入文件中的以下代码:
#cmakedefine VAR ...
会被替换为:
#define VAR ...
或
/* #undef VAR */
具体会被替换成那种格式,需要根据if(VAR)
命令的返回值判断,真值返回第一种,假值返回第二种。
#cmakedefine01 VAR
会被替换为:
#define VAR 0
或
#define VAR 1
取决于if(VAR)
返回真值还是假值。
6、target_include_directories
向一个目标中添加头文件搜索路径。
语法如下:
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
指定target的头文件搜索路径,target使用前必须先定义,使用add_executable
或add_library
定义target
,target
不能是别名target。
使用BEFORE
和AFTER
,可以选择将头文件搜索目录插到最前面还是最后。
INTERFACE
、PUBLIC
、PRIVATE
关键字指定后面参数的作用域。PRIVATE
和PUBLIC
作用域会将条目添加到target
的INCLUDE_DIRECTORIES
属性(本target构建需要的头文件搜索路径)。PUBLIC
和INTERFACE
作用域会将条目添加到target
的INTERFACE_INCLUDE_DIRECTORIES
属性(作为依赖库需要的头文件搜索路径)。
对同一个target
重复调用,会按照调用顺序,添加头文件搜索路径。
SYSTEM
选项指定这些目录是系统头文件搜索路径,这可能会抑制告警,在依赖检查时,会跳过包含的头文件,系统头文件搜索路径会在普通头文件搜索路径之后使用。
SYSTEM
与PUBLIC
或INTERFACE
一起使用的时候,会将包含路径添加到INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
属性中。
这个命令的头文件包含路径也可以使用生成器表达式,语法:$<...>
。
头文件包含路径可以是绝对路径,也可以是相对路径,如果是相对路径,就会基于CMAKE_CURRENT_SOURCE_DIR
计算出绝对路径。如果路径以生成器表达式开始,那么该路径会被认为是一个绝对路径,不会做任何修改,直接使用。
头文件包含目录在构建和安装的场景下,会有区别,在构建和安装的时候,可能需要使用不同的头文件包含路径。BUILD_INTERFACE
和INSTALL_INTERFACE
生成表达式可以用来区分不同的场景。
在INSTALL_INTERFACE
中可以使用相对路径,最终他会基于安装目录生成绝对路径。在BUILD_INTERFACE
中不可以使用相对路径,因为他不会自动转换相对路径。
示例如下:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
7、add_library
7.1 正常库
add_library(<name> [<type>] [EXCLUDE_FROM_ALL] <sources>...)
创建一个库文件。
<type>
选项如下:
- STATIC:生成静态库
- SHARED:生成动态库
- MODULE:插件,可能不会链接到其他target,但是可以通过类似
dlopen
的方法,在运行时打开使用
type
默认值由BUILD_SHARED_LIBS
变量值指定。行为如下:
if(BUILD_SHARED_LIBS)
add_library(example SHARED ${sources})
else()
add_library(example STATIC ${sources})
endif()
EXCLUDE_FROM_ALL
:自动设置target
的EXCLUDE_FROM_ALL
属性。
<name>
属性在一个项目中应该是唯一的。构建的库文件名根据不同的平台会有差异,lib<name>.a
或<name>.lib
。
SHARED
和MODULE
类型的库的POSITION_INDEPENDENT_CODE
会被自动置为ON
。
SHARED
和STATIC
库的FRAMEWORK
属性会被设置,用于创建macOS框架。
如果库没有导出符号表,那么就不能声明为SHARED
库。
7.2 对象库
add_library(<name> OBJECT <sources>...)
添加一个对象库(只把源文件编译成对象文件,不会链接和归档源文件)。
其他目标可以使用对象库中的文件作为输入源,表达式如下:$<TARGET_OBJECTS:objlib>
。
示例如下:
add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)
7.3 接口库
add_library(<name> INTERFACE)
接口库会指定依赖库的使用前置条件,但是不会编译源文件,也不会生成库文件。
add_library(<name> INTERFACE [EXCLUDE_FROM_ALL] <sources>...)
将<sources>...
包含在接口库中。
7.4 导入库
add_library(<name> <type> IMPORTED [GLOBAL])
<type>
有效取值如下:
- STATIC, SHARED, MODULE, UNKNOWN:
引用项目外部库文件,target
的IMPORTED_LOCATION
属性指定了外部库文件所在路径。 - OBJECT:
引用项目外部的对象文件。target
的IMPORTED_OBJECTS
属性指定了外部对象文件所在路径。 - INTERFACE:
不引用库和对象文件。
GLOBAL
选项:让target名字全局可见。
7.5 别名库
add_library(<name> ALIAS <target>)
创建库的别名。
8、add_subdirectory
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])
添加构建子目录
source_dir
指定构建的子目录路径,如果是相对路径,则基于当前目录计算路径。binary_dir
指定输出文件目录。如果没指定binary_dir
,则使用source_dir
作为输出文件目录。
如果source_dir
中有CMakeLists.txt
文件,则会立即执行该文件,执行完之后在接着执行当前文件。
如果指定了EXCLUDE_FROM_ALL
参数,那么就会将子目录的EXCLUDE_FROM_ALL
属性设置为true,那么在make的时候,这个子目录的所有target将不会构建。
SYSTEM
参数会设置子目录的SYSTEM
参数,这个属性会用来初始化非导入target的SYSTEM属性。
9、target_link_libraries
target_link_libraries(<target> ... <item>... ...)
指定链接的库文件。
<item>
选项如下:
- 库目标名:链接的时候会生成库文件的全路径
- 库文件的绝对路径
- 库文件名:例如foo -> -lfoo 或 foo.lib
- 链接选项:以
-
开头,而不是-l
或-framework
。这个链接选项会被插入到链接库的地方。插入位置可能不对。可以使用target
的LINK_OPTIONS
属性,和target_link_options
命令显式设置链接选项。 - 生成器表达式:
$<...>
生成器表达式可以计算出上述的选项,或者是选项列表。如果...
出现了分号,那么需要在生成器表达式外面加上双引号,以确保把这个表达式当成一个完整的item。生成器表达式也可以作为上述item的一部分使用。例如:foo$<1:_d>
。 debug
、optimized
、general
关键字后面跟<item>
。这个<item>
仅会用于对应的构建配置。debug
对应Debug
配置,或者DEBUG_CONFIGURATIONS
全局属性指定的配置。optimized
对应除Debug
以外的所有配置。general
对应所有配置,这个是可选,默认配置。
item
中包含::
,如Foo::Bar
,会被认为是导入库或别名库。
target_link_libraries(<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
该命令既可以指定链接依赖,又可以指定链接接口。
PUBLIC
后面的item
可以被链接,同时也会添加到链接接口中;PRIVATE
后面的item
可以被链接,但不会加入到链接接口中;INTERFACE
后面的item
会加入到链接接口中,但不会进行链接。
10、if
条件执行指令。
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
复合条件计算优先级如下:
- 括号
- 一元测试:EXISTS, COMMAND, 和 DEFINED
- 二元测试:EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL, STREQUAL, STRLESS, STRLESS_EQUAL, STRGREATER, STRGREATER_EQUAL, VERSION_EQUAL, VERSION_LESS, VERSION_LESS_EQUAL, VERSION_GREATER, VERSION_GREATER_EQUAL, PATH_EQUAL, 和 MATCHES
- 一元逻辑操作符:NOT
- 二元逻辑操作符:AND,OR
10.1 基础表达式
if(<constant>)
真值:1, ON, YES, TRUE, Y, 非0数字(浮点数也行)
假值:0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串,以-NOTFOUND
结尾
如果不是上述值,则被当成变量或者字符串
if(<variable>)
如果variable
的值是真值,那么判断条件就是true
。除了真值以外的其他值都是假值。注意,宏参数不是变量,环境变量不能使用这种方式测试。
if(<string>)
字符串一直都被计算为假值,以下情况除外:
- 字符串值是真值。
11、macro
宏定义,和c语言中的宏定义类似,原理也是文本替换。
macro(<name> [<arg1> ...])
<commands>
endmacro()
宏是大小写不敏感的。
11.1 参数
${ARGC}
表示传入给宏的参数个数;
${ARGV}
表示预期的参数列表;
${ARGN}
表示预期参数之后的所有多余参数列表;
${ARGV#}
表示具体某个参数,如${ARGV0}
、${ARGV1}
、${ARGV2}
;
11.2 函数与宏
在函数中,ARGN
, ARGC
, ARGV
和 ARGV0
, ARGV1
是真正的变量,而在宏中,仅仅只是文本替换。
12、aux_source_directory
aux_source_directory(<dir> <variable>)
获取指定目录下的所有源文件,并存储到指定的变量中。
13、option
定义一个开关选项
option(<variable> "<help_text>" [value])
如果选项定义时没有指定<value>
,那么默认值就是OFF
。
如果<variable>
已经被定义为正常或缓存变量,那么该命令没有任何效果。
在CMake
项目模式下,<variable>
会被创建成缓存变量,在CMake
脚本模式下,<variable>
会被创建成正常变量。
14、list
Reading
list(LENGTH <list> <out-var>)
list(GET <list> <element index> [<index> ...] <out-var>)
list(JOIN <list> <glue> <out-var>)
list(SUBLIST <list> <begin> <length> <out-var>)
Search
list(FIND <list> <value> <out-var>)
Modification
list(APPEND <list> [<element>...])
list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
list(INSERT <list> <index> [<element>...])
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])
list(PREPEND <list> [<element>...])
list(REMOVE_ITEM <list> <value>...)
list(REMOVE_AT <list> <index>...)
list(REMOVE_DUPLICATES <list>)
list(TRANSFORM <list> <ACTION> [...])
Ordering
list(REVERSE <list>)
list(SORT <list> [...])
list命令会在当前作用域中创建一个新的变量,如果想将变量传递到其他作用域中,可以使用set()命令,带上PARENT_SCOPE
或CACHE_INTERNAL
。
cmake中的list其实是用分号分隔的字符串。例如,set(var a b c d e)
就会创建字符串a;b;c;d;e
。
cmake中对列表索引的时候,0表示第一个元素,-1表示列表最后一个元素。
14.1 Reading
list(LENGTH <list> <output variable>)
返回列表长度。
list(GET <list> <element index> [<element index> ...] <output variable>)
返回指定的元素
list(JOIN <list> <glue> <output variable>)
使用glue
字符串连接列表所有元素
list(SUBLIST <list> <begin> <length> <output variable>)
获取子列表,如果length
等于0返回空表,等于-1返回begin
后面的所有元素。
14.2 Search
list(FIND <list> <value> <output variable>)
返回value
在列表中的索引,如果没找到,返回-1。
14.3 Modification
list(APPEND <list> [<element> ...])
向列表中添加元素,如果list不存在则创建空列表。
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
根据给定的正则表达式,包含或移除元素。
list(INSERT <list> <element_index> <element> [<element> ...])
将元素按指定的索引插入列表。
如果索引不在列表范围之内,则会报错。有效索引是0到N,N是列表长度,如果列表不存在,则会创建空列表。
list(POP_BACK <list> [<out-var>...])
弹出后面的元素,如果没有变量接收,那么就会弹出最后一个元素,如果接收的变量有N个,那么就会弹出N个元素。
list(POP_FRONT <list> [<out-var>...])
弹出前面的元素,和POP_BACK
类似。
list(PREPEND <list> [<element> ...])
在列表前面添加元素。
list(REMOVE_ITEM <list> <value> [<value> ...])
从列表中移除所有的给定元素。
list(REMOVE_AT <list> <index> [<index> ...])
从列表中移除给定索引的元素。
list(REMOVE_DUPLICATES <list>)
移除重复元素。
list(TRANSFORM <list> <ACTION> [<SELECTOR>] [OUTPUT_VARIABLE <output variable>])
转换列表,如果没有给定SELECTOR
,那么就会在列表中的所有item上应用ACTION。如果没有指定变量,则会把转换后的结果放在本地,如果指定了这将结果放到指定变量中。
ACTION
取值必须是以下选项:
- APPEND, PREPEND:给list中的每个元素在前面或后面加上value
list(TRANSFORM <list> (APPEND|PREPEND) <value> ...)
- TOLOWER, TOUPPER:将list中的每个元素转为大写或小写
list(TRANSFORM <list> (TOLOWER|TOUPPER) ...)
- STRIP: 移除list中元素的前面和后面的空白字符
list(TRANSFORM <list> STRIP ...)
- GENEX_STRIP: 移除list中元素的所有生成器表达式
list(TRANSFORM <list> GENEX_STRIP ...)
- REPLACE: 将正则表达式匹配到的部分全部替换成指定的表达式
list(TRANSFORM <list> REPLACE <regular_expression> <replace_expression> ...)
SELECTOR
取值必须是以下选项:
- AT:指定列表索引
list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
- FOR: 指定元素范围,同时可以指定一个递进数
list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
- REGEX: 指定正则表达式,只有匹配的元素才会被转换
list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
14.4 Ordering
list(REVERSE <list>)
原地倒换列表元素
list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
按字典序原地排序列表。
使用COMPARE
关键字选择排序算法,取值有以下几种:
- STRING:以字典序排序列表,默认行为
- FILE_BASENAME:按文件路径进行排序
- NATURAL:以自然顺序排序,如10.0 1.1 2.1 8.0 2.0 3.1,如果按自然序,则是1.1 2.0 2.1 3.1 8.0 10.0,如果是字典序,则是1.1 10.0 2.0 2.1 3.1 8.0
使用CASE
关键字,表明是否大小写敏感,取值有以下两种:
- SENSITIVE: 大小写敏感,默认行为
- INSENSITIVE:大小写不敏感
ORDER
决定排序顺序,取值有以下两种:
- ASCENDING:升序
- DESCENDING:降序
15、message
记录消息
General messages
message([<mode>] "message text" ...)
Reporting checks
message(<checkState> "message text" ...)
16、target_compile_definitions
添加目标编译选项。
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
PRIVATE
和PUBLIC
设置目标的COMPILE_DEFINITIONS
属性,PUBLIC
和INTERFACE
设置INTERFACE_COMPILE_DEFINITIONS
属性。
本目标编译使用COMPILE_DEFINITIONS
属性,依赖本目标的目标编译使用INTERFACE_COMPILE_DEFINITIONS
属性。
参数中的-D
前导会被移除,如:
target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC -DFOO) # -D removed
target_compile_definitions(foo PUBLIC "" FOO) # "" ignored
target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored
17、target_compile_options
添加编译选项。
target_compile_options(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
和target_compile_definitions
命令类似,PRIVATE
和PUBLIC
设置COMPILE_OPTIONS
属性,PUBLIC
和INTERFACE
设置INTERFACE_COMPILE_OPTIONS
属性。
18、install
指定安装规则。
install(TARGETS <target>... [...])
install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
install(RUNTIME_DEPENDENCY_SET <set-name> [...])
此命令有多个可选项:
- DESTINATION
指定安装目录,可以是相对路径也可以是绝对路径。
如果是相对路径,那么就会基于CMAKE_INSTALL_PREFIX
计算绝对路径。CMAKE_INSTALL_PREFIX
可以在安装的时候重新设置。
- PERMISSIONS
指定安装文件权限,有效权限:OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID 和 SETGID。
- CONFIGURATIONS
指定安装的构建配置列表(如Debug、Release)。
例如为Debug和Release指定不同的安装路径:
install(TARGETS target
CONFIGURATIONS Debug
RUNTIME DESTINATION Debug/bin)
install(TARGETS target
CONFIGURATIONS Release
RUNTIME DESTINATION Release/bin)
- COMPONENT
指定与安装规则关联的安装组件名。例如,runtime和development。
在指定安装组件时,则会只执行特定的组件安装,如果是全量安装,则会安装所有的组件。如果没有指定组件名,则会创建一个默认的组件名Unspecified
。默认组件名可以通过CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
变量进行控制。
- EXCLUDE_FROM_ALL
指定全量安装的时候,不安装此组件,仅通过指定组件安装。
- RENAME
为已安装的文件指定一个可能与原始文件不同的名称。仅当使用该命令安装单个文件时才允许重命名。
- OPTIONAL
安装文件不存在时,不报错。
18.1 安装目标
install(TARGETS targets... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
该命令的输出结果可能有以下几种:
- ARCHIVE
- 静态库
- DLL导入库(扩展名
.lib
)
- LIBRARY
- 动态库
- RUNTIME
- 可执行文件
- DLL(扩展名
.dll
)
- OBJECTS
对象库关联的对象文件
- FRAMEWORK
带有FRAMEWORK
属性的动态库和静态库(macOS平台)
- BUNDLE
带有MACOSX_BUNDLE
属性的可执行文件(macOS平台)
- PUBLIC_HEADER
在非apple平台上,与库关联的任何PUBLIC_HEADER文件都安装在由PUBLIC_HEADER参数指定的目标中。
- PRIVATE_HEADER
类似于PUBLIC_HEADER,但用于PRIVATE_HEADER文件。
- RESOURCE
类似于PUBLIC_HEADER和PRIVATE_HEADER,但用于RESOURCE文件。
当DESTINATION没有给出时,将会使用下列的默认值:
如果需要指定特定的安装目录,建议使用GNUInstallDirs
中的变量,这有利于后期指定特定的安装目录。
如·:
add_library(mylib STATIC ...)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER mylib.h)
include(GNUInstallDirs)
install(TARGETS mylib
PUBLIC_HEADER
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/myproj
)
除了上面列出的常见选项外,每个目标还可以接受以下附加参数:
- NAMELINK_COMPONENT
在某些平台上,版本化的共享库具有符号链接,例如:
lib<name>.so -> lib<name>.so.1
NAMELINK_COMPONENT选项类似于COMPONENT选项,但它会更改共享库名称链接的安装组件(如果生成了)。如果未指定,则默认为COMPONENT的值。
该选项只能在LIBRARY
中设置,不能在其他选项中设置。
install(TARGETS mylib
LIBRARY
COMPONENT Libraries
NAMELINK_COMPONENT Development
PUBLIC_HEADER
COMPONENT Development
)
该选项通常用于具有独立运行时和开发包的包管理器。例如,在Debian系统上,库应该在运行时包中,而头文件和命名链接应该在开发包中。
- NAMELINK_ONLY
此选项导致在安装库目标时仅安装名称链接。在受版本控制的共享库没有名称链接的平台上,或者当库没有版本控制时,NAMELINK_ONLY选项不安装任何东西。在LIBRARY块之外使用这个参数是错误的。
当给出NAMELINK_ONLY时,NAMELINK_COMPONENT或COMPONENT都可以用来指定名称链接的安装组件,但通常应该首选COMPONENT。
- NAMELINK_SKIP
类似于NAMELINK_ONLY,但它具有相反的效果:当安装库目标时,它会导致安装库文件而不是名称链接。当没有指定NAMELINK_ONLY或NAMELINK_SKIP时,将同时安装这两个部分。在受版本控制的共享库没有符号链接的平台上,或者当库没有版本控制时,NAMELINK_SKIP将安装该库。在LIBRARY块之外使用这个参数是错误的。
如果指定了NAMELINK_SKIP,则NAMELINK_COMPONENT不起作用。不建议将NAMELINK_SKIP与NAMELINK_COMPONENT结合使用。
- EXPORT
该选项将安装的目标文件与名为
- INCLUDES DESTINATION
该选项指定一个目录列表,当通过install(EXPORT)命令导出时,这些目录将被添加到
- RUNTIME_DEPENDENCY_SET
此选项会将已安装的可执行文件、共享库和模块目标的所有运行时依赖项添加到指定的运行时依赖项集中。然后可以使用install(RUNTIME_DEPENDENCY_SET)命令安装该集合。
该关键字与RUNTIME_DEPENDENCIES关键字互斥。
- RUNTIME_DEPENDENCIES
此选项导致安装的可执行文件、共享库和模块目标的所有运行时依赖项与目标本身一起安装。RUNTIME、LIBRARY、FRAMEWORK和泛型参数用于确定这些依赖项安装的属性(DESTINATION、COMPONENT等)。
RUNTIME_DEPENDENCIES在语义上等同于下面这对调用:
install(TARGETS ... RUNTIME_DEPENDENCY_SET <set-name>)
install(RUNTIME_DEPENDENCY_SET <set-name> args...)
其中<set-name>将是随机生成的集合名称。args……可以包括install(RUNTIME_DEPENDENCY_SET)命令支持的以下任何关键字:
-
DIRECTORIES
-
PRE_INCLUDE_REGEXES
-
PRE_EXCLUDE_REGEXES
-
POST_INCLUDE_REGEXES
-
POST_EXCLUDE_REGEXES
-
POST_INCLUDE_FILES
-
POST_EXCLUDE_FILES
可以在对该命令的TARGETS形式的单个调用中指定一组或多组属性。一个目标可以在不同的位置安装多次。示例代码如下:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
将把myExe安装到<prefix>/bin,把myStaticLib安装到<prefix>/lib/static。在非DLL平台上,mySharedLib将安装到<prefix>/lib和/some/full/path。在DLL平台上,mySharedLib DLL将安装到<prefix>/bin和/some/full/path,其导入库将安装到<prefix>/lib/static和/some/full/path。
接口库也可以作为安装目标,接口库不会安装任何组件,但是会包含在关联的EXPORT
里。如果安装对象库没有给定destination
,那么这些对象库就会被导出为接口库。
安装EXCLUDE_FROM_ALL
属性为true的目标,其行为是不可预期的。
18.2 安装导入运行时构建
install(IMPORTED_RUNTIME_ARTIFACTS targets...
[RUNTIME_DEPENDENCY_SET <set-name>]
[[LIBRARY|RUNTIME|FRAMEWORK|BUNDLE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
] [...]
)
IMPORTED_RUNTIME_ARTIFACTS
指定导入目标的运行时工件安装规则。当项目需要绑定安装外部可执行文件或模块时,可以使用此命令。
18.3 安装文件
install(<FILES|PROGRAMS> files...
TYPE <type> | DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])
如果文件名是相对路径,那么就会基于当前源代码路径计算绝对路径。安装文件的默认权限是644。
PROGRAMS
和FILES
基本相同,唯一的区别就是默认文件权限,PROGRAMS
比FILES
多了可执行权限,默认为755。PROGRAMS
目的在于安装程序(例如shell脚本),而不是目标。
必须指定TYPE
或DESTINATION
中的其中一个参数,不能都不指定。TYPE
指定安装的文件类型,指定TYPE
之后,随后会使用GNUInstallDirs
中相应的变量,设置DESTINATION,如果想显示指定安装路径,可使用DESTINATION
参数。TYPE
的可能取值及相关变量、默认值见下表:
上面DATAROOT
前缀,它的默认值是share
目录。
18.4 安装目录
install(DIRECTORY dirs...
TYPE <type> | DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>] [EXCLUDE_FROM_ALL]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
当目录名是相对路径的时候,就会基于当前源代码路径计算绝对路径。FILE_PERMISSIONS
和DIRECTORY_PERMISSIONS
指定文件和目录权限。如果指定了USE_SOURCE_PERMISSIONS
,但是没有指定FILE_PERMISSIONS
,那么文件权限就会拷贝源文件权限。如果没有指定权限,那么文件权限将和FILE
命令权限保持一致,即644,文件夹权限和PROGRAM
保持一致,即755。
MESSAGE_NEVER
指定是否输出安装状态。
PATTERN
和REGEX
通过指定通配表达式和正则表达式匹配特定的文件夹或目录。使用文件或目录的绝对路径进行匹配。PATTERN
只会匹配完整的文件名,匹配的部分必须在完整路径的末尾,且前面需要带一个斜杆。REGEX
可以匹配全路径的任意部分,同时可以使用/
和$
近似达到PATTERN
的作用。默认情况下,不管是否匹配,文件和目录都会全部安装。FILES_MATCHING
可以用来指定未匹配的文件不安装(不包括目录)。
例如:
install(DIRECTORY src/ DESTINATION include/myproj
FILES_MATCHING PATTERN "*.h")
上面的示例会提取并安装所有的头文件。
PATTERN
和REGEX
命令后面可以跟一些选项用来控制匹配的文件和目录。EXCLUDE
选项用于跳过匹配的文件和路径。PERMISSIONS
用于指定匹配文件的权限。
install(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)
TYPE
和DESTINATIONS
选项必须指定其中一个。TYPE
的取值和FILES
类似,如下:
18.5 自定义安装逻辑
install([[SCRIPT <file>] [CODE <code>]]
[ALL_COMPONENTS | COMPONENT <component>]
[EXCLUDE_FROM_ALL] [...])
SCRIPT
会在安装的时候调用指定的CMake脚本。如果是相对路径,这基于当前源代码路径计算绝对路径。CODE
会在安装的时候执行给定的CMake代码。CODE
参数需要使用双引号包括,如下:
install(CODE "MESSAGE(\"Sample install message.\")")
ALL_COMPONENTS
选项表示在每个组件安装的时候都执行指定的脚本和代码,COMPONENTS
表示只有在特定组件安装的时候才执行指定的脚本和代码。
18.6 安装导出
install(EXPORT <export-name> DESTINATION <dir>
[NAMESPACE <namespace>] [[FILE <name>.cmake]|
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[EXPORT_LINK_INTERFACE_LIBRARIES]
[COMPONENT <component>]
[EXCLUDE_FROM_ALL])
install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])
EXPORT表单生成并安装一个CMake文件,其中包含从安装树导入目标到另一个项目的代码。NAMESPACE
选项将<namespace>
添加到目标名的前面。默认情况下,生成文件的文件名叫<export-name>.cmake
,但是也可以通过FILE
选项指定其他的名字。如果指定了CONFIGURATIONS
选项,那么只有在特定的配置被安装的时候,才会安装cmake文件。
EXPORT
命令有利于帮助外部项目使用当前项目构建和安装的目标。例如:
install(TARGETS myexe EXPORT myproj DESTINATION bin)
install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)
install(EXPORT_ANDROID_MK myproj DESTINATION share/ndk-modules)
上述示例会将myexe
可执行文件安装到<prefix>/bin
,同时生成导入文件<prefix>/lib/myproj/myproj.cmake
和<prefix>/share/ndk-modules/Android.mk
。外部项目可以使用include
命令,加载导入文件,以引用可执行文件。
18.7 安装运行时依赖
install(RUNTIME_DEPENDENCY_SET <set-name>
[[LIBRARY|RUNTIME|FRAMEWORK]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
] [...]
[PRE_INCLUDE_REGEXES regexes...]
[PRE_EXCLUDE_REGEXES regexes...]
[POST_INCLUDE_REGEXES regexes...]
[POST_EXCLUDE_REGEXES regexes...]
[POST_INCLUDE_FILES files...]
[POST_EXCLUDE_FILES files...]
[DIRECTORIES directories...]
)
运行时依赖在DLL平台上,将安装到RUNTIME
路径下;在非DLL平台,会安装到LIBRARY
路径下;macOS系统,安装到FRAMEWORK
路径下。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通