在《深入浅出CMake(一):基础篇》文章中,我们已经知道了怎么依葫芦画瓢编写简单的 CMake 构建文件了,但如果应对复杂的工程的话,这还是远远不够的。
CMake 是一套编译构建体系,所以它有自己的一套语法概念,也有自己的 CMake Language,你可以讲它看做是一套脚本语言,所以它能做很多编程的事情。
这篇博文的目的是对 CMake 的基础语法进行概括,文章最后用一个九九乘法表的例子让读者加深印象。
文章开始前,让我们先回想 C/C 、JAVA、PYTHON 等等这些编程语言有什么特征
- 能够进行条件判断 if/else
- 能够循环处理 while/for/foreach
- 能够操作字符串 string
- 能够读写文件
这些特征足够我们在日常开发中编写常见的算法逻辑,处理很多问题了。
幸运的是 CMake 有类似的语法,能实现类似的功能。
CMake 有一个很重要的概念叫做命令.
代码语言:javascript复制COMMAND()
CMake 构建过程的操作基本上依靠各种各样的命令完成的,比如之前我们见过的
代码语言:javascript复制project(test)
add_executable(hello hello.cpp)
相信不难理解,官网中关于命令完整介绍在这里
下面开始,简单对 CMake 中一些语法过一眼
条件判断 if()/endif()
比如,它可以进行条件判断
代码语言:javascript复制if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
if() 和 endif() 是配对使用的。
循环判断 foreach()/while()
代码语言:javascript复制foreach(<loop_var> <items>)
<commands>
endforeach()
items 是一个 list,里面的元素用分号 ; 或者空格分割开来
比如
代码语言:javascript复制foreach(index A B C D)
也可以这样
代码语言:javascript复制foreach(index RANGE 9)
RANGE 是指定的迭代模型,index 取值从 0 到 9,包括 9.
代码语言:javascript复制while(<condition>)
<commands>
endwhile()
while 循环和普通的开发无多大差别,这个不细说。
当然跳出当前判断也有
代码语言:javascript复制break()
continue()
return()
设置变量 set()/unset()
CMake 的变量可以分为 普通变量、系统环境变量。
set() 命令可以设置普通变量、系统环境变量、和缓存。
代码语言:javascript复制set(<variable> <value>... [PARENT_SCOPE])
设置普通的变量,将 value 的值赋值给 variable,后面的参数是可选的,代表有效域。
代码语言:javascript复制set(ENV{<variable>} [<value>])
设置系统环境变量
代码语言:javascript复制set(<variable> <value>... CACHE <type> <docstring> [FORCE])
设置缓存
访问变量的时候用 ${variable}
list
有自己的数据结构
代码语言:javascript复制Reading
list(LENGTH <list> <out-var>)
list(GET <list> <element index> [<index> ...] <out-var>)
list(JOIN <list> <glue> <out-var>)
list(SUBLIST <list> <begin> <length> <out-var>)
Search
list(FIND <list> <value> <out-var>)
Modification
list(APPEND <list> [<element>...])
list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
list(INSERT <list> <index> [<element>...])
list(REMOVE_ITEM <list> <value>...)
list(REMOVE_AT <list> <index>...)
list(REMOVE_DUPLICATES <list>)
list(TRANSFORM <list> <ACTION> [...])
Ordering
list(REVERSE <list>)
list(SORT <list> [...])
CMake 对于 list 操作手段比较丰富,涉及到读取 list 信息,查找元素,修改、添加、插入元素及排序。
篇幅限制,这里不一一展开。
读写文件 file()
可以读写文件
代码语言:javascript复制Reading
file(READ <filename> <out-var> [...])
file(STRINGS <filename> <out-var> [...])
file(<HASH> <filename> <out-var>)
file(TIMESTAMP <filename> <out-var> [...])
Writing
file({WRITE | APPEND} <filename> <content>...)
file({TOUCH | TOUCH_NOCREATE} [<file>...])
file(GENERATE OUTPUT <output-file> [...])
Filesystem
file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
file(RENAME <oldname> <newname>)
file({REMOVE | REMOVE_RECURSE } [<files>...])
file(MAKE_DIRECTORY [<dir>...])
file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
file(SIZE <filename> <out-var>)
file(READ_SYMLINK <linkname> <out-var>)
file(CREATE_LINK <original> <linkname> [...])
Path Conversion
file(RELATIVE_PATH <out-var> <directory> <file>)
file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)
Transfer
file(DOWNLOAD <url> <file> [...])
file(UPLOAD <file> <url> [...])
Locking
file(LOCK <path> [...])
CMake 在构建过程中,避免不了要读写一些配置文件,这个时候 file() 命令就显得很有必要了。
操作字符串 string()
也可以对 string 文本进行操作
代码语言:javascript复制Search and Replace
string(FIND <string> <substring> <out-var> [...])
string(REPLACE <match-string> <replace-string> <out-var> <input>...)
Regular Expressions
string(REGEX MATCH <match-regex> <out-var> <input>...)
string(REGEX MATCHALL <match-regex> <out-var> <input>...)
string(REGEX REPLACE <match-regex> <replace-expr> <out-var> <input>...)
Manipulation
string(APPEND <string-var> [<input>...])
string(PREPEND <string-var> [<input>...])
string(CONCAT <out-var> [<input>...])
string(JOIN <glue> <out-var> [<input>...])
string(TOLOWER <string1> <out-var>)
string(TOUPPER <string1> <out-var>)
string(LENGTH <string> <out-var>)
string(SUBSTRING <string> <begin> <length> <out-var>)
string(STRIP <string> <out-var>)
string(GENEX_STRIP <string> <out-var>)
Comparison
string(COMPARE <op> <string1> <string2> <out-var>)
Hashing
string(<HASH> <out-var> <input>)
Generation
string(ASCII <number>... <out-var>)
string(CONFIGURE <string1> <out-var> [...])
string(MAKE_C_IDENTIFIER <string> <out-var>)
string(RANDOM [<option>...] <out-var>)
string(TIMESTAMP <out-var> [<format string>] [UTC])
string(UUID <out-var> ...)
string() 命令包含的功能很多,文章篇幅限制,有机会我之后会单独写一篇博文详细介绍。
终端打印 message()
也可以向终端打印。
代码语言:javascript复制message([<mode>] "message to display" ...)
message() 命令可以向终端打印字符串
并且,它支持不同类型
- none
- STATUS
- WARNING
- AUTHOR_WARNING
- SEND_ERROR
- FATAL_ERROR
- DEPRECATION
开发者可以根据实际情况定义不同的消息等级
数学运算 math()
还能做数学运算
代码语言:javascript复制math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])
上面命令的用法是,计算数学表达是 experssion,然后将结果保存在 variable 中,后面是指定的格式为 10 进制还是 16 进制。
正因为有了这些灵活的命令,CMake 显得无所不能,带来的好处也显而易见,程序员编写 CMakeLists.txt 会更加得心应手。
下面,我们就把 CMake 当做是一种编码语言,写一个简单的例子。
九九乘法表
我们的目的是用 CMake 在屏幕上打印一个九九乘法表。
代码语言:javascript复制cmake_minimum_required(VERSION 2.8.11)
project(test)
foreach(i RANGE 1 9)
set(exp "")
foreach(j RANGE 1 ${i})
math(EXPR value "${j} * ${i}")
string(APPEND exp "${j}" "*")
string(APPEND exp "${i}" "=")
string(APPEND exp "${value}" " ")
endforeach()
message(${exp})
endforeach()
这是 CMakeLists.txt 中的代码,可以看到主要运用了 foreach()、string()、set()、message() 几个命令。
在当前目录运行 cmake .
命令。
可以看到终端打印了如下结果
代码语言:javascript复制1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
-- Configuring done
-- Generating done
CMake 的命令很多,但只要我们把握大概的方向,在实际编码当中就可以现学现用,掌握了 CMake 的基本语法,我们就可以比较从容去应对在复杂的工程当中配置复杂的 CMakeLists.txt 文件了。
下一篇,讲解如何用 CMake 查找第三方库。