CMake 语言 15 分钟入门教程
cmake 是一个跨平台的编译安装工具,可以用简单的语句来描述所有平台的编译安装过程。
本文介绍 cmake 的基础语法。
第一个例子
使用任意的文本编辑器,输入:
1 | message("Hello world!") |
然后保存为 hello.txt
文本文件,执行:
1 | cmake -P hello.txt |
输出:
Hello world!
所有变量都是字符串
在 cmake 中,所有变量都是字符串。可以使用 ${}
来引用一个变量。例如,修改 hello.txt
:
1 | message("Hello ${NAME}!") |
然后在执行 cmake
命令时对变量 NAME
进行定义:
1 | cmake -DNAME=Leo -P hello.txt |
输出:
Hello Leo!
如果直接执行:
1 | cmake -P hello.txt |
输出:
Hello !
也可以直接在脚本中定义变量:
1 | set(URL "leehao.me") |
执行 cmake
命令后,输出:
Please visit leehao.me!
使用前缀模拟数据结构
cmake 并没有类,但是我们可以通过定义一组变量,该组变量以相同的前缀来模拟类,并使用嵌套的 ${}
来引用这些变量。例如:
1 | set(LEO_NAME "Leo Lee") |
输出:
Leo Lee lives at Guangzhou City.
每个语句都是命令
在 cmake 中,每个语句都是一个命令,这个命令使用字符串参数列表(参数可采用空格分隔),并且没有返回值。
例如,可以使用 math
来执行算术运算。math
接受三个参数,第一个参数是 EXPR
,第二个参数是变量名称,第三个参数是需要计算的算术表达式,其运算结果会被赋予第二个变量。
1 | math(EXPR MY_SUM "1 + 1") |
输出:
The sum is 2.
Double that is 4.
流程控制命令
流程控制命令包括 if/endif
和 while/endwhile
。
例如,判断当前环境是否为 win32:
1 | if(WIN32) |
例如,使用 while/endwhile
循环打印出所有小于 20 的斐波那契数列:
1 | set(A "1") |
在 cmake 中,变量与数字进行比较的语法与其他语言不同,例如上面的小于比较,就使用了 LESS
,其他比较条件的使用,可以参考官方说明:链接。
输出:
1
1
2
3
5
8
13
列表:分号分隔的字符串
直接以例子说明。以下例子中,将三个参数传递给 math
:
1 | set(ARGS "EXPR;T;1 + 1") |
如果引用变量 ${}
外部添加双引号,则 cmake 会将整个字符串作为一个参数,并保留分号:
1 | set(ARGS "EXPR;T;1 + 1") |
输出:
EXPR;T;1 + 1
如果 ${}
不带双引号,例如:
1 | set(ARGS "EXPR;T;1 + 1") |
则输出:
EXPRT1 + 1
如果有两个以上的参数传递给 set
命令,则它们会被分号连接,然后传递给指定的变量:
1 | set(MY_LIST Please visit leehao.me) |
输出:
Please;visit;leehao.me
可以使用 list 命令处理列表:
1 | set(MY_LIST Please visit url leehao.me) |
输出:
Please;visit;leehao.me
可以使用 foreach/endforeach
处理列表,迭代除第一个参数外的列表的所有项,并将每项赋值给第一个参数变量:
1 | foreach(ARG Please visit url leehao.me) |
输出:
Please
visit
url
leehao.me
定义函数
在 cmake 中,可以使用 function/endfunction
来定义一个函数,例如下面的函数将参数值 * 2 后输出:
1 | function(doubleIt VALUE) |
输出:
8
函数中定义的变量不会影响调用方的作用域,如果需要返回值,可以将变量传递给函数,然后使用 set
命令,并指定 PARENT_SCOPE
参数:
1 | function(doubleIt VARNAME VALUE) |
cmake 中,使用 macro/endmacro
定义宏。与函数不同,宏内改变变量的值会影响调用方的作用域:
1 | macro(doubleIt VARNAME VALUE) |
设置和获取属性
cmake 可以使用 add_executable
、add_library
或 add_custom_target
等命令来定义目标(target)。与变量不同,目标在每个作用域都可见,且可以使用 get_property
和 set_property
获取或设置其属性。
与上面例子不同,直接使用 cmake -P
执行脚本会导致报错:
Command add_executable() is not scriptable
故这里改用 cmake 来编译单个源文件的方式来展示目标属性的获取。
源文件 leehao.cpp
如下:
1 |
|
CMakeLists.txt
如下,获取目标 test
的 SOURCES
属性,并赋值给 MYAPP_SOURCES
:
1 | cmake_minimum_required (VERSION 2.8) |
执行 cmake .
,输出:
– The C compiler identification is GNU 4.8.5
– The CXX compiler identification is GNU 4.8.5
– Check for working C compiler: /usr/bin/cc
– Check for working C compiler: /usr/bin/cc – works
– Detecting C compiler ABI info
– Detecting C compiler ABI info - done
– Check for working CXX compiler: /usr/bin/c++
– Check for working CXX compiler: /usr/bin/c++ – works
– Detecting CXX compiler ABI info
– Detecting CXX compiler ABI info - done
leehao.cpp
– Configuring done
– Generating done
– Build files have been written to: /home/lihao/code/cpp/cmake
可以看到输出 leehao.cpp
,也就是说正确获取了目标 test
的 SOURCES
属性。
上面的命令会输出 Makefile
文件,接下来还可以执行 make
命令,以进一步编译生成可执行文件 test
。
参考资料
- https://www.hahack.com/codes/cmake/
- https://cmake.org/download/
- https://blog.csdn.net/u011231598/article/details/80338941
- https://github.com/SFUMECJF/cmake-examples-Chinese
- https://preshing.com/20170522/learn-cmakes-scripting-language-in-15-minutes/
- https://baike.baidu.com/item/cmake/7138032
- https://stackoverflow.com/questions/38131127/cmake-error-command-add-definitions-is-not-scriptable