0 源文件
- Cmake的源码文件,可以包含命令+注释+空格+换行。
- 以cmake编写的源文件以CMakeLists.txt命名或者以.cmake为扩展名。
- 一般在项目源码的最顶层目录会放置CMakeLists.txt编写的整个项目的构建规则,或者通过add_subdirectory()命令把下一层源码目录的构建规则也添加进来。
- Cmake源文件中所有有效的语句都是命令(内置的命令或者自定义的函数/宏命令)
1 注释
1.1 单行注释
语法格式如下:
#注释内容
备注:注释从#开始到行尾结束
例子:
# 这是单行注释
message("First Argument\n" # 这是单行注释 :)
"Second Argument") # 这是单行注释.
1.2 多行注释
Cmake使用括号参数 (Bracket Argument )来实现多行注释
语法格式如下:
#[=*[注释内容]=*]
备注:其中*号表示0个或者多个=,当然左边=号的数量要等于右边=的数量
最简单格式是:
#[[注释内容]]
例子:
#[[这是多行注释!
这是多行注释!
这是多行注释!]]
message("First Argument\n" #[[这是多行注释]] "Second Argument")
2 变量
在cmake语言中,所有的变量都是string 类型。可以使用cmake内置的set()和unset() 命令来明确的设置或者移除一个变量。
2.1 定义变量
语法格式如下:
set(变量名 变量值)
备注:变量名是大小写敏感的,变量名可以包含任何文本,官方建议只使用字母数字-和_
例子:
set(var 123)
2.2 变量的引用
语法格式如下:
${变量名}
$ENV{VAR}
备注: $ENV{VAR}是对环境变量VAR的引用,cmake支持变量嵌套引用,解引用的顺序从内到外
例子:
set(var 123)
message(" var = ${var}")
message("PATH = $ENV{PATH}")
set(inner_var foo)
set(outer_foo_var 999)
message("nest variable value is :${outer_${inner_var}_var}")
3 列表(lists)
列表也是字符串,可以把列表看做特殊的变量,这个变量有多个值,每个值用分号”;”进行分隔。
3.1 定义列表
语法格式如下:
set(列表名 值1 值2 ... 值N)
或者
set(列表名 “值1;值2; ...;值N”)
例子:
set(list_var 1 2 3 4) # list_var = 1;2;3;4
set(list_foo "5;6;7;8") # list_foo = 5;6;7;8
3.2 列表的引用
语法格式如下:
${列表名}
例子:
set(list_var 1 2 3 4) # list_var = 1;2;3;4
set(list_foo "5;6;7;8") # list_foo = 5;6;7;8
message(${list_var})#输出: 1234
message(${list_foo})#输出:5678
message("${list_var}")#输出:1;2;3;4
message("${list_foo}")#输出:5;6;7;8
备注:不加引号的引用cmake将自动在分号处进行切分成多个列表元素,并把它们作为多个独立的参数传给命令。加了引号的引用cmake不会进行切分并保持分号不动,把整个引号内的内容当作一个参数传给命令。
4 流控命令
在cmake语言中,不管是条件语句,循环语句,函数或者宏,都是命令。
4.1 操作符
用于条件/循环的表达式的操作符,这些操作符是大小写敏感的。操作符的处理优先级:
带()的表达式 > 一元 > 二元 > 逻辑
操作符类型 | 操作符名称 |
---|---|
一元 | 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,MATCHES |
逻辑 | NOT, AND, OR |
4.2 布尔常量值
Cmake中的布尔常量值也是大小写敏感的。
类型 | 值 |
---|---|
true | 1, ON, YES, TRUE, Y, 非0的值 |
false | 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串””, 以-NOTFOUND 结尾的字符串 |
4.3 条件命令
语法格式如下:
if(表达式)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
elseif(表达式2)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
else(表达式)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endif(表达式)
备注:elseif 和else部分是可选的,也可以有多条elseif ,这些是根据你的实际的业务需求来选择的。缩进和空格对语句解析没有影响。
例子:
set(if_tap OFF)
set(elseif_tap ON)
if(${if_tap})
message("这是 if 表达式")
elseif(${elseif_tap})
message("这是 elseif 表达式")
else(${if_tap})
message("这是 else 表达式")
endif(${if_tap})
4.4 循环命令
语法格式如下:
while(表达式)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endwhile(表达式)
备注:循环可以用break()命令退出,continue()命令可以跳过下面的语句块,立即进入下一次的循环迭代。
例子:
set(a "")
while(NOT a STREQUAL "xxxxx")
set(a "${a}x")
message(" a = ${a}")
endwhile()
4.5 循环遍历
语法格式一如下:
foreach(循环变量 参数1 参数2 ...)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endforeach(循环变量)
备注:每次迭代设置${循环变量} = 参数N
例子:
foreach(item "A" "B" "C")
message(" ${item}")
endforeach(item)
语法格式二如下:
foreach(循环变量 RANGE total)
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endforeach(循环变量)
备注:循环的范围从0~total
foreach(循环变量 RANGE start stop [step])
# 要执行的命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endforeach(循环变量)
备注:循环的范围从start ~stop ,循环增量为step
例子:
foreach(x RANGE 10)
message(" ${x}")
endforeach(x)
foreach(x RANGE 10 14 2)
message(" ${x}")
endforeach(x)
语法格式三如下:
foreach(loop_var IN [LISTS [list1 [...]]]
[ITEMS [item1 [...]]])
备注:foreach支持对列表的遍历
例子:
set(list_var "1;2;3;4")
foreach(x IN LISTS list_var)
message(" ${x}")
endforeach(x)
备注:同样foreach也支持break(),continue()命令操作
5 命令
Cmake支持很多内置的命令,像我们之前使用的set,message,if,while,foreach等待都是内置的命令。也可以自定义命令。命令可以带字符串参数,但是没有还回值。命令名是大小写不敏感的,所以SET和set表示同一个命令,但是命令带的参数名是大小写敏感的ARG和arg是两个不同的参数。
5.1 自定义命令
自定义函数命令格式如下:
function(<name> [arg1 [arg2 [arg3 ...]]])
# 自定义命令块
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endfunction(<name>)
备注:定义了一个函数名为name,参数为arg1, arg2, arg3, ...的函数命令。参数之间用空格进行分隔,如果某一参数里面包含空格最好用双引号把该参数包起来(引号内的所有字符串只当作一个参数),比如”arg1”。如果不指定参数列表,则函数可以接受任意的参数,ARGC内置变量表明传人参数的个数,ARGV0, ARGV1, ARGV2, ...内置变量可以获得对应传入的参数,ARGV内置变量可以获得整个参数列表
命令的调用格式如下:
name(实参列表)
例子:
function(foo x y z)
message("Calling function 'foo':")
message(" x = ${x}")
message(" y = ${y}")
message(" z = ${z}")
message("ARGC = ${ARGC} arg1 = ${ARGV0} arg2 = ${ARGV1} arg3 = ${ARGV2} all args = ${ARGV}")
endfunction(foo)
foo("1" "2" "3")
自定义宏命令格式如下:
macro(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
endmacro(<name>)
备注:宏和函数的基本上是一样的,只是说函数命令有自己的作用域,宏命令的作用域和调用者的作用域一样。
6 变量的作用域
Cmake里面有三种作用域,全局层的,目录层的,函数层的。
- 全局层:cache变量,在整个项目范围之内可见,一般在用set定义变量的时候指定CACHE参数就能定义为cache的变量。
- 目录层:在当前目录CMakeLists.txt中定义,及在该文件中包含进来的(通过include或者macro引进的)其他的cmake源文件中定义的变量属于目录层这一级的作用域。
- 函数层:在命令函数中定义的变量,属于函数作用域内的变量。
其中全局层的 < 目录层的 < 函数层的
- <之后的层定义的变量将覆盖掉<之前层定义的变量。
- 引用一个变量会先在当前层的作用域查找该变量的值,只有当前层找不到该变量的值,才会去上一层的作用域查找。比如在function命令中要引用一个变量,它会先查看该函数是否有“set”该变量如果有的话就用,如果没有的话就要往上一层比如当前的目录层(也有可能是调用它的另一个函数),查看是否有“set”该变量如果有的话就用,如果在当前目录层及父目录层都没找到的话,就去找是否有对应的同名cache变量如果有的话就用,都没找到就是空值。
- 在当前作用域修改了上一层作用域的变量,并不会传递到上一层的作用域(当前作用域只是复制上一作用域的全部变量到自己的作用域在进行修改),比如在函数命令中对目录层作用域定义的一个变量进行修改,并不会影响目录层中该变量的值。
备注:如果修改时通过set命令明确指定PARENT_SCOPE参数,修改的变量作用域就是在上一层作用域,而不是当前作用域。
参考文献
[1] https://cmake.org/cmake/help/v3.10/manual/cmake-language.7.html#id9
[2] http://preshing.com/20170522/learn-cmakes-scripting-language-in-15-minutes/
[3] https://cgold.readthedocs.io/en/latest/overview.html
网友评论