CMake 语法快速参考
该文档以 CMake 3.24 版本的语法为准,排除了官方不推荐的语法,仅建议作为快速了解 CMake 语法以及简单参考使用。更为详尽的描述请见:cmake.org/cmake/help/… 。
文件组织
一个项目中的 CMake 语言源文件被组织成以下三种类型:
目录
CMake 处理一个项目源代码树的入口点为顶级源目录的 CMakeLists.txt 文件。该文件可能包含整个构建规范或使用 add_subdirectory() 命令添加子目录到构建系统当中。
add_subdirectory() 命令添加的所有子目录都须包含一个 CMakeLists.txt 文件作为其入口点。CMake 在构建系统中会根据生成其对应的目录树作为其默认的工作/输出目录。
脚本
通过使用带有 -P 选项的 cmake(1) 命令行工具,可以在脚本模式下处理单个 <script>.cmake 源文件。脚本模式只运行给定 CMake 语言源文件中的命令,不会生成构建系统。该类型的文件不允许使用已被定义为构建目标或操作的 CMake 命令。
模块
目录或脚本中的 CMake 语言代码可能使用 include() 在命令所在的上下文内加载一个 <module>.cmake 源文件。(见 cmake-modules(7) 文档手册页 CMake 发行版中包含的模块。)
项目源代码树也可以提供自己的模块,在 CMAKE_MODULE_PATH 环境变量上指定。
语法
命令调用
命令名(参数1 参数2 ...)
命令名称不区分大小写。参数中嵌套的不带引号的括号必须平衡。
命令参数
括号参数
括号参数以 , [ + 个 = + [ 开头,以] + 个 = + ] 结束。
示例:
message([=[
这是括号长度为 1 的括号参数中的第一行。
转义和 ${变量} 不会被评估。
即使它包含一个 ; 字符,这里始终是一个参数。
这段文本不会在长度为 0 的右括号结束上,例如 ]]。
但它会被长度为 1 的右括号结束。
]=])
带引号的参数
带引号的参数包含在开始和结束双引号字符之间的内容:
示例:
message("这是一个包含多行的引用参数。
即使它包含一个 ; 字符,这里始终是一个参数。
转义和 ${变量} 都会被评估。
这段文本不会被转义过的双引号结束,就像:\"。
但它会被未转义的双引号结束。
")
不带引号的参数
该类型的参数不能使用带引号的参数的语法,除非被反斜杠 \ 转义。
示例:
foreach(arg
无空格
转义过的\ 空格
这将;会被;分成;五个;参数
转义过的\;分号
)
message("${arg}")
endforeach()
注意,该类型参数的内容第一个字符不应为括号,这种情况应当使用其他类型的参数。
变量引用
变量引用的格式为 ${<variable>}。变量引用只会在带引号的参数与不带引号的参数中被评估。变量引用会被变量的值替换,若变量未设置,则会替换为空字符串。变量引用可以由内到外嵌套,例如 ${outer_${inner_variable}_variable}.
文字变量引用可能由字母、数字、/_.+- 和转义序列组成。嵌套引用可用于评估任何名称的变量。
变量 章节记录了变量名的范围以及它们的值是如何设置的。
一个 环境变量引用 的格式为 $ENV{<variable>}。参阅官方文档的 环境变量 部分以了解更多信息。
一个 缓存变量引用 的格式为 $CACHE{<variable>}。参阅官方文档的 CACHE 部分以了解更多信息。
if() 命令允许以 <variable> 的形式直接引用变量,但环境和缓存变量不能使用这种语法。
注释
括号注释
括号注释由 # + 括号参数 组成。
#[[这是一个括号注释。
它一直延续到右括号。]]
message("第一个参数\n" #[[括号注释]] "第二个参数")
控制结构
条件块
if()/ elseif()/ else()/ endif() 命令分隔要有条件地执行的代码块。
循环
foreach()/ endforeach()和 while()/ endwhile()命令分隔要在循环中执行的代码块。在该此类的块内可以使用 break()命令终止循环,或使用 continue() 命令立即开始下一次迭代。
变量
变量是 CMake 语言中的基本存储单元,其值总是为字符串类型。set() 和 unset() 显式命令分别用于设置或取消设置变量。变量名区分大小写,可以由几乎任何文本组成,但我们建议坚持使用仅包含的名称字母数字字符加上 _和 -.
变量具有动态作用域。每个变量的 set 和 unset 会在当前作用域内创建一个绑定:
-
函数作用域
由
function()命令进行命令定义而创建的命令,调用时会在新变量所绑定作用域内处理记录的命令。变量的set和unset在此范围内绑定并且对当前函数可见以及其任何嵌套调用中可见,在函数返回之后销毁。 -
目录作用域
每个目录都有自己的变量绑定。在
CMakeLists.txt文件处理前,CMake 会复制父目录中当前定义的所有变量绑定,用于初始化新的目录作用域。 对于 CMake Scripts ,处理cmake -P命令时, 也会绑定一个目录作用域的变量。不在函数调用内的变量
set或unset将绑定到当前目录范围。 -
持久缓存
CMake 存储一组单独的“缓存”变量或“缓存条目”,其值在项目构建树中的多次运行中持续存在。缓存条目有一个独立的绑定范围,仅由显式请求修改,例如由
CACHE的选项set()和unset()命令。
在评估变量引用时,CMake 首先搜索用于绑定的函数调用堆栈(如果有),然后回退到当前目录范围内的绑定(如果有)。如果找到一个“set”绑定,则使用它的值。如果找到 “unset” 绑定或没有找到绑定,CMake 就会搜索缓存条目。如果找到缓存条目,则使用其值。否则,变量引用的计算结果为空字符串。使用 $CACHE{VAR}语法可用于直接缓存条目查找。
cmake-variables(7) 手册记录了许多变量 由 CMake 提供或在设置时对 CMake 有意义 按项目代码。
注意:
CMake 会保留以下标识符:
- 以
CMAKE_(大写、小写或混合大小写)为首,或 - 以
_CMAKE_(大写、小写或混合大小写)为首,或 - 以
_为首,后跟任何CMake Command名称。
环境变量
环境变量与普通变量,具有以下差异:
-
作用域
环境变量在 CMake 过程中具有全局作用域,且永远不会被缓存。
-
引用
-
初始化
CMake 环境变量的初始值是调用过程。可以使用
set()和unset()命令更改其值。这些命令只会影响正在运行的 CMake 进程,并不会影响到系统真实的环境变量值。请参阅 cmake -E env 在修改后的环境中运行命令的命令行工具。
-
检查
请参阅 cmake -E 环境 显示所有当前环境变量的命令行工具。
这个 cmake-env-variables(7) 手册记录了对 CMake 有特殊意义的变量。
列表
字符串在某些情况下可能会被视为列表,例如在 不带引号的参数 的评估中。在这种情况下,字符串 通过 ; 字符 拆分成列表元素。
列表的元素由通过 ; 分割的字符串表示。例如,set() 命令将多个值存储到目标变量中作为一个列表:
set(srcs a.c b.c c.c) # 设置 "srcs" 为 "a.c;b.c;c.c"
列表适用于简单的用例,例如源文件列表,不应用于复杂的数据处理任务。大多数构造列表的命令都不会对 ; 进行转义:
set(x a "b;c") # "x" 将设置为 "a;b;c", 而不是 "a;b\;c"
通常,列表不支持包含 ; 字符。 为避免出现问题,请考虑以下建议:
-
许多 CMake 的命令、变量和属性的接口都接收分号分隔的列表,所以应当避免传递包含元素带分号的列表,除非它们的文档说明了会直接支持或某种方式来转义或编码分号。
-
构造列表时,在各元素上用其他占位符替换
;。 然后在处理列表元素时再将占位符替换回;。例如,以下代码使用|代替;字符:set(mylist a "b|c") foreach(entry IN LISTS mylist) string(REPLACE "|" ";" entry "${entry}") # 在这之后便可直接正常使用 "${entry}" endforeach()ExternalProject模块的LIST_SEPARATOR选项是一个使用这种方法构建的接口没那 示例。 -
在
generator expressions列表中,使用$<SEMICOLON>生成器表达式。 -
在
function()的实现中,避免使用ARGV和ARGN, 因为它不会将值中的分号与那些互相分隔的值区分开来。更推荐使用命名的位置参数和ARGC和ARGV#变量。当使用cmake_parse_arguments()解析参数时,推荐使用它的PARSE_ARGV签名,因为其使用ARGV#变量。请注意,此方法不适用于
macro()实现,因为它们使用占位符引用参数,而不是真正的变量。