1. 概述

CMake 工具能够自动生成 Makefile 文件,减轻手写 Makefile 文件的工作量,同时减少书写 Makefile 文件产生的错误。

2. CMake 命令

CMake 运行主要分为两个阶段:

  • 配置阶段:解析 CMakeLists.txt 文件

  • 生成阶段:生成构建环境

有关 cmake 的详细参数参见 cmake --help,本文仅对其中较难理解的选项加以描述。

2.1 缓存选项

CMake 支持缓存选项。在 CMake 中,如果一个变量被标记为「缓存」,则 cmake 的时候会将其写入到 CMakeCache.txt 文件中。当 cmake 命令寻找变量时,它会首先去 CMakeCache.txt 文件中寻找。cmake 创建缓存选项的格式如下:

1
cmake -D <var>[:<type>]=<value>     # <var>[:<type>]=<value> 具体参见下文「CMakeCache.txt 编写」

2.2 常用选项

  • -DCMAKE_BUILD_TYPE= :指定编译软件的版本格式,取值为 ReleaseRelWithDebInfoDebug 等。

  • -DCMAKE_INSTALL_PREFIX= :指定需要安装的软件路径,默认为安装路径为 /usr/local(默认系统安装)。

仅用户安装一般指定安装路径为 ~/.local,当然也可以随用户自定义。

  • -DBUILD_SHARED_LIBS=DBUILD_SHARED_LIBS 是一个全局的 flag,为 bool 类型,取值为 ONOFF(默认为 ON)。其作用是:

如果 DBUILD_SHARED_LIBS 设定为 ON,则 CMakeLists.txt 中所有的 add_library() 创建的库都默认为共享库而不是静态库,除非 add_library() 中有显式地指定编译为静态库。反之则为静态库。

  • -DBUILD_TESTING=DBUILD_TESTING 是一个全局的 flag,为 bool 类型,取值为 ONOFF(默认为 ON)。其作用是:

当使用 CTest 模块时,DBUILD_TESTING 用来控制是否使能 testing。

  • --trace :用于详细输出 cmake 的每一行信息。

--trace-source=<file> :将 cmake 输出的所有信息都保存在 file 文件中。

3. CMakeLists.txt 编写

3.1 设定编译器

在运行 CMake 前首先需要指定 CC、CXX 编译器,否则 CMake 将使用系统默认的 CC、CXX 编译器。可以在 bash shell 中临时设定:

1
2
CC=/usr/bin/gcc
CXX=/usr/bin/g++

也可以在 CMakeLists.txt 文件中设定:

1
2
set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")

3.2 添加 CMake 最小要求版本

1
cmake_minimum_required(VERSION 3.1)

3.3 添加项目信息

1
2
3
project(MyProject   VERSION 1.0         # 项目版本
DESCRIPTION "xxx" # 项目描述
LANGUAGES CXX) # 项目语言

CMake 支持的语言有:C , CXX , Fortran , ASM , CUDA (CMake 3.8+), CSharp (3.8+), and SWIFT (CMake 3.15+experimental)。默认为 CCXX

3.4 生成文件

  • 生成目标文件
1
add_executable(one two.cpp three.h)

one 既是生成的可执行文件名称,也是 CMake 创建的目标文件的名称。one 后面紧跟着项目的源文件,比如这里示例的 two.cpp three.h 都是源文件。

  • 生成库文件
1
add_library(onelib STATIC twolib.cpp threelib.h)

可选的库文件类型有:STATIC、SHARED、MODULE

3.5 为目标文件添加内容

【注】可以为目标文件添加这些内容:

include directories, linked libraries (or linked targets), compile options, compile definitions, compile features, and more.

  • 为目标文件添加 include 路径
1
target_include_directories(one PUBLIC include)

PUBLIC/PRIVATE/INTERFACE 用来指定「是/否/仅需要时」影响其他依赖于当前目标文件 one 的目标文件。

  • 为目标文件添加链接库
1
target_link_libraries(one -lcrypt -lcap)

one 是 CMake 创建的目标文件的名称。

3.6 设定变量、缓存项、属性

【注】访问一个名为 VARIABLE 的局部变量使用 ${VARIABLE},访问一个名为 VARIABLE 的环境变量使用 $ENV{VARIABLE}

  • 设定局部变量
1
set(MY_VARIABLE "value")

局部变量的值还可以是列表:

1
2
set(MY_VARIABLE "one" "two")
set(MY_VARIABLE "one;two")

上述两条语句等价,因为变量的列表值在内部存储时就是使用 “;” 分隔的。可以添加 PARENT_SCOPE 关键字指定将变量的作用域往外跳一级。

  • 设定缓存变量
1
2
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description")       # 不会覆盖已存在的变量
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description" FORCE) # 会覆盖已存在的变量
  • 修改命令行参数变量
1
option(MY_OPTION "This is settable from the command line" OFF)  # 此处假定 MY_OPTION 为 bool 值
  • 设定环境变量
1
set(ENV{variable_name} value)
  • 设定属性 & 访问属性
    属性可以看作是依附于某一项(比如目录、目标文件等)的全局变量。
1
2
3
set_property(TARGET TargetName[TargetName1...] PROPERTY CXX_STANDARD 11)                # 可以为多个 targets/files/tests 设置属性
set_target_properties(TargetName[TargetName1...] PROPERTIES CXX_STANDARD 11) # 仅用于为 targets 设置属性
get_property(ResultVariable TARGET TargetName[TargetName1...] PROPERTY CXX_STANDARD) # 获取 targets 的属性值并保存在 ResultVariable 中

3.7 if 语句块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

## 第一种格式
if(variable)
# If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number
else()
# If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND`
endif()
# If variable does not expand to one of the above, CMake will expand it then try again

## 第二种格式
if("${variable}")
# True if variable is not false-like
else()
# Note that undefined variables would be `""` thus false
endif()

if 语句块中可以包含的关键字有:

  • 一元:NOT、TARGET、EXISTS(file)、DEFINED 等

  • 二元:STREQUAL、AND、OR、MATCHES(regular expression)、VERSION_LESS、VERSION_LESS_EQUAL 等

  • 括号:()

3.8 生成表达式

  • $<KEYWORD> :计算 KEYWORD 的值

  • $<KEYWORD:value> :根据 KEYWORD 的值来控制整个表达式的值,KEYWORD = 1 时表达式值为 value,KEYWORD = 0 时表达式值为空字符串。

常用的生成表达式有 BUILD_INTERFACEINSTALL_INTERFACE 生成表达式。

1
2
3
4
5
6
target_include_directories(
MyTarget
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

BUILD_INTERFACE 表达式包装的构建需求只被在同一个构建系统下,或者使用 export() 指令导出的目标上使用。INSTALL_INTERFACE 表达式包装的构建需求只被用在使用 install(EXPORT) 指令安装和导出的目标上:

3.9 函数和宏

函数和宏的唯一区别在于作用域,函数有作用域但宏没有。

1
2
3
4
5
6
7
8
9
10
11
12

## 函数举例
function(SIMPLE REQUIRED_ARG)
message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()

## 宏举例
macro(SIMPLE REQUIRED_ARG)
message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endmacro()

CMake 中有一个参数接收模块:cmake_parse_arguments(),用来定义函数/宏需要接收的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

## 定义函数
function(COMPLEX)
cmake_parse_arguments(
COMPLEX_PREFIX
"SINGLE;ANOTHER"
"ONE_VALUE;ALSO_ONE_VALUE"
"MULTI_VALUES"
${ARGN}
)
……
endfunction()

## 调用函数
complex(SINGLE ONE_VALUE value MULTI_VALUES some other values)

上文调用函数后可以发现:

1
2
3
4
5
COMPLEX_PREFIX_SINGLE = TRUE
COMPLEX_PREFIX_ANOTHER = FALSE
COMPLEX_PREFIX_ONE_VALUE = "value"
COMPLEX_PREFIX_ALSO_ONE_VALUE = <UNDEFINED>
COMPLEX_PREFIX_MULTI_VALUES = "some;other;values"

如果函数/宏调用时传入了 cmake_parse_arguments() 未指定的其他参数,则其内容均保存在 COMPLEX_PREFIX_UNPARSED_ARGUMENTS 变量中。

4. CMakeCache.txt 编写

CMakeCache.txt 文件本是由 cmake 命令默认生成的,但其内容是可以修改的。CMakeCache.txt 文件主要保存的是 cmake 过程中需要使用的环境变量,当 cmake 命令寻找变量时,它会首先去 CMakeCache.txt 文件中寻找。

4.1 内容条目

随便看一个 CMakeCache.txt 文件就会发现,其文首给出了该文件的作用和内容条目格式:

1
2
3
4
5
6
7
8
9
10
11
# This is the CMakeCache file.
# For build in directory: xxx/build
# It was generated by CMake: yyy/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.

简单来说,CMakeCache.txt 文件就是一条条环境变量的键(包含类型)值对条目组成的,而条目的具体格式如下:

1
KEY:TYPE=VALUE

5. 注释

在可编写的 CMake 文件(CMakeLists、CMakeCache.txt等)中,其注释规则都是一样的。

5.1 单行注释

1
# 要注释的单行内容

5.2 多行注释

1
2
3
#[[
要注释的多行内容
]]