CMake介绍和简要说明

编译系统的对比和说明1

  • 手写MakeFile: 适合于小型项目编译。当项目中模块众多并且之间依赖关系复杂时,维护Makefile非常困难。

  • Modern CMake: 开源社区非常流行的build system generator,通过执行CMakeLists生成makefile/ninja等,构建C++项目。尤其是2013年以来Modern CMake的发展,理念是:一切皆为target or target property。Modern CMake通过target之间的依赖关系,实现target属性(头文件查找路径、编译选项、链接依赖)内部隐藏或者自动传递,可以非常方便地处理大型C++项目的复杂依赖关系。

    相比于makefile,Modern CMake大大解放了C/C++研发人员的生产力。

  • Bazel (and Blade): Bazel是Google开源的编译构建工具,以Monolithic Repository为理念。与makefile & CMake不同,Bazel另起炉灶,采用client/server运行模式,为云编译而生。Bazel工具将编译过程分三个阶段:Load Phase/Analysis Phase/Execution phase。研发人员实现workspace/build/.bzl三种文件,Bazel执行这些文件生成action graph,执行action来构建项目。

2022 CMake版本选择2

What minimum to choose - OS support:

  • 3.4: The bare minimum. Never set less.
  • 3.7: Debian old-stable.
  • 3.10: Ubuntu 18.04.
  • 3.11: CentOS 8 (use EPEL or AppSteams, though)
  • 3.13: Debian stable.
  • 3.16: Ubuntu 20.04.
  • 3.19: First to support Apple Silicon.
  • latest: pip/conda-forge/homebew/chocolaty, etc.

What minimum to choose - Features:

  • 3.8: C++ meta features, CUDA, lots more
  • 3.11: IMPORTED INTERFACE setting, faster, FetchContent, COMPILE_LANGUAGE in IDEs
  • 3.12: C++20, cmake --build build -j N, SHELL:, FindPython
  • 3.14/3.15: CLI, FindPython updates
  • 3.16: Unity builds / precompiled headers, CUDA meta features
  • 3.17/3.18: Lots more CUDA, metaprogramming

CMake文件编写基础

Minimum Version

1
cmake_minimum_required(VERSION 3.1)

这条命令几乎是所有CMakeLists.txt的第一行命令,它设置项目需要的最小 cmake 版本,因为较新的版本会有老版本没有的命令,如果 cmake 版本号小于该命令指定的版本,cmake 会报错。另外在后面的视频里会提到版本 2.8.12 之后的 cmake 为 modern cmake,更加注重 modular design,因此项目使用的版本不应低于 2.8.12。

Tips: 在CMake 3.12版本以后,这个命令支持VERSION 3.1...3.15这样的表达,相关细节内容可以参考3

Setting a Project

1
2
3
project(MyProject VERSION 1.0
DESCRIPTION "Very nice project"
LANGUAGES CXX)

在这里project名称必须是第一个变量。后面的VERSION, DESCRIPTION, LANGUAGES等等属性都是可选的选项4

Making an executable

1
add_executable(one two.cpp three.h)

one 既是生成的可执行文件的名称,也是创建的 CMake 目标(target)的名称。

紧接着的是源文件的列表,你想列多少个都可以。CMake 很聪明 ,它根据拓展名只编译源文件。在大多数情况下,头文件将会被忽略;列出他们的唯一原因是为了让他们在 IDE 中被展示出来,目标文件在许多 IDE 中被显示为文件夹。

Making a Library

1
add_library(one STATIC two.cpp three.h)

通过add_library 命令你可以制作一个库,同时你可以选择库的类型,可以是 STATIC,SHARED, 或者MODULE.如果你不选择它,CMake 将会通过 BUILD_SHARED_LIBS 的值来选择构建 STATIC 还是 SHARED 类型的库。

在下面的章节中你将会看到,你经常需要生成一个虚构的目标,也就是说,一个不需要编译的目标。例如,只有一个头文件的库。这被叫做 INTERFACE 库,这是另一种选择,和上面唯一的区别是后面不能有文件名。

Definition of Targets

1
target_include_directories(one PUBLIC include)

target_include_directories 为目标添加了一个目录。 PUBLIC 对于一个二进制目标没有什么含义;但对于库来说,它让 CMake 知道,任何链接到这个目标的目标也必须包含这个目录。其他选项还有 PRIVATE(只影响当前目标,不影响依赖),以及 INTERFACE(只影响依赖)。

我们可以使用下面的命令,将another库和上面的one库链接起来:

1
2
add_library(another STATIC another.cpp another.h)
target_link_libraries(another PUBLIC one)

target_link_libraries 可能是 CMake 中最有用也最令人迷惑的命令。

  • 如果one已经被定义过,那么target_link_libraries这个命令将会把one的依赖添加到another上。
  • 如果one没有被定义过,那么target_link_libraries这个命令将会从你的path中找到one这个library。
  • 或者你可以在这里给定one的绝对路径。

虽然经典的CMake允许缺省关键字PUBLIC, 但是缺省与非缺省混用,CMake将会报错。所以推荐全局使用targets和关键字。

综合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# cmake版本要求定义
cmake_minimum_required(VERSION 3.8)

# 定义项目为Calculator
project(Calculator LANGUAGES CXX)

# 添加calclib库,这里编译为静态链接库
add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)
target_include_directories(calclib PUBLIC include) # 申明include文件夹为calclib库的PUBLIC目录
target_compile_features(calclib PUBLIC cxx_std_11) # 申明calclib的编译标准为cxx11

# 添加可执行程序
add_executable(calc apps/calc.cpp)
target_link_libraries(calc PUBLIC calclib) # 链接calc和calclib

如何调试CMakeLists文件

如果只是需要打印单一的变量,那么可以参考如下两种方法

  1. 使用message命令

    1
    message(STATUS "MY_VARIABLE=${MY_VARIABLE}")
  2. 使用CMake内置的模组CMakePrintHelpers

    1
    2
    include(CMakePrintHelpers)
    cmake_print_variables(MY_VARIABLE)

如果需要打印某些目标的变量,那么使用CMakePrintHelpers的方法就是比较方便的选择了

1
2
cmake_print_properties(TARGETS foo bar PROPERTIES
LOCATION INTERFACE_INCLUDE_DIRS)

上面的命令将会打印foo, barLOCATIONINTERFACE_INCLUDE_DIRS属性。

CMake学习参考资料

相关的学习网站

相关的学习视频


  1. 大型C++工程的构建技术:Modern CMake vs Bazel↩︎

  2. Do's and Don'ts · Modern CMake↩︎

  3. Introduction to the Basics↩︎

  4. Introduction to the Basics↩︎

  5. CMake 基本用法介绍↩︎