组织
CMake输入文件以CMake语言写在名为CMakeLists.txt的源文件中,或者以.CMake
扩展名结尾的文件中
项目中的CMake语言源文件被组织为:
- Directories (CMakeLists.txt)
- Scripts (< script>.cmake)
- Modules (< module>.cmake).
Directories
- 当CMake处理一个项目源树时,入口点是顶级源目录中的一个名为CMakeLists.txt的源文件。
- 这个文件可能包含整个构建规范,或者使用add_subdirectory()命令向构建添加子目录。
- 该命令添加的每个子目录还必须包含一个CMakeLists.txt文件作为该目录的入口点。
- 对于处理CMakeLists.txt文件的每个源目录,CMake在构建树中生成一个响应的目录,作为默认的工作和输出目录。
Scripts
- 单个< script>.cmake源文件可以通过使用带有-P选项的cmake(1)命令行工具在脚本模式下进行处理。
- 脚本模式只是在给定的CMake语言源文件中运行命令,并不生成生成系统。它不允许使用CMake命令来定义构建目标或操作。
Modules
- 【Directories 】或【Scripts】中的CMake语言代码可以使用include()命令在include上下文的范围内加载< module>.CMake源文件。请参阅cmake模块(7)手册页,了解cmake发行版中包含的模块文档。
- 项目源树还可以提供自己的模块,并在 CMAKE_MODULE_PATH 变量中指定它们的位置。
语法
编码
- CMake Language源文件可以用7位ASCII文本编写,以便在所有支持的平台上实现最大的可移植性。换行可以被编码为\n或\r\n,但当读取输入文件时将被转换为\n。
- 注意,这个实现是8位的,所以在支持这种编码的系统api平台上,源文件可以被编码为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'>
请注意,任何不在【命令参数】或括号注释内的源文件行都可以以行注释结束。
命令调用
命令调用是一个名称,后跟由空格分隔的paren封闭参数:
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
bracket_argument(括号参数)
一个括号参数,受Lua长括号语法的启发,将内容括在相同长度的开始和结束“括号”之间:
bracket_argument ::= bracket_open bracket_content bracket_close
bracket_open ::= '[' '='* '['
bracket_content ::= <any text not containing a bracket_close with
the same number of '=' as the bracket_open>
bracket_close ::= ']' '='* ']'
- 左括号被写成[后跟0或多个=后跟[。相应的右括号写为],后面跟着相同数目的=,后面跟着]
- 括号参数内容包括在开始和结束括号之间的所有文本,除非紧跟在开始括号之后的换行符(如果有的话)被忽略。不会对包含的内容(如转义序列或变量引用)执行计算。方括号参数总是作为一个参数给命令调用。
例子:
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.
]=])
Quoted Argument(引号参数)
用双引号括起来的参数将内容括在双引号的开始和结束字符之间:
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.\
")
注意:3.0之前的CMake版本不支持使用\继续。如果引用的参数包含以奇数个\字符结尾的行,则报告错误。
无引号的参数
非引号参数不包含在任何引号语法中。它不能包含任何空格,(,),#,"或\,除非用反斜杠转义:
unquoted_argument ::= unquoted_element+ | unquoted_legacy
unquoted_element ::= <any character except whitespace or one of '()#"\'> |
escape_sequence
unquoted_legacy ::= <see note in text>
不带引号的参数内容由允许或转义字符的连续块中的所有文本组成。转义序列和变量引用都会被计算。结果值的划分方式与list划分元素的方式相同。每个非空元素都作为参数提供给命令调用。因此,一个未带引号的参数可以作为零个或多个参数提供给命令调用。
例如:
foreach(arg
NoSpace
Escaped\ Space
This;Divides;Into;Five;Arguments
Escaped\;Semicolon
)
message("${arg}")
endforeach()
我们不建议在新代码中使用遗留的不带引号的参数。相反,使用引号参数或括号参数来表示内容。
转义序列
转义序列是一个\后跟一个字符:
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分别编码制表符、回车符或换行符。一个;外部的任何变量引用本身编码,但可以在无引号参数中使用;而不需要在上面划分参数值。一个;inside变量引用编码文字;的性格
变量引用
变量引用的形式为${<variable>}
,并且在带引号参数或不带引号参数中计算。变量引用被替换为该变量的值,如果没有设置该变量,则替换为空字符串。变量引用可以嵌套并由内到外计算,例如${outer_${inner_variable}_variable}
。
字面值变量引用可以包含字母数字字符,字符·/_.+-
和转义序列。嵌套引用可用于计算任何名称的变量。也请参阅政策CMP0053文档,了解历史考虑和为什么$在技术上也是允许的,但不鼓励的原因。
【Variables】部分记录变量名的范围以及如何设置它们的值。
环境变量引用的格式为$ENV{<variable>}
。有关详细信息,请参见环境变量部分。
缓存变量引用的格式为$cache{<variable>}
。有关详细信息,请参见缓存。
if()
命令有一个特殊的条件语法,允许使用缩写形式<variable>
而不是${<variable>}
的变量引用。但是,环境和缓存变量总是需要被引用为$ENV{<variable>}
或$cache{<variable>}
。
注释
注释以#字符开头,该字符不在方括号参数、带引号的参数中,也不作为不带引号的参数的一部分用\转义。有两种类型的注释:括号注释和行注释。
括号注释
紧跟在bracket_open后面的#构成了一个由整个括号括起来的括号注释:
bracket_comment ::= '#' bracket_argument
例子:
#[[这是一个括号注释。
它一直运行到右括号。]]
message("First Argument\n" #[[Bracket Comment]] "Second Argument")
注意3.0之前的CMake版本不支持括号注释。它们将开头#解释为行注释的开始。
行注释
没有紧跟bracket_open的#会形成行注释,一直运行到行尾:
line_comment ::= '#' <任何不以括号开头的文本
不包含换行符>
例子:
# This is a line comment.
message("First Argument\n" # This is a line comment :)
"Second Argument") # This is a line comment.
流程控制
条件块
if()/elseif()/else()/endif()命令将有条件执行的代码块分隔开。
循环
foreach()/endforeach()和while()/endwhile()命令分隔要在循环中执行的代码块。在这样的块中,break()命令可以用来提前终止循环,而continue()命令可以用来立即开始下一个迭代。
命令定义
macro()/endmacro()和function()/endfunction()命令将代码块分隔,以便稍后作为命令调用。
变量
变量是CMake语言中最基本的存储单元。它们的值总是字符串类型,尽管有些命令可能将字符串解释为其他类型的值。set()和unset()命令显式地设置或取消设置变量,但其他命令也有修改变量的语义。变量名区分大小写,可以包含几乎任何文本,但我们建议只包含字母数字字符加上_和-的名称。
变量具有动态作用域。每个变量“set”或“unset”在当前作用域内创建一个绑定:
- 函数作用域:
- 由function()命令创建的【命令定义】创建命令,在调用时,这些命令将在新的变量绑定作用域内处理记录的命令
- 变量“set”或“unset”绑定在此作用域内,对当前函数和其中的任何嵌套调用可见,但在函数返回后不可见。
- 目录作用域
- 源树中的每个目录都有自己的变量绑定。
- 在处理目录的CMakeLists.txt文件之前,CMake复制当前在父目录中定义的所有变量绑定(如果有),以初始化新的目录作用域
- 使用CMake-P处理CMake脚本时,将变量绑定到一个“目录”作用域中。
- 不在函数调用内的变量“set”或“unset”绑定到当前目录作用域。
- 持久缓存
- CMake存储一组单独的“cache”变量或“cache entries”,它们的值在项目生成树中的多个运行中保持不变。
- 缓存项有一个仅由显式请求修改的独立绑定范围,如set()和unset()命令的Cache选项。
当计算变量引用时,CMake首先在函数调用堆栈(如果有的话)中搜索绑定,然后返回到当前目录作用域(如果有的话)中的绑定。
- 如果找到一个“set”绑定,则使用它的值。
- 如果找到一个“unset”绑定,或者没有找到绑定,CMake就会搜索一个缓存条目。
- 如果找到一个缓存条目,就使用它的值。
- 否则,变量引用将计算为空字符串。
$CACHE{VAR}语法可以用来进行直接的缓存条目查找。
CMake -variables(7)手册文档记录了cmake提供的许多变量,或者在项目代码设置时对cmake有意义的变量。
环境变量
环境变量和普通变量一样,有以下区别:
- 作用域:环境变量在CMake过程中具有全局作用域。它们从不被缓存。
- 引用:变量引用的形式为
$ENV{< Variable >}
。 - 初始化
- CMake环境变量的初始值是调用进程的初始值。
- 可以使用set()和unset()命令更改值。
- 这些命令只影响正在运行的CMake进程,对整个系统环境没有影响。
- 更改后的值不会写回调用流程,后续的构建或测试流程也不会看到它们。
CMake -env-variables(7)手册记录了对cmake有特殊意义的环境变量。
列表
尽管CMake中的所有值都存储为字符串,但在某些上下文中,例如在计算一个未带引用的参数时,字符串可以被视为一个列表。在这样的上下文中,一个字符串被拆分为列表元素;不跟在数量不相等的[and]字符后面且前面不直接跟一个\的字符。
元素列表通过连接以;分隔的元素来表示为字符串。例如,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"