CMake is not a build system, c make is a “cross-platform C++ build generator”。

基础概念

CMake中,有以下基础概念:

  • command: CMakeLists.txt中,我们写的每一行代码都是command
  • target: CMake中,target是构建的目标,通常是一个要通过构建产生的可执行文件或者库文件,也可能是包含一组command的抽象target。

基础流程

声明Minimun Version

CMakeLists.txt的第一行必须使用 commdn: cmake_minimum_required 声明所支持的CMake版本号:
cmake_minimum_required(VERSION, 3.1)
上面的VERSION,是 cmake_minimum_required 这个command的关键字,这个关键字后面定义的version决定了执行构建所需的CMake最低版本,以及在使用更高的版本时,CMake应当遵循的policy(相当于隐含地调用了一次 cmake_policy command ,随着版本迭代,CMake会有一些行为变更,但如果我们声明的版本是3.1,不论使用的CMake版本是什么,CMake都会遵循3.1版本规定的行为。)

自3.12起,CMake支持声明一个版本范围:
cmake_minumum_required(VERSION, 3.7…3.22)
低于3.12的CMake版本会将上面的版本识别为 3.7,忽略 … 部分。

声明项目基础信息

每个CMake文件都会使用 command: project 定义项目的基础信息:
project(MyProject
VERSION 1.0
DESCRIPTION ‘Very nice project’
LANGUAGES CXX)
注意上面的语法:字符串必须要使用引号括起来,分隔符可以使用使用任意多个空格或换行,command的第一个参数必须是项目名称,其他参数都是可选的,并且顺序不重要,只要跟在command keyword后面就可以。

定义可执行文件target

使用 command: add_executable 来定义一个可执行文件target:
add_executable(one two.cpp three.h)
上面的coomand定义了一个target:one,one既是target名称,可以在后面的CMake脚本中使用,也是要构建的可执行文件的文件名。

定义库target

https://cmake.org/cmake/help/latest/command/add_library.html
使用 command: add_library 定义一个库文件target:
add_library(one STATIC two.cpp three.h)
这个command的第一个参数是库的target和文件名称,第二个参数是可选的,用来指定构建的库类型,后面是用于构建库的源文件。
下面还会介绍一种特殊的抽象target,这种target不构建任何文件,常用于定义header-only库,这种库在CMake中叫做INTERFACE库。

配置target

通过上面的 add_executable 或者 add_library 定义了target后,还需要配置target,配置项包括:

  • 指定需要的头文件: target_include_directories
    • target_include_directories(one PUBLIC includedir)
      • 对于库文件target,PUBLIC表示依赖了这个target的其他target,也需要使用这里指定的头文件,第二个参数还可以指定为PRIVATE或者INTERFACE
  • 指定要链接的库: target_link_libraries
    • target_link_directories(another PUBLIC one)
      • target_link_directories 这个command比较容易让人混淆的一点是:one如果是一个target,则CMake会将这个target链接到another,否则CMake会尝试找到系统中的名为one的库文件,将这个库文件链接到another
  • 指定编译时要定义的预处理器符号:target_compile_definations
  • 指定编译选项:target_compile_options

Variables & Cache

Variables

Local Variable

使用 set 定义变量:
set(MY_VARIABLE “value”)
变量名称通常是全大写的。使用 ${} 可以访问变量值,例如 ${MY_VARIABLE} 。
变量存在scope的概念,CMake中,会产生scope的包括函数和子目录,一个scope中定义的变量无法被上层scope访问,如果需要将变量导出到上次scope,需要在使用 set 定义变量时使用keyword: PARENT_SCOPE。

CMake中有list的概念,list就是用分号分隔的字符串,当用 set 定义变量时,使用多个值会导致变量被定义为list形式的,也就是多个值中间会被加上分号,合并成一个字符串。

CMake中,字符串字面量的引号是可选的,如果字符串内没有空格,就可以省略引号。

在展开一个字符串变量时,CMake并不会为展开结果两边加上引号,因此为了保险起见,最好总是在展开字符串变量时,写上引号。

Cache Variable

https://cmake.org/cmake/help/latest/command/set.html#set-cache-entry
Cache variable,缓存变量,指的是允许用户在调用cmake时设置的变量。

Environment Variable

https://cmake.org/cmake/help/latest/command/set.html#set-environment-variable
Environment Variable,环境变量,CMake可以设置当前CMake进程的环境变量。一般来说不应该使用它们。

CMake预定义Variable

https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html

Cache

CMakeCache.txt文件,是CMake产生的Cache,执行CMake时,这个文件会在build目录下被生成。CMake会在这个文件中记录用户的选择,这样下次用户执行CMake时就不用重新选择一遍了。

Properties

Properties,属性,除了变量,CMake也可以将信息保存在属性里。
属性和变量类似,但它是和其他东西关联的,例如target或者directory。不过,也可以定义Global属性,这种属性就好像一个没有scope限制的变量。CMake中,不少内置的属性都是从加了 CMAKE_ 前缀的同名变量创建的,例如,通过设置CMAKE_CXX_STANDART变量,就可以让所有之后定义的target关联上值相同的CXX_STANDART属性。

通过 set_property 定义属性。为了方便,CMake也提供了 set_target_properties 用于专门定义target属性:
https://cmake.org/cmake/help/latest/command/set_property.html#command:set_property
https://cmake.org/cmake/help/latest/command/set_target_properties.html?highlight=set_target_property

通过 get_property 访问属性:
https://cmake.org/cmake/help/latest/command/get_property.html#command:get_property

CMake预定义Property

https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html

CMake脚本编程

CMake支持使用 if 控制代码的执行,支持通过 generator-expressions 控制构建和安装时的行为,也支持定义Macro或者Function。
https://cliutils.gitlab.io/modern-cmake/chapters/basics/functions.html

在代码中访问CMake变量

通过使用command: configure_file,CMake可以将变量值输出到一个CMake生成的文件中:
configure_file (
“${PROJECT_SOURCE_DIR}/include/My/Version.h.in”
“${PROJECT_BINARY_DIR}/include/My/Version.h”
)

  1. // Version.h.in
  2. #pragma once
  3. #define MY_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
  4. #define MY_VERSION_MINOR @PROJECT_VERSION_MINOR@
  5. #define MY_VERSION_PATCH @PROJECT_VERSION_PATCH@
  6. #define MY_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
  7. #define MY_VERSION "@PROJECT_VERSION@"

CMake会查找输入配置文件中的 @VAR@ 以及 ${VAR} ,将它们替换为CMake同名变量值,然后生成输出的文件。
在使用 configure_file 时,一般需要搭配:include_directories(${CMAKE_CURRENT_BINARY_DIR}),将生成的代码添加到include目录中。

此外,在使用这种方式生成C头文件时,还可以使用两种特殊的输入格式:#cmakedefine 和 #cmakedefine01,用来生成合适的C预处理器代码:
https://cmake.org/cmake/help/latest/command/configure_file.html?highlight=configure_file

在CMake中读取文件

参考下面的 Reading Files 部分:
https://cliutils.gitlab.io/modern-cmake/chapters/basics/comms.html