CMake 快速入门

  CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样 Makefile 或者 project 文件,能测试编译器所支持的 C++ 特性,类似 UNIX 下的 automake

  说在前头的个人的一些想法 (《CMake Practice》 也这么说 ) 和建议吧:
  ①. CMake 的学习过程是实践过程,没有实践,读的再多几天后也会忘记。所以,最好是你现在有实际的项目需求(没有的话,希望这个日后还可以作为参考),另外,文章中的实例请你必须亲自运行验证一下。

  ②. 如果你的工程只有几个文件,直接编写 Makefile 是个可行的选择,不过考虑到移植性,比如究竟是gcc呢?g++呢?还是什么其他的呢?还是使用 CMake 吧。

  ③. 如果使用的是 C/C++/Java 之外的语言,请不要使用 CMake(目前还不支持)。

  ④. 如果你使用的语言有非常完备的构建体系,比如 Java 的 Ant,也不需要学习 CMake

CMake 简介

  先来说说 Unix 下的 automake 吧。
  某天我要安装某个开源项目 tinySLAM,结果,人家用的是 automake,结果就悲剧了。
  README 提示先运行他的某个脚本 ./bootstrap,其实里面就是一堆 automake 的命令,内容如下。然后就是典型的 ./configuremakemake install

1
2
3
4
5
libtoolize
aclocal
autoheader
automake --add-missing --copy
autoconf

  结果,第一步就报错了…

  搞到最后,才发现,automake 死板的要求你必须有某些文件,空的也行,不然不让你构建…所以,只能很无奈的表示屈服,通过下面的命令,问题是解决了。

1
$ touch NEWS README AUTHORS ChangeLog

  所以说,automake 实在不行啦,确实需要 CMake 来拯救。通过上面的例子,也是想说明一下,了解包括 automake 在内的构建套件,与我们的日常开发息息相关。因为开源,源代码都有了,那就需要你本地构建、安装,这就需要构建套件的支持。所以很多源码都会提**供构建套件需要的配置文件(如上面的 ./bootstrap./configure),而目前比较受欢迎,使用最广的(你可以自己看看 github…),便是 CMake,所以你会在很多源代码中看到 CMakeLists.txt 的踪影。

  因此,学习 CMake,熟悉 CMake 的基本语法,有助于我们了解源代码的依赖关系,更好的阅读开源工程;另外,更重要的,则是,源代码的本地构建、安装并不总是顺利的(如上面的例子),对 CMake 有一定的了解,能更好的解决我们在构建、安装过程中遇到的问题!

  CMake 的组态档取名为 CMakeLists.txtCMake 并不直接建构出最终的软件,而是产生标准的建构档(如 UnixMakefile)。

  CMake 是跨平台,可生成 native 编译配置文件,在 Linux/Unix平台,生成 Makefile,在苹果平台,生成 xcode,在 Windows 平台,可以生成 MSVC 工程文件。

  CMake 的应用非常广泛!Qt 采用了 cmake 作为构建系统,openCV 采用 cmake 作为构建系统…

简单的 HelloWorld

  我们所有的 CMake 练习均放在 ~/Workspace/cmake_ws/ 下,一般情况下,Ubuntu 都自带CMakeWindows 下也可以安装 CMake

1
2
3
4
5
6
> cd ~/Workspace/
> mkdir cmake_ws
> cd cmake_ws/
> mkdir ex_01
> cd ex_01/
> touch CMakeLists.txt main.c

  新建的工程源文件 main.cCMake 文件 CMakeLists.txt 内容如下:

1
2
3
4
5
6
7
8
// main.c

#include <stdio.h>

int main(int argc, char **argv[]){
printf("Hello World from ex_01 Main!\n");
return 0;
}

注:
  main.c 是一个再典型不过的 hello world

1
2
3
4
5
6
7
8
9
10
11
12
# CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

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 main.c)
ADD_EXECUTABLE(hello ${SRC_LIST})

注:
  上述是一个简单的 CMake 文件,CMAKE_MINIMUM_REQUIRED 指令用于检查系统 CMake 版本是否满足最低要求,PROJECT 指令通过名字定义工程,SET 用于设置 CMake 变量,另外通过 ${VAR_NAME} 的方式引用变量,MESSAGE 用于向终端输出信息,ADD_EXECUTABLE 则用于生成可执行二进制文件。 CMake 指令是大小写不敏感的,很多开源项目的 CMakeLists.txt 中,指令都是小写的。更多具体的语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 定义这个工程会生成一个文件名为 target 的可执行文件,相关的源文件是source1、source2...定义的源文件列表
# 参数之间可以通过空格或者";"分开
ADD_EXECUTABLE(target source1 source2...)
ADD_EXECUTABLE(target source1;source2...)

# 定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。
# 这个指令隐式的定义了两个 CMake 变量: <projectname>_BINARY_DIR 以及 <projectname>_SOURCE_DIR
PROJECT(projectname [CXX][C][Java])

# 这个指令用于向终端输出用户定义的信息,包含了三种类型:
# SEND_ERROR,产生错误,生成过程被跳过
# STATUS,输出前缀为" - - "的信息
# FATAL_ERROR,立即终止所有 CMake 过程
MESSAGE([SEND_ERROR |STATUS | FATAL_ERROR] "message to display"...)

# 这个指令可以用来显式的定义变量。
# 比如我们用到的是 SET(SRC_LIST main.c);如果有多个源文件,也可以定义成 SET(SRC_LIST main.c t1.c t2.c)
SET(VAR [VALUE] [CACHETYPE DOCSTRING[FORCE]])

开始构建:
  所有的文件创建完成后,ex_01 目录中应该存在 main.cCMakeLists.txt 两个文件,接下来我们来构建这个工程,在这个目录运行:

1
2
3
4
> cd ~/Workspace/cmake_ws/ex_01/
> cmake .
> make
> ./hello

  “cmake .”. 表示当前目录,运行后将会自动生成 CMakeFiles 文件夹,CMakeCache.txtcmake_install.cmake 等文件,并且生成了 Makefile。下一步 “make” 则是去执行的 Makefile,生成我们的目标文件 hello你可以通过 “make VERBOSE=1” 看到 make 构建的详细过程。最后通过 ./hello 即可运行目标二进制文件。完成构建后的工程源文件目录如下:

  实际上,上面我们采用的是一种叫做 in-source build(内部构建) 的构建方式,顾名思义,这种方式直接在源代码中进行构建,构建过程的中间产物以及最终的目标文件都会混在一起,我们没办法将项目文件与其分开,更没办法做到自动删除这些中间文件和目标文件。
  另外一种比较合理的构建方式叫做 out-of-source build(外部构建) ,这种方式单独在与源代码工程独立的目录下执行构建,保证源代码的纯洁性,更能实现中间文件和目标文件的快速删除。

1
2
3
4
5
6
7
8
> cd ~/Workspace/cmake_ws/ex_01/
> mkdir build
> cd build/
> cmake ..
> make
> ./hello
> cd ..
> rm -rf build/

  “cmake ..” 中的 “..” 表示 build 目录的父目录,即是我们的源代码工程目录,因为父目录存在我们需要的 CMakeLists.txt。这里要说明的是,你也可以在其他地方进行构建(“cmake )。显然,这种方式是有点麻烦,所以一般 建议在源代码工程目录下新建 build 目录进行构建
  通过 外部构建 这种方式完成构建后的工程源文件目录如下,构建过程中的中间产物和目标文件都存放在 build 目录中,源文件并没有受其影响。通过删除该文件夹,即可实现中间文件快速删除。建议大家采用这种方式进行构建,后续的练习也均按照这种方式进行

更好一点的 HelloWorld

  上述的工程还不够好,跟一般的项目工程还有很大区别,一般项目工程大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── build
| └── bin
| | └── <project_name>
├── doc
| └── <project_name>.txt
├── src
| ├── xxx.c
| └── CMakeLists.txt
├── CMakeLists.txt
├── COPYRIGHT
├── README
└── run<project_name>.sh

  src,用来放置工程源代码,包括源文件、头文件以及该目录的 CMake 文件;doc,用来放置工程的说明文档;文本文件 COPYRIGHTREADME 是有关 license 和如何使用的说明;构建后的目标文件将放在构建目录的 bin 子目录下;工程目录下添加一个 run<project_name>.sh 脚本,可以用来调用二进制文件,比如需要按照一定配置、顺序调用多个目标文件,才能完成整个项目的功能。

  首先,建立该目录树,编写 src 目录下的工程源代码,包括 main.c 和该目录的 CMake 文件 CMakeLists.txt

1
2
3
4
5
6
7
// ./src/main.c
#include <stdio.h>

int main(int argc, char **argv[]){
printf("Hello World from ex_02 Main!\n");
return 0;
}

  main.c 文件基本与第一个例子一样。

1
2
3
4
# ./src/CMakeLists.txt
ADD_EXECUTABLE(hello main.c)

INSTALL(TARGETS hello RUNTIME DESTINATION bin)

  ADD_EXECUTABLE 用于生成目标文件,INSTALL 命令则是跟 make install 安装相关,这里是安装目标文件。

  接下来是工程根目录下的 CMakeLists.txt,该文件是整个工程的 CMake 文件,需要使用 src 目录下的 CMake 文件。

1
2
3
4
5
6
7
8
9
10
11
12
# ./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(FILES COPYRIGHT README DESTINATION doc/cmake/ex_02)
INSTALL(DIRECTORY doc/ DESTINATION doc/cmake/ex_02)

ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/hello)
ENABLE_TESTING()

  ADD_SUBDIRECTORY 将整个工程的 CMake 文件与 src 工程源代码关联;INSTALL 用于安装,这里是安装脚本文件、普通文件和 doc 目录。ADD_TESTENABLE_TESTING 则与 “make test” 相关,具体的使用方法如下:

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
49
50
51
# 这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
# 上面的例子定义了将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为 bin 目录。
# 如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在 build/src 目录(这个目录跟原有的 src 目录对应)
# 指定 bin 目录后,相当于在编译时将 src 重命名为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

# INSTALL 指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等
# 参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件(可执行二进制、动态库、静态库)
# 目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制
# DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径;否则是相对路径${CMAKE_INSTALL_PREFIX}/<dir>
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT<component>]
[OPTIONAL]]
[...]
)

# 用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径(相对于指令所在 CMakeLists.txt)
INSTALL(FILES files...
DESTINATION<dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT<component>]
[RENAME <name>] [OPTIONAL]
)

# 非目标文件的可执行程序安装(比如脚本之类),跟上面的 FILES 指令使用方法相似
INSTALL(PROGRAMS files...DESTINATION<dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT<component>]
[RENAME <name>] [OPTIONAL]
)

# DIRECTORY 后面连接的是所在 Source 目录的相对路径,但务必注意:abc 和 abc/有很大的区别。
# 如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc;
# 如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONSpermissions...]
[DIRECTORY_PERMISSIONSpermissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT<component>]
[[PATTERN<pattern> |REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]]
[...]
)

构建:
  构建过程基本与上一个例子一样,make 之后,生成的目标文件将保存在 ./build/bin/ 目录下,make testmake install 分别执行上述的 ADD_TESTINSTALL 指令。

1
2
3
4
5
6
7
8
9
10
> cd ~/Workspace/cmake_ws/ex_02/
> mkdir build
> cd build/
> cmake ..
> make
> ./hello
> make test
> make install
> cd ..
> rm -rf build/

  前面说过,DESTINATION 定义了安装的路径,如果路径以 / 开头,那么指的是绝对路径;否则是相对路径 ${CMAKE_INSTALL_PREFIX}/<dir>,默认的的安装路径前缀 ${CMAKE_INSTALL_PREFIX}/usr/local,所以,采用默认的安装路径 make install 时,会因为权限问题而报错。

  在执行 “cmake” 命令时可以指定参数 CMAKE_INSTALL_PREFIX,如下:

安装:
  通过前面的分析可以看出,安装其实就是将目标文件(可能是二进制文件或者静态库)及其依赖的文件(可能是动态库),还有相关的说明文档等放在指定的目录下。

  可能你会有这样的疑问:不就存放些文件嘛,干嘛说成安装这么高大上?

  其实,我个人理解,称为 安装 主要是因为存放的路径比较特殊,如上面提到的 /usr/local,这些路径存在于 shell 的默认路径中。举个例子,某个可执行文件安装到 /usr/local 中,那他在任何一个terminal 中都能被运行,这不就像在 Windows 下,随便双击某个桌面图标,就能启动程序一样,把那个程序安装到系统上了。

CMake,库的创建、安装&使用

  了解了二进制可执行文件的 CMake 工程创建、构建和安装之后,这一节我们了解一下库,包括静态库(.a)和动态库(.so)。一般库工程大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── build
| └── lib
| | ├── lib<project_name>.so
| | └── lib<project_name>.a
├── doc
| └── <project_name>.txt
├── lib
| ├── xxx.c
| └── CMakeLists.txt
├── CMakeLists.txt
├── COPYRIGHT
└── README

  与上一个例子的区别只体现在放置工程源代码的目录变成 lib,而不是 src,其实就换个名字,换汤不换药。
  我们在源代码工程 lib 目录下添加 hello.chello.hCMakeLists.txt

1
2
3
4
5
6
7
8
// ./lib/hello.h

#ifndef _HELLO_H_
#define _HELLO_H_

void HelloFunc(void);

#endif

  以库的形式提供 HelloFunc() 函数。

1
2
3
4
5
6
7
8
// ./lib/hello.c

#include "hello.h"
#include <stdio.h>

void HelloFunc(){
printf("Hello World from ex_03 function HelloFunc()\n");
}

  HelloFunc() 函数就一简单的 hello world 功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ./lib/CMakeLists.txt

SET(LIBHELLO_SRC hello.c)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")

INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

INSTALL(FILES hello.h DESTINATION include)

  ADD_LIBRARY 指令用于生成目标库,动态库或者静态库;SET_TARGET_PROPERTIES 可以用于修改目标文件的属性,上面例子依次为动态库的版本信息、静态库的输出名。具体的使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# libname 不需要写全 libhello.so,只需要填写 hello 即可,CMake 系统会自动为你生成 libhello.X
# SHARED,动态库;STATIC,静态库
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN
)

# 这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本
SET_TARGET_PROPERTIES(target1 target2...
PROPERTIES prop1 value1
prop2 value2...
)
# VERSION 指代动态库版本,SOVERSION 指代 API 版本
SET_TARGET_PROPERTIES(hello PROPERTIESVERSION 1.2 SOVERSION 1)

  在上面的例子中,hello 作为一个 target 是不能重名,所以,只能为动态库和静态库创建不同的 target。另一方面,又想保证动态库静态库的名字均为 libhello.X,所以可以通过修改静态库文件输出名属性的方法来满足需求。

  此外,按照规则,动态库是应该包含一个版本号的。按照上述例子进行构建,生成的动态库如下。可以看出,除了生成的 libhello.so.1.2,其他两个是到该动态库的链接,方便调用者灵活调用。

  INSTALL 指令和之前类似,这里要注意的是,静态库要使用 ARCHIVE 关键字,另外,安装的时候需要将库的 .h 头文件一并安装。

1
2
3
4
5
6
INSTALL(TARGETS hello hello_static 
LIBRARY DESTINATION lib
ARCHIVED ESTINATION lib
)

INSTALL(FILES hello.h DESTINATION include/hello)

  顶层目录下的 CMakeLists.txt 与上一个例子基本一致。

1
2
3
4
5
# ./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)

  在这个例子中,我们可以构建静态库和动态库,并完成该库的安装。

  接下来这个例子,便是如何使用上面创建的静态库和动态库,其工程目录与第二个例子一致,主要区别在 src 源代码目录的内容上。

1
2
3
4
5
6
7
8
// ./src/main.c
#include "hello.h"

int main(int argc, char **argv[]){
HelloFunc();

return 0;
}

  调用通过库提供的 HelloFunc() 函数。

1
2
3
4
5
6
7
8
# ./src/CMakeLists.txt

INCLUDE_DIRECTORIES(/home/durant35/Workspace/cmake_tb/ex_03/install/include)
LINK_DIRECTORIES(/home/durant35/Workspace/cmake_tb/ex_03/install/lib)

ADD_EXECUTABLE(main main.c)
#TARGET_LINK_LIBRARIES(main libhello.so)
TARGET_LINK_LIBRARIES(main libhello.a)

  INCLUDE_DIRECTORIES,为整个工程添加 include 路径,主要是 .h 头文件路径;LINK_DIRECTORIES 添加库链接路径;TARGET_LINK_LIBRARIES 确定目标文件链接库信息。具体的语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来
# 默认的行为是追加到当前的头文件搜索路径的后面(AFTER)
INCLUDE_DIRECTORIES([AFTER|BEFORE][SYSTEM] dir1 dir2...)

# 添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可执行二进制,在编译时就需要指定一下这些共享库的路径
LINK_DIRECTORIES(directory1 directory2...)

# 这个指令可以用来为 target 添加需要链接的共享库,可以是一个可执行文件,或者一个共享库
TARGET_LINK_LIBRARIES(target library1
<debug | optimized> library2
...
)

  在上面的例子中,假如没有使用 INCLUDE_DIRECTORIES 指令,会找不到 hello.h 头文件:

  假如没有使用 LINK_DIRECTORIES 指令,则会找不到 HelloFunc() 函数的具体实现:

  TARGET_LINK_LIBRARIES 可以链接动态库,如下。ldd 表明生成的目标可执行文件依赖于我们上一个例子安装的动态库。

  TARGET_LINK_LIBRARIES 也可以链接静态库,如下。HelloFunc() 函数以静态库的形式提供给目标可执行文件,不需要像动态库一样动态加载,已经与源文件一同生成目标可执行文件。

CMake,变量

引用:
  前面我们已经提到了,使用 ${variable_name} 进行变量的引用。在 IF 等语句中,是直接使用变量名而不通过 ${variable_name} 取值。

自定义变量方式:
  主要有隐式定义和显式定义两种。
  使用 SET(variable_name value) 指令,显式定义变量。
  PROJECT 指令,会隐式的定义 _BINARY_DIR_SOURCE_DIR 两个变量。

常用变量:
  CMAKE_BINARY_DIR/PROJECT_BINARY_DIR /_BINARY_DIR
   这三个变量指代的内容是一致的,如果 in-source build,指的是工程顶层目录;如果是 out-of-source build,指的是工程编译发生的目录。

  CMAKE_SOURCE_DIR/PROJECT_SOURCE_DIR /_SOURCE_DIR
   这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。

  CMAKE_CURRENT_SOURCE_DIR
   指的是当前处理的 CMakeLists.txt 所在的路径,比如上面例子中的 src 源代码子目录。

  CMAKE_CURRRENT_BINARY_DIR
   如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致;
   如果是 out-of-source 编译,他指的是 target 编译目录。
   使用我们上面提到的 ADD_SUBDIRECTORY(src bin) 可以更改这个变量的值。
   使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>) 并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。

  EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH
   分别用来重新定义最终结果的存放目录。

调用环境变量:
  使用 $ENV{variable_name} 指令就可以调用系统的环境变量了。

系统信息变量:
  UNIX
   在所有的类 UNIX 平台为 true,包括 OS Xcygwin
  WIN32
   在所有的 win32 平台为 true,包括 cygwin

1
2
3
4
5
6
7
8
# 判断平台差异,控制在不同的平台进行不同的控制
IF(WIN32)
MESSAGE(STATUS “Thisiswindows.”)
#作一些 Windows 相关的操作
ELSE(WIN32)
MESSAGE(STATUS “Thisisnotwindows”)
#作一些非 Windows 相关的操作
ENDIF(WIN32)

CMake,指令

  ADD_EXECUTABLEADD_LIBRARYADD_SUBDIRECTORY

  ADD_TESTENABLE_TESTING 指令

  INSTALL 指令

  IF 指令
   ELSE ELSEIF ENDIF
   NOT AND OR

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
# 一般用法,有点别扭
IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)

# 阅读起来舒服一点的
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTSON)
IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE()
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF()

  WHILE 指令的语法是:

1
2
3
4
5
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)

  FOREACH 指令

============
参考资料: 《CMake Practice》

文章目录
  1. 1. CMake 简介
  2. 2. 简单的 HelloWorld
  3. 3. 更好一点的 HelloWorld
  4. 4. CMake,库的创建、安装&使用
  5. 5. CMake,变量
  6. 6. CMake,指令