1. 概述
CMake 工具能够自动生成 Makefile 文件,减轻手写 Makefile 文件的工作量,同时减少书写 Makefile 文件产生的错误。
2. CMake 命令
CMake 运行主要分为两个阶段:
配置阶段:解析 CMakeLists.txt 文件
生成阶段:生成构建环境
有关 cmake
的详细参数参见 cmake --help
,本文仅对其中较难理解的选项加以描述。
2.1 缓存选项
CMake 支持缓存选项。在 CMake 中,如果一个变量被标记为「缓存」,则 cmake
的时候会将其写入到 CMakeCache.txt 文件中。当 cmake
命令寻找变量时,它会首先去 CMakeCache.txt 文件中寻找。cmake
创建缓存选项的格式如下:
cmake -D <var>[:<type>]=<value> # <var>[:<type>]=<value> 具体参见下文「CMakeCache.txt 编写」
2.2 常用选项
-DCMAKE_BUILD_TYPE=
:指定编译软件的版本格式,取值为Release
、RelWithDebInfo
、Debug
等。-DCMAKE_INSTALL_PREFIX=
:指定需要安装的软件路径,默认为安装路径为/usr/local
(默认系统安装)。
仅用户安装一般指定安装路径为
~/.local
,当然也可以随用户自定义。
-
-DBUILD_SHARED_LIBS=
:DBUILD_SHARED_LIBS
是一个全局的 flag,为bool
类型,取值为ON
或OFF
(默认为ON
)。其作用是:
如果
DBUILD_SHARED_LIBS
设定为ON
,则 CMakeLists.txt 中所有的add_library()
创建的库都默认为共享库而不是静态库,除非add_library()
中有显式地指定编译为静态库。反之则为静态库。
-
-DBUILD_TESTING=
:DBUILD_TESTING
是一个全局的 flag,为bool
类型,取值为ON
或OFF
(默认为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 中临时设定:
CC=/usr/bin/gcc
CXX=/usr/bin/g++
也可以在 CMakeLists.txt 文件中设定:
set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")
3.2 添加 CMake 最小要求版本
cmake_minimum_required(VERSION 3.1)
3.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)
。默认为 C
和 CXX
。
3.4 生成文件
- 生成目标文件
add_executable(one two.cpp three.h)
one 既是生成的可执行文件名称,也是 CMake 创建的目标文件的名称。one 后面紧跟着项目的源文件,比如这里示例的 two.cpp three.h 都是源文件。
- 生成库文件
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 路径
target_include_directories(one PUBLIC include)
PUBLIC/PRIVATE/INTERFACE
用来指定「是/否/仅需要时」影响其他依赖于当前目标文件 one 的目标文件。
- 为目标文件添加链接库
target_link_libraries(one -lcrypt -lcap)
one 是 CMake 创建的目标文件的名称。
3.6 设定变量、缓存项、属性
【注】访问一个名为 VARIABLE
的局部变量使用 ${VARIABLE}
,访问一个名为 VARIABLE
的环境变量使用 $ENV{VARIABLE}
。
- 设定局部变量
set(MY_VARIABLE "value")
局部变量的值还可以是列表:
set(MY_VARIABLE "one" "two")
set(MY_VARIABLE "one;two")
上述两条语句等价,因为变量的列表值在内部存储时就是使用 ";" 分隔的。可以添加 PARENT_SCOPE
关键字指定将变量的作用域往外跳一级。
- 设定缓存变量
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description") # 不会覆盖已存在的变量
set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description" FORCE) # 会覆盖已存在的变量
- 修改命令行参数变量
option(MY_OPTION "This is settable from the command line" OFF) # 此处假定 MY_OPTION 为 bool 值
- 设定环境变量
set(ENV{variable_name} value)
- 设定属性 & 访问属性
属性可以看作是依附于某一项(比如目录、目标文件等)的全局变量。
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 语句块
## 第一种格式
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_INTERFACE
和 INSTALL_INTERFACE
生成表达式。
target_include_directories(
MyTarget
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
BUILD_INTERFACE 表达式包装的构建需求只被在同一个构建系统下,或者使用 export() 指令导出的目标上使用。INSTALL_INTERFACE 表达式包装的构建需求只被用在使用 install(EXPORT) 指令安装和导出的目标上:
3.9 函数和宏
函数和宏的唯一区别在于作用域,函数有作用域但宏没有。
## 函数举例
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(),用来定义函数/宏需要接收的参数。
## 定义函数
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)
上文调用函数后可以发现:
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 文件就会发现,其文首给出了该文件的作用和内容条目格式:
# 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 文件就是一条条环境变量的键(包含类型)值对条目组成的,而条目的具体格式如下:
KEY:TYPE=VALUE
5. 注释
在可编写的 CMake 文件(CMakeLists、CMakeCache.txt等)中,其注释规则都是一样的。
5.1 单行注释
# 要注释的单行内容
5.2 多行注释
#[[
要注释的多行内容
]]