CMAKE 学习

1. 初始cmake

1.1 特点

  • 开源
  • 跨平台
  • 管理大型项目
  • 简化编译
  • 高效
  • 可扩展

    1.2 问题

    需要编写CMakeLists.txt,与pkgconfig配合不好。

    1.3 适用

    适用C/C++的大型工程,qt不需要。

    2. 安装

    官网下载
    包管理工具下载

3. 初试cmake

3.1 写一个简单的main.c和CMakeLists.txt

1
2
3
4
5
6
7
//main.c
#include <stdio.h>
int main()
{
printf(“Hello World from t1 Main!\n”);
return 0;
}
1
2
3
4
5
PROJECT (HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})

3.2 开始构建

1
2
3
cmake . #生成Makefile等文件
make #开始make
./hello #执行应用

3.3 简单解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PROJECT(projectname [CXX] [C] [Java])
# 可以指定工程的名称和语言。这个指令隐含定义了<projectname>_BINARY_DIR和<projectname>_SOURCE_DIR。
# 由于是内部编译,上面的变量代表了工程所在的目录,如果是外部编译会不同。
# 建议使用cmake预定义的PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR否则修改工程名称会修改前面的变量。

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
# SET命令显示定义变量

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"
...)

# 这个指令向terminal输出用户定义的信息,包含三种类型:
# SEND_ERROR, 产生错误,生成过程被跳过。
# STATUS, 输出前缀为-的信息
# FATAL_ERROR,立即终止所有cmake 过程。

ADD_EXECUTABLE(hello ${SRC_LIST})
# 定义了这个工程会生成一个文件名为hello的可执行文件。
# 使用${}来应用变量,但是在IF语句中,不需要${}。

3.4 基本语法

  1. 变量使用${}取值,if中直接变量名
  2. command(arg1 arg2 …)
  3. 指令大小写无关,参数和变量大小写相关。但是推荐全大写指令。

    3.5 关于语法的疑惑

    如果文件名中间没有空格,可以不加引号。但是如果有空格,需要加引号。
    1
    2
    # 例如
    SET(SRC_LIST "fu nc.c")

另外可以忽略source列表的源文件后缀,但是不建议这么做。
参数也可以使用;隔开。

3.6 清理工程

make clean 即可对结果进行清理

3.7 其他问题

  • cmake 不支持make distclean
  • cmake强烈建议外部构建(out-of-source build)

    3.8 内部构建与外部构建

    外部编译举例(编译wxGTX动态库和静态库)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 解压wxGTX
    mkdir static shared
    cd static
    ../configure --enable-static # 在static 生成静态库
    cd ../shared
    ../configure --enable-shared # 在shared生成动态库

    # 修改以前的编译方式
    mkdir build
    cd build
    cmake ..
    make

注意编译后的HELLO_BINARY_DIR指代的路径改变为<src目录>/build

3.9 小结

这一节讲了3个显示命令的用法和变量。后面开始构架工程。

4. 一个更好的helloworld

本章任务:

  1. 为工程添加src子目录,用来放置工程源代码
  2. 添加一个doc,放置工程文档hello.txt
  3. 在工程目录添加文本文件COPYWRITE,README
  4. 在工程目录添加一个runhello.sh,用来调试hello二进制
  5. 将构建后的目标文件放入构建目录的bin子目录
  6. 最终安装这些文件:将hello二进制和runhello.sh安装至/usr/bin,将doc目录的内容以及COPYWRITE/README安装到<工程目录>/doc/cmake/t2

4.1 准备工作

  1. 创建t2并把源码和CMakeLists.txt拷贝到目录下
  2. 创建src,并把源码cut到目录下,添加CMakeLists.txt
  3. 修改工程的CMakeLists.txt
  4. 建立build目录并外部编译。
    1
    2
    3
    4
    5
    6
    # src/CMakeLists.txt
    ADD_EXECUTABLE(hello main.c)

    # t1/CMakeLists.txt
    PROJECT(HELLO)
    ADD_SUBDIRECTORY(src bin)

4.2 语法解释

1
2
3
4
5
ADD_SUBDIRECTORY(source_dir [binary_dir][EXCLUDE_FROM_ALL])
# 如果不指定目录,那么bin文件会放在build/src目录下。
SUBDIRS(dir1 dir2 ...)
# 这个目录不推荐使用,可以一次添加多个子目录,并且即使外部编译子目录体系仍保存。
# 如果把第一条改成第二条命令,那么bin文件会放在build/src目录下。

4.3 另存为bin文件

1
2
3
4
5
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 设置exe文件输出路径
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 设置库文件输出路径
# 这个文件应该放在ADD_EXECUTABLE和ADD_LIBRARY路径。

4.4 如何安装

  1. 编译后make install安装
  2. 打包时的指定目录安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 例如一个简单的Makefile
    DESTDIR=
    install:
    mkdir -p $(DESTDIR)/usr/bin
    install -m 755 hello $(DESTDIR)/usr/bin

    # 通过make install 安装到/usr/bin
    # 或者make install DESTDIR=/tmp/test 将他安装在 /tmp/test/usr/bin 目录,打包时这个方式经常被使用。
    # 或者定义PREFIX使用autotools
    # ./configure --prefix=/usr 或者./configure --prefix=/usr/local

对于我们的Helloworld工程,可以引入一个新的cmake指令INSTALL和一个变量CMAKE_INSTALL_PREFIX
CMAKE_INSTALL_PREFIX类似configure命令的–prefix。
常用方法如下

1
cmake -DCMAKE_INSTALL_PREFIX=/usr

INSTALL指令用于定义安装规则,安装内容包括目标二进制、动态库以及文件、目录和脚本
INSTALL指令包含了各种安装类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 目标文件的安装:
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
# 参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库。
# 目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME特指可执行目标二进制。
# DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>

# 普通文件的安装
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
# 安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果默认不定义权限 PERMISSIONS,安装后的权限为:644

# 非目标文件的可执行程序安装(比如脚本之类):
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
# 跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为:755
# 目录的安装:
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])

# DIRECTORY 后面连接的是所在 Source 目录的相对路径,但务必注意:abc 和 abc/有很大的区别。
# 如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc,如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。
# PATTERN 用于使用正则表达式进行过滤,PERMISSIONS 用于指定 PATTERN 过滤后的文件权限。

# 安装时CMAKE 脚本的执行:
INSTALL([[SCRIPT <file>] [CODE <code>]] [...])

# SCRIPT 用于安装时调用cmake脚本文件(<abc>.cmake文件)
# CODE 用于执行CMAKE指令,必须以""括起来

4.5 修改Project并支持安装。

  1. 根目录下,添加doc,在doc下建立一个hello.txt
  2. 根目录下添加runhello.sh脚本,COPYWRITE和README
  3. 改写根目录的CMakeLists.txt
  4. 在build目录使用cmake -DCMAKE_INSTALL_PREFIX=<想安装的路径> .. 进行外部编译
  5. make && make install
1
2
3
4
5
6
# /CMakeLists.txt
PROJECT (HELLO)
ADD_SUBDIRECTORY(src bin)
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
INSTALL(PROGRAMS build/bin/hello runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)

4.6 小结

如果没有定义CMAKE_INSTALL_PREFIX,那么会默认安装到/usr/local

5. 静态库和动态库的构建

本章任务:

  1. 建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc向终端输出HelloWorld字符串
  2. 安装头文件与共享库

5.1 建立并搭建一个动态库

  1. 在工程目录下建立t3目录
  2. 在t3下创建CMakeLists.txt
  3. 在t3下创建lib目录,并创建hello.c,hello.h,lib/CMakeLists.txt
  4. 根目录下建立build目录并编译。
    1
    2
    3
    4
    5
    6
    7
    # /CMakeLists.txt
    PROJECT(HELLOLIB)
    ADD_SUBDIRECTORY(lib)

    # lib/CMakeLists.txt
    SET(LIBHELLO_SRC hello.c)
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

5.2 语法解释

1
2
3
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)

库类型有三种:
SHARED,动态库
STATIC,静态库
MODULE,在使用dyld的OS有效,如果不支持dyld,则被当做SHARED对待。

EXCLUDE_FROM_ALL 意思是库不会被默认构建,除非有其他组件依赖或者手工构建

5.3 添加一个静态库

修改lib 下的CMakeLists.txt来实现这个过程

1
2
3
4
5
6
7
8
9
10
# lib/CMakeLists.txt
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
# 先添加一个静态库,名字为hello_static
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
# 为了得到同名的静态库和动态库,所以设置输出的名称
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# 为了避免删除已经存在的hello.so(生成新target会清除同名的文件)所以,定义了CLEAN_DIRECT_OUTPUT 属性。

5.4 动态库版本号

按照规则,动态库是应该包含一个版本号的,我们可以看一下系统的动态库,一般情况是

1
2
3
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2

为了实现这一功能,所以我们修改了lib/CMakeList.txt

1
2
3
4
5
# lib/CMakeLists.txt
...
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
# VERSION指代动态库版本
# SOVERSION指代API版本

5.5 安装共享库和头文件

为了把libhello.so.x和libhello.a,hello.h安装到系统目录让其他人开发使用,本例中将hello共享库安装到< prefix >/lib目录,将hello.h安装到< prefix >/include/hello目录。
利用INSTALL命令可以做到这一点。

1
2
3
4
# lib/CMakeLists.txt
...
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)

然后bash执行

1
2
3
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install

可将头文件和共享库安装到/usr下

5.6 小结

本小节,我们谈到了:
如何通过 ADD_LIBRARY 指令构建动态库和静态库。
如何通过 SET_TARGET_PROPERTIES 同时构建同名的动态库和静态库。
如何通过 SET_TARGET_PROPERTIES 控制动态库版本
最终使用上一节谈到的 INSTALL 指令来安装头文件和动态、静态库。

6. 如何使用外部共享库和头文件

6.1 准备工作

  1. 建立t4目录,并在目录下建立src目录
  2. 进入src建一个main.c和CMakeLists.txt
  3. 在根目录建立工程的CMakeLists.txt
1
2
3
4
5
6
7
8
/* src/main.c */
# include <hello.h> //预处理头文件
int main()
{

HelloFunc();
return 0;
}
1
2
# src/CMakeLists.txt
ADD_EXECUTABLE(main main.c)

6.2 外部构建

  1. 建立build目录
  2. 执行外部构建,发现make报错

    error: hello.h: 没有那个文件或目录。
    因为hello.h位于/usr/include/hello 目录中,系统不能直接找到hello这个目录

6.3 引入头文件的搜索位置

src/CMakeLists.txt添加命令,引入头文件的搜索位置。

1
2
3
4
5
6
# src/CMakeLists.txt
INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(main main.c)

# INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
# 默认的行为是追加到当前的头文件搜索路径的后面

然后重新进行外部编译,结果出现不一样的报错。

error:undefined reference to `HelloFunc’
因为没有link到共享库libhello上

6.4 为 target 添加共享库

将目标文件链接到libhello,需要引入两个新指令。

1
2
3
4
# 添加非标准的共享库搜索路径,这里不使用
LINK_DIRECTORIES(directory1 directory2 ...)
# 为target添加需要链接的共享库。
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...)

所以修改src/CMakeLists.txt

1
2
3
INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(main main.c)
TARGET_LINK_LIBRARIES(main hello)

重新外部构建,正常运行。
可以使用ldd查看外部依赖。

1
2
3
4
5
ldd src/main
linux-gate.so.1 => (0xb7ee7000)
libhello.so.1 => /usr/lib/libhello.so.1 (0xb7ece000)
libc.so.6 => /lib/libc.so.6 (0xb7d77000)
/lib/ld-linux.so.2 (0xb7ee8000)

如果链接到静态库也是一样,就是把hello改成libhello.a

6.5 特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

这两个变量主要是用来解决以前 autotools 工程中–extra-include-dir 等参数的支持的。
也就是,如果头文件没有存放在常规路径(/usr/include, /usr/local/include 等),则可以通过这些变量就行弥补。

1
2
3
4
5
6
7
8
9
10
11
为了将程序更智能一点,我们可以使用 CMAKE_INCLUDE_PATH来进行,使用 bash 的方法
如下:
export CMAKE_INCLUDE_PATH=/usr/include/hello
然后在头文件中将 INCLUDE_DIRECTORIES(/usr/include/hello)替换为:
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)

如果你不使用 FIND_PATH,CMAKE_INCLUDE_PATH 变量的设置是没有作用的,你不能指望它会直接为编译器命令添加参数-I<CMAKE_INCLUDE_PATH>。
以此为例,CMAKE_LIBRARY_PATH 可以用在 FIND_LIBRARY中。

6.6 小节

本节我们探讨了:
如何通过 INCLUDE_DIRECTORIES 指令加入非标准的头文件搜索路径。
如何通过 LINK_DIRECTORIES 指令加入非标准的库文件搜索路径。
如果通过 TARGET_LINK_LIBRARIES 为库或可执行二进制加入库链接。
并解释了如何链接到静态库。

7. cmake常用变量和常用环境变量

7.1 变量定义和引用

显式定义:
SET(VARIABLE_NAME file)
隐式定义:
比如PROJECT会隐式定义< projectname >_BINARY_DIR< projectname >_SOURCE_DIR 两个变量。
变量引用
使用${}俩引用变量,而在IF等语句中直接使用变量名。

7.2 CMake常用变量

  1. CMAKE_BINARY_DIR | PROJECT_BINARY_DIR|< projectname >_BINARY_DIR
    三个变量指代同样的内容,如果是内部构建,就是工程的根目录。如果是外部构建,指的是编译发生的目录。
  2. CMAKE_SOURCE_DIR|PROJECT_SOURCE_DIR|<projectname>_SOURCE_DIR
    这三个变量指代的内容是一致的,不论哪一种构建方式,都是工程的根目录。
  3. CMAKE_CURRENT_SOURCE_DIR
    当前处理的CMakeLists.txt所在的路径,比如上面提到的src子目录。
  4. CMAKE_CURRENT_BINARY_DIR
    如果是内部构建,它跟CMAKE_CURRENT_SOURCE_DIR 一致,如果是外部构建,他指的是 target 编译目录。
    使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。
  5. CMAKE_CURRENT_LIST_FILE
    输出调用这个变量的 CMakeLists.txt 的完整路径
  6. CMAKE_CURRENT_LIST_LINE
    输出这个变量所在的行
  7. CMAKE_MODULE_PATH
    这个变量用来定义自己的 cmake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理
    CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。
    比如
    1
    SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

这时候可以通过 INCLUDE 指令来调用自己的模块了。

  1. EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH
    分别用来重新定义最终结果的存放目录
  2. PROJECT_NAME
    返回通过 PROJECT 指令定义的项目名称。

    7.3 CMake调用环境变量的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ENV{NAME}
# 这个指令调用环境变量
# 比如
MESSAGE(STATUS "HOME dir : $ENV{HOME}")
# 设置环境变量的方式
SET(ENV{NAME} value)

CMAKE_INCLUDE_CURRENT_DIR
# 自动添加 CMAKE_CURRENT_BINARY_DIR 和CMAKE_CURRENT_SOURCE_DIR 到当前处理的 CMakeLists.txt。

CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
# 将工程提供的头文件目录始终至于系统头文件目录的前面

CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

7.4 系统信息的变量

  1. CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
  2. CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 4
  3. CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如 2.4.6 中的 6
  4. CMAKE_SYSTEM,系统名称,比如 Linux-2.6.22
  5. CMAKE_SYSTEM_NAME,不包含版本的系统名,比如 Linux
  6. CMAKE_SYSTEM_VERSION,系统版本,比如 2.6.22
  7. CMAKE_SYSTEM_PROCESSOR,处理器名称,比如 i686.
  8. UNIX,在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin
  9. WIN32,在所有的 win32 平台为 TRUE,包括 cygwin

    7.5 主要的开关选项

  10. CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS
    用来控制IF ELSE语句的书写方式,语法部分会讲到
  11. BUILD_SHARED_LIBS
    用来控制默认的库编译方式,如果不设置,使用ADD_LIBRARY并没有指定库类型的情况下,默认编译生成的都是静态库。
    如果SET(BUILD_SHARED_LIBS ON)后,默认生成的是动态库。
  12. CMAKE_C_FLAGS
    设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加
  13. CMAKE_CXX_FLAGS
    设置C++编译选项,也可以通过指令ADD_DEFINITIONS()添加

    7.6 小结

    本章介绍了一些较常用的 cmake 变量,这些变量仅仅是所有 cmake 变量的很少一部分。

8. CMake常用命令

8.1 基本指令

  1. ADD_DEFINITATIONS
    向C/C++编译器添加-D定义,比如
    1
    ADD_DEFINITATIONS(-DENABLE_DEBUG -DABC)

如果代码中定义了#ifdef #endif代码块,那么会生效。

  1. ADD_DEPENDENCIES
    定义target依赖的其他target,确保在这个target编译前,其他target已经被构建。
    1
    ADD_DEPENDENCIES(target-name depend-target1)

8.4 控制指令

  1. IF指令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    IF(expression)
    #THEN section
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ELSE(expression)
    COMMAND1(ARGS...)
    COMMAND2(ARGS...)
    ENDIF(expression)

    # 表达式使用方法
    IF(var)
    # 如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND为真

    IF(NOT var)

    IF(var1 AND var2)
    # 当两个变量都为真是为真。
    IF(var1 OR var2)
    # 当两个变量其中一个为真时为真。
    IF(COMMAND cmd)
    # 当给定的 cmd 确实是命令并可以调用是为真。
    IF(EXISTS dir)或者 IF(EXISTS file)
    # 当目录名或者文件名存在时为真。
    IF(file1 IS_NEWER_THAN file2)
    # 当 file1 比 file2 新,或者 file1/file2 其中有一个不存在时为真,文件名请使用完整路径。
    IF(IS_DIRECTORY dirname)
    # 当 dirname 是目录时,为真。
    IF(variable MATCHES regex)
    # 当给定的变量或者字符串能够匹配正则表达式 regex 时为真。
    IF(variable LESS|GREATER|EQUAL number)
    # 数字比较
    IF(string STRLESS|STRGREATER|STREQUAL string)
    # 字典序比较
    IF(DEFINED variable)
    # 如果被定义为真
  2. while
    WHILE 指令的语法是:

    1
    2
    3
    4
    5
    WHILE(condition)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    ENDWHILE(condition)
  3. FOREACH
    FOREACH 指令的使用方法有三种形式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 列表
    FOREACH(loop_var arg1 arg2 ...)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
    ENDFOREACH(loop_var)
    # 范围
    FOREACH(loop_var RANGE total)
    ENDFOREACH(loop_var)
    # 从 0 到 total 以1为步进

    # 范围和步进
    FOREACH(loop_var RANGE start stop [step])
    ENDFOREACH(loop_var)

    # 这个指令需要注意的是,直到遇到 ENDFOREACH 指令,整个语句块才会得到真正的执行。

8.5 小结

本小节基本涵盖了常用的 cmake 指令,包括基本指令、查找指令、安装指令以及控制语句等,特别需要注意的是,在控制语句条件中使用变量,不能用${}引用,而是直接应用变量名。

10.参考

《Cmake 实践》