cmake 是一个跨平台的编译安装工具,可以用简单的语句来描述所有平台的编译安装过程。
本文介绍 cmake 的基础语法。
第一个例子
使用任意的文本编辑器,输入:
message("Hello world!")
然后保存为 hello.txt 文本文件,执行:
cmake -P hello.txt
输出:
Hello world!
所有变量都是字符串
在 cmake 中,所有变量都是字符串。可以使用 ${} 来引用一个变量。例如,修改 hello.txt :
message("Hello ${NAME}!")
然后在执行 cmake 命令时对变量 NAME 进行定义:
cmake -DNAME=Leo -P hello.txt
输出:
Hello Leo!
如果直接执行:
cmake -P hello.txt
输出:
Hello !
也可以直接在脚本中定义变量:
set(URL "leehao.me")
message("Please visit ${URL}!")
执行 cmake 命令后,输出:
Please visit leehao.me!
使用前缀模拟数据结构
cmake 并没有类,但是我们可以通过定义一组变量,该组变量以相同的前缀来模拟类,并使用嵌套的 ${} 来引用这些变量。例如:
set(LEO_NAME "Leo Lee")
set(LEO_ADDRESS "Guangzhou City")
set(PERSON "LEO")
message("${${PERSON}_NAME} lives at ${${PERSON}_ADDRESS}.")
输出:
Leo Lee lives at Guangzhou City.
每个语句都是命令
在 cmake 中,每个语句都是一个命令,这个命令使用字符串参数列表(参数可采用空格分隔),并且没有返回值。
例如,可以使用 math 来执行算术运算。math 接受三个参数,第一个参数是 EXPR,第二个参数是变量名称,第三个参数是需要计算的算术表达式,其运算结果会被赋予第二个变量。
math(EXPR MY_SUM "1 + 1")
message("The sum is ${MY_SUM}.")
math(EXPR DOUBLE_SUM "${MY_SUM} * 2")
message("Double that is ${DOUBLE_SUM}.")
输出:
The sum is 2. Double that is 4.
流程控制命令
流程控制命令包括 if/endif 和 while/endwhile。
例如,判断当前环境是否为 win32:
if(WIN32)
message("You're running CMake on Windows.")
endif()
例如,使用 while/endwhile 循环打印出所有小于 20 的斐波那契数列:
set(A "1")
set(B "1")
while(${A} LESS "20")
message("${A}") # 打印 A
math(EXPR T "${A} + ${B}") # 计算 A + B 的值,并存储在变量 T
set(A "${B}") # 设置变量 A 的值为 B
set(B "${T}") # 设置变量 B 的值为 T
endwhile()
在 cmake 中,变量与数字进行比较的语法与其他语言不同,例如上面的小于比较,就使用了 LESS,其他比较条件的使用,可以参考官方说明:链接。
输出:
1 1 2 3 5 8 13
列表:分号分隔的字符串
直接以例子说明。以下例子中,将三个参数传递给 math:
set(ARGS "EXPR;T;1 + 1")
math(${ARGS}) # 等价于 math(EXPR T "1 + 1")
message(${T})
如果引用变量 ${} 外部添加双引号,则 cmake 会将整个字符串作为一个参数,并保留分号:
set(ARGS "EXPR;T;1 + 1")
message("${ARGS}")
输出:
EXPR;T;1 + 1
如果 ${} 不带双引号,例如:
set(ARGS "EXPR;T;1 + 1")
message(${ARGS})
则输出:
EXPRT1 + 1
如果有两个以上的参数传递给 set 命令,则它们会被分号连接,然后传递给指定的变量:
set(MY_LIST Please visit leehao.me)
message("${MY_LIST}")
输出:
Please;visit;leehao.me
可以使用 list 命令处理列表:
set(MY_LIST Please visit url leehao.me)
list(REMOVE_ITEM MY_LIST "url")
message("${MY_LIST}")
输出:
Please;visit;leehao.me
可以使用 foreach/endforeach 处理列表,迭代除第一个参数外的列表的所有项,并将每项赋值给第一个参数变量:
foreach(ARG Please visit url leehao.me)
message("${ARG}")
endforeach()
输出:
Please visit url leehao.me
定义函数
在 cmake 中,可以使用 function/endfunction 来定义一个函数,例如下面的函数将参数值 * 2 后输出:
function(doubleIt VALUE)
math(EXPR RESULT "${VALUE} * 2")
message("${RESULT}")
endfunction()
doubleIt("4")
输出:
8
函数中定义的变量不会影响调用方的作用域,如果需要返回值,可以将变量传递给函数,然后使用 set 命令,并指定 PARENT_SCOPE 参数:
function(doubleIt VARNAME VALUE)
math(EXPR RESULT "${VALUE} * 2")
set(${VARNAME} "${RESULT}" PARENT_SCOPE) # 设置返回值
endfunction()
doubleIt(RESULT "4") # RESULT 变量存储函数的返回值
message("${RESULT}") # 输出:8
cmake 中,使用 macro/endmacro 定义宏。与函数不同,宏内改变变量的值会影响调用方的作用域:
macro(doubleIt VARNAME VALUE)
math(EXPR ${VARNAME} "${VALUE} * 2")
endmacro()
doubleIt(RESULT "4")
message("${RESULT}")
设置和获取属性
cmake 可以使用 add_executable、add_library 或 add_custom_target 等命令来定义目标(target)。与变量不同,目标在每个作用域都可见,且可以使用 get_property 和 set_property 获取或设置其属性。
与上面例子不同,直接使用 cmake -P 执行脚本会导致报错:
Command add_executable() is not scriptable
故这里改用 cmake 来编译单个源文件的方式来展示目标属性的获取。
源文件 leehao.cpp 如下:
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
CMakeLists.txt 如下,获取目标 test 的 SOURCES 属性,并赋值给 MYAPP_SOURCES:
cmake_minimum_required (VERSION 2.8)
add_executable(test leehao.cpp)
get_property(MYAPP_SOURCES TARGET test PROPERTY SOURCES)
message("${MYAPP_SOURCES}")
执行 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