cmake-language(7)¶
组织¶
CMake 输入文件以“CMake 语言”编写,源文件名为 CMakeLists.txt
或以 .cmake
文件名扩展名结尾。
项目中的 CMake 语言源文件组织为
目录¶
当 CMake 处理项目源代码树时,入口点是顶层源代码目录中名为 CMakeLists.txt
的源文件。此文件可以包含整个构建规范,也可以使用 add_subdirectory()
命令将子目录添加到构建中。通过命令添加的每个子目录也必须包含一个 CMakeLists.txt
文件作为该目录的入口点。对于每个处理其 CMakeLists.txt
文件的源目录,CMake 都会在构建树中生成一个相应的目录,作为默认工作和输出目录。
脚本¶
单个 <script>.cmake
源文件可以使用 cmake(1)
命令行工具与 -P
选项在脚本模式下处理。脚本模式只是运行给定 CMake 语言源文件中的命令,不生成构建系统。它不允许定义构建目标或操作的 CMake 命令。
模块¶
目录或脚本中的 CMake 语言代码可以使用 include()
命令在包含上下文的作用域中加载 <module>.cmake
源文件。有关 CMake 发行版中包含的模块的文档,请参阅 cmake-modules(7)
手册页。项目源代码树也可以提供自己的模块,并在 CMAKE_MODULE_PATH
变量中指定其位置。
语法¶
编码¶
CMake 语言源文件可以采用 7 位 ASCII 文本编写,以最大限度地提高所有支持平台之间的可移植性。换行符可以编码为 \n
或 \r\n
,但在读取输入文件时会转换为 \n
。
请注意,实现是 8 位干净的,因此源文件可以在支持此编码的系统 API 的平台上编码为 UTF-8。此外,CMake 3.2 及更高版本支持 Windows 上编码为 UTF-8 的源文件(使用 UTF-16 调用系统 API)。此外,CMake 3.0 及更高版本允许源文件中有一个前导的 UTF-8 字节顺序标记。
源文件¶
CMake 语言源文件由零个或多个命令调用组成,这些调用由换行符和可选的空格和注释分隔。
file ::=file_element
* file_element ::=command_invocation
line_ending
| (bracket_comment
|space
)*line_ending
line_ending ::=line_comment
?newline
space ::= <match '[ \t]+'> newline ::= <match '\n'>
命令调用¶
一个命令调用是一个名称,后跟括号括起来的参数,参数之间用空格分隔
command_invocation ::=space
*identifier
space
* '('arguments
')' identifier ::= <match '[A-Za-z_][A-Za-z0-9_]*'> arguments ::=argument
?separated_arguments
* separated_arguments ::=separation
+argument
? |separation
* '('arguments
')' separation ::=space
|line_ending
例如
add_executable(hello world.c)
命令名称不区分大小写。参数中未引用的嵌套括号必须平衡。每个 (
或 )
都作为文字未引用参数传递给命令调用。这可以在调用 if()
命令时用于括起条件。例如
if(FALSE AND (FALSE OR TRUE)) # evaluates to FALSE
命令参数¶
命令调用中有三种类型的参数
argument ::=bracket_argument
|quoted_argument
|unquoted_argument
方括号参数¶
一个方括号参数,受 Lua 长方括号语法的启发,将内容括在相同长度的开头和结尾“方括号”之间
bracket_argument ::=bracket_open
bracket_content
bracket_close
bracket_open ::= '[' '='* '[' bracket_content ::= <any text not containing abracket_close
with the same number of '=' as thebracket_open
> bracket_close ::= ']' '='* ']'
开括号写成 [
,后面跟着零个或多个 =
,再后面跟着 [
。相应的闭括号写成 ]
,后面跟着相同数量的 =
,再后面跟着 ]
。方括号不能嵌套。始终可以选择一个唯一的长度来作为开括号和闭括号,以包含其他长度的闭括号。
方括号参数内容由开括号和闭括号之间的所有文本组成,但紧随开括号后的一个换行符(如果有)将被忽略。不会对封闭内容执行任何评估,例如转义序列或变量引用。方括号参数始终作为正好一个参数传递给命令调用。
例如
message([=[
This is the first line in a bracket argument with bracket length 1.
No \-escape sequences or ${variable} references are evaluated.
This is always one argument even though it contains a ; character.
The text does not end on a closing bracket of length 0 like ]].
It does end in a closing bracket of length 1.
]=])
注意
CMake 3.0 之前的版本不支持方括号参数。它们将开括号解释为非引用参数的开始。
引用参数¶
一个引用参数用开双引号和闭双引号字符将内容括起来
quoted_argument ::= '"'quoted_element
* '"' quoted_element ::= <any character except '\' or '"'> |escape_sequence
|quoted_continuation
quoted_continuation ::= '\'newline
引用参数内容由开头和结尾引号之间的所有文本组成。无论是转义序列还是变量引用都会被评估。引用参数总是作为一个参数精确地传递给命令调用。
例如
message("This is a quoted argument containing multiple lines.
This is always one argument even though it contains a ; character.
Both \\-escape sequences and ${variable} references are evaluated.
The text does not end on an escaped double-quote like \".
It does end in an unescaped double quote.
")
任何以奇数个反斜杠结尾的行上的最后一个 \
被视为行继续符,并与紧随其后的换行符一起被忽略。例如
message("\
This is the first line of a quoted argument. \
In fact it is the only line but since it is long \
the source code uses line continuation.\
")
注意
CMake 3.0 之前的版本不支持使用 \
进行续行。它们会报告包含以奇数个 \
字符结尾的行的引用参数中的错误。
非引用参数¶
一个非引用参数不被任何引用语法包围。它不能包含任何空格、(
、)
、#
、"
或 \
,除非通过反斜杠转义
unquoted_argument ::=unquoted_element
+ |unquoted_legacy
unquoted_element ::= <any character except whitespace or one of '()#"\'> |escape_sequence
unquoted_legacy ::= <see note in text>
非引用参数内容由允许或转义字符的连续块中的所有文本组成。转义序列和变量引用都将被评估。结果值将以与列表拆分为元素相同的方式进行分割。每个非空元素都将作为参数传递给命令调用。因此,一个非引用参数可以作为零个或多个参数传递给命令调用。
例如
foreach(arg
NoSpace
Escaped\ Space
This;Divides;Into;Five;Arguments
Escaped\;Semicolon
)
message("${arg}")
endforeach()
注意
为了支持旧版 CMake 代码,非引用参数还可以包含双引号字符串("..."
,可能包含水平空格)和 make 风格的变量引用($(MAKEVAR)
)。
未转义的双引号必须平衡,不能出现在非引用参数的开头,并且被视为内容的一部分。例如,非引用参数 -Da="b c"
、-Da=$(v)
和 a" "b"c"d
均被字面解释。它们可以分别写成引用参数 "-Da=\"b c\""
、"-Da=$(v)"
和 "a\" \"b\"c\"d"
。
Make 风格的引用被视为内容的一部分,并且不进行变量扩展。它们被视为单个参数的一部分(而不是单独的 $
、(
、MAKEVAR
和 )
参数)。
上述“unquoted_legacy”的生成表示此类参数。我们不建议在新代码中使用旧版未引用参数。相反,请使用引用参数或方括号参数来表示内容。
转义序列¶
一个转义序列是一个 \
后面跟着一个字符
escape_sequence ::=escape_identity
|escape_encoded
|escape_semicolon
escape_identity ::= '\' <match '[^A-Za-z0-9;]'> escape_encoded ::= '\t' | '\r' | '\n' escape_semicolon ::= '\;'
一个 \
后面跟着一个非字母数字字符只是编码该字面字符,而不将其解释为语法。一个 \t
、\r
或 \n
分别编码一个制表符、回车符或换行符。一个 \;
在任何变量引用之外编码自身,但可以在未引用参数中使用,以编码 ;
而不根据它分割参数值。一个 \;
在变量引用内部编码字面 ;
字符。(另请参阅策略 CMP0053
文档中的历史考虑事项。)
变量引用¶
一个变量引用的形式是 ${<variable>}
,它在引用参数或非引用参数中进行评估。变量引用会被替换为指定变量或缓存条目的值,如果两者都未设置,则替换为空字符串。变量引用可以嵌套并从内到外进行评估,例如 ${outer_${inner_variable}_variable}
。
字面变量引用可以由字母数字字符、字符 /_.+-
和转义序列组成。嵌套引用可用于评估任何名称的变量。另请参阅策略 CMP0053
文档,了解历史考虑因素以及为什么 $
在技术上也允许但不建议使用。
变量部分记录了变量名称的范围以及它们的取值方式。
一个环境变量引用的形式是 $ENV{<variable>}
。有关更多信息,请参阅环境变量部分。
一个缓存变量引用的形式是 $CACHE{<variable>}
,它被指定缓存条目的值替换,而不检查同名的普通变量。如果缓存条目不存在,则被空字符串替换。有关更多信息,请参阅 CACHE
。
if()
命令有一个特殊的条件语法,允许变量引用采用短形式 <variable>
而不是 ${<variable>}
。但是,环境变量始终需要引用为 $ENV{<variable>}
。
控制结构¶
条件块¶
循环¶
foreach()
/endforeach()
和 while()
/endwhile()
命令限定循环执行的代码块。在此类块内,break()
命令可用于提前终止循环,而 continue()
命令可用于立即开始下一次迭代。
命令定义¶
macro()
/endmacro()
和 function()
/endfunction()
命令界定了要记录以便后续作为命令调用的代码块。
变量¶
变量是 CMake 语言中的基本存储单元。它们的值始终为字符串类型,尽管某些命令可能会将这些字符串解释为其他类型的值。set()
和 unset()
命令显式地设置或取消设置变量,但其他命令也具有修改变量的语义。变量名称区分大小写,并且可以由几乎任何文本组成,但我们建议只使用由字母数字字符以及 _
和 -
组成的名称。
变量具有动态作用域。每次变量的“设置”或“取消设置”都会在当前作用域中创建一个绑定
- 块作用域
block()
命令可以为变量绑定创建一个新的作用域。- 函数作用域
由
function()
命令创建的命令定义会创建命令,当被调用时,这些命令会在一个新的变量绑定作用域中处理记录的命令。在该作用域中,“设置”或“取消设置”变量会进行绑定,并且对于当前函数及其内部的任何嵌套调用都是可见的,但在函数返回后则不可见。- 目录作用域
源代码树中的每个目录都有其自己的变量绑定。在处理目录的
CMakeLists.txt
文件之前,CMake 会复制父目录中当前定义的所有变量绑定(如果有)以初始化新的目录作用域。CMake 脚本在用cmake -P
处理时,会在一个“目录”作用域中绑定变量。不在函数调用内部的“设置”或“取消设置”变量会绑定到当前目录作用域。
- 持久缓存
CMake 存储了一组单独的“缓存”变量,或“缓存条目”,它们的值在项目构建树中的多次运行中保持不变。缓存条目具有一个独立的绑定作用域,仅通过显式请求才能修改,例如通过
set()
和unset()
命令的CACHE
选项。
在评估变量引用时,CMake 首先搜索函数调用堆栈(如果有)中的绑定,然后回退到当前目录作用域中的绑定(如果有)。如果找到“设置”绑定,则使用其值。如果找到“取消设置”绑定或未找到绑定,CMake 然后搜索缓存条目。如果找到缓存条目,则使用其值。否则,变量引用评估为空字符串。$CACHE{VAR}
语法可用于直接查找缓存条目。
cmake-variables(7)
手册记录了 CMake 提供或在项目代码中设置时对 CMake 有意义的许多变量。
环境变量¶
环境变量与普通变量类似,但有以下区别:
- 作用域
环境变量在 CMake 进程中具有全局作用域。它们从不缓存。
- 引用
- 初始化
CMake 环境变量的初始值是调用进程的值。值可以使用
set()
和unset()
命令更改。这些命令只影响正在运行的 CMake 进程,而不是整个系统环境。更改的值不会写回调用进程,也不会被后续的构建或测试进程看到。要在一个修改过的环境中运行命令,请参阅
cmake -E env
命令行工具。- 检查
要显示所有当前环境变量,请参阅
cmake -E environment
命令行工具。
cmake-env-variables(7)
手册记录了对 CMake 具有特殊含义的环境变量。
列表¶
尽管 CMake 中的所有值都存储为字符串,但在某些上下文中,例如在评估未引用参数时,字符串可能被视为列表。在这种上下文中,字符串通过在不跟随不等数量的 [
和 ]
字符且不紧跟 \
的 ;
字符处进行分割,从而划分为列表元素。序列 \;
不会分割值,但在结果元素中会被 ;
替换。
元素列表通过用 ;
分隔符连接元素来表示为字符串。例如,set()
命令将多个值作为列表存储到目标变量中
set(srcs a.c b.c c.c) # sets "srcs" to "a.c;b.c;c.c"
列表适用于简单用例,如源文件列表,不应用于复杂数据处理任务。大多数构建列表的命令不会转义列表元素中的 ;
字符,从而扁平化嵌套列表
set(x a "b;c") # sets "x" to "a;b;c", not "a;b\;c"
通常,列表不支持包含 ;
字符的元素。为避免问题,请考虑以下建议:
许多 CMake 命令、变量和属性的接口都接受分号分隔的列表。除非它们明确支持或提供某种方式来转义或编码分号,否则避免向这些接口传递包含分号的元素列表。
构建列表时,在元素中将未使用的占位符替换为
;
。然后,在处理列表元素时,将;
替换为占位符。例如,以下代码使用|
代替;
字符set(mylist a "b|c") foreach(entry IN LISTS mylist) string(REPLACE "|" ";" entry "${entry}") # use "${entry}" normally endforeach()
ExternalProject
模块的LIST_SEPARATOR
选项就是使用这种方法构建的接口的一个例子。在
生成器 表达式
的列表中,使用$<SEMICOLON>
生成器表达式。在
function()
实现中,避免使用ARGV
和ARGN
,它们无法区分值中的分号与分隔值的分号。相反,更倾向于使用命名位置参数和ARGC
和ARGV#
变量。当使用cmake_parse_arguments()
解析参数时,更倾向于其PARSE_ARGV
签名,它使用ARGV#
变量。请注意,此方法不适用于
macro()
实现,因为它们使用占位符而不是真实变量来引用参数。
注释¶
注释以
#
字符开头,该字符不在方括号参数、引用参数内,或未被\
转义作为非引用参数的一部分。注释有两种类型:方括号注释和行注释。方括号注释¶
紧随
#
之后跟着一个bracket_open
构成一个方括号注释,由整个方括号包围的内容组成例如
注意
CMake 3.0 之前的版本不支持方括号注释。它们将开头的
#
解释为行注释的开始。行注释¶
紧跟在
bracket_open
之后没有#
的字符构成一个行注释,它一直延伸到行尾例如