chapter01 - basic

三个 stages:配置、生成、构建。

配置会生成 CMakeCache。生成是用它去生成 build tree 的其他内容。

Generating a Build System

生成 build tree

cmake -B build/ -S source/

最好不要使用无参数的 cmake。

选择生成器

cmake -G <generator>

也可以使用环境变量 CMAKE_GENERATOR。但是要注意大小写:

CMAKE_GENERATOR=Ninja cmake -B build

对一个已经指定了生成器的 build tree 设置新的 CMAKE_GENERATOR 并重新配置是无效的,这个时候环境变量会背忽略。而使用 -G 指定则提示有冲突。

指定 cache

cmake -C <dir-of-CMakeCache.txt>

cmake 会先读 cache 再去进行接下来的配置。

cmake -L[A][H] <path-to-source>:在配置的时候还会列举出配置。

(py310) xxx /data/modern-cmake/examples/chapter01/01-hello $ cmake -B build -L
-- Configuring done
-- Generating done
-- Build files have been written to: /data/modern-cmake/examples/chapter01/01-hello/build
-- Cache values
CMAKE_BUILD_TYPE:STRING=
CMAKE_INSTALL_PREFIX:PATH=/usr/local

可以看到当前有两个选项可以通过 cache 或者命令行的 -D 选项更改。

-A 使得被标记为 ADVANCED 的变量也被列出。

-D 用来定义变量,-U 用来移除变量。

cmake --system-information 用来打印系统信息。

--log-level 指定日志等级。

--log-context 可以提供更多信息,比如调用栈。

CMakePresets.json

一个用来提供预设配置的文件。命令行 优先于 预设配置 优先于 环境变量 优先于 默认值

相关命令:

cmake --list-presets
cmake --preset=<preset>

Building

cmake --build <dir> -- <build-tool-options>

用 make 的时候加 -j 会好一点,而用 ninja 不要加。ninja 自动选择的并行数似乎是比当前的处理器数量略大一些的,但是我也不清楚是多少。CMAKE_BUILD_PARALLEL_LEVEL 环境变量也能设置并发数。

在构建的时候 -t/--target 都能用于指定目标。多个目标就重复 -t A -t B。特殊目标有:

  • clean 用于清理项目

先清理再构建:

cmake --build build/ --clean-first

多配置

有的生成器支持多配置:Ninja Multi-Config, Xcode, Visual Studio。

这样就不必在配置阶段通过 -DCMAKE_BUILD_TYPE=Release 指定好配置,而是在编译阶段通过 --config 传入配置(默认还是 Debug)。

export CMAKE_GENERATOR="Ninja Multi-Config"
cmake -B build
cmake --build build/ --config Debug
cmake --build build/ --config Release
# 再编译一次 Debug,提示:ninja: no work to do. 说明之前的编译结果得以保留
cmake --build build/ --config Debug

对于 Ninja Multi-Config:

如果在 add_executable 之前有 set(CMAKE_EXPORT_COMPILE_COMMANDS on),那么导出的 compile_commands.json 会同时包含多个配置下的文件生成方式。看起来可能有点问题,但实际上基本不影响正常工作。

https://gitlab.kitware.com/cmake/cmake/-/issues/23733

上面有人合并了新代码尝试解决这个问题,但是过去了一年现在最新的 3.28.1 仍然是生成了多份配置。

虽然 CMake 一共有 4 种配置,但是 Ninja Multi-Config 只能生成 Debug/Release/RelWithDebInfo。

Installing

略。

Scripting

可以创建独立脚本。

cmake [{-D <var>=<value>}...] -P <cmake-script-file> [-- <options>...]

参数被放在 CMAKE_ARGV1, CMAKE_ARGV2, … 里。需要自己用 CMAKE_ARGC 作字符串插值。而且不只是 -- 之后的参数,连 cmake 这条语句本身的所有参数都会占用位置,比如 cmake, -P, some.cmake 也都会分别占据 CMAKE_ARGV0, CMAKE_ARGV1, CMAKE_ARGV2

When running scripts, CMake won’t execute any of the usual stages (such as configurationor generation), and it won’t use the cache.

由于没有 build tree 和 source tree 的概念,相关的变量值会变成当前的工作路径

即便是脚本也应该有 cmake_minimum_required(VERSION 3.20.0) 这样类似的版本说明。

CMake command-line tool mode

cmake -E 可以运行 cmake 内置的命令,这些命令跨平台。支持的命令有 copy、cat、chdir 之类的。可以用 cmake -E capabilities 查看。

Navigating the project files

  • source tree
  • build tree/build root/binary tree
  • listfiles – 推荐命名 CMakeLists.txt 或 xxx.cmake

CMakeCache.txt 中有:

########################
# EXTERNAL cache entries
########################

这一段是可以由用户修改的,不会被重新配置清理。实测 internal cache 也可以被修改,但是不会保证正确性。

配置文件

<PackageName>-config.cmake 或者 <PackageName>Config.cmake

配置文件可以暴露一些 cmake 宏以方便用户使用。

用 find_package 去找包。

<Config>Version.cmake 用来表明当前包的版本。

如果一个包的提供者没有给出 Config-file,则不能用 find_package,得用 Find-modules 来代替。Find-modules 会去找相关的系统路径,从而找出对编译有帮助的变量。举例:FindCURL 就是 Find-module。

模块

CMake 自带一些模块,可以用 include 来包含,比如 include(TestBigEndian)

cmake 其实自带大小端相关的变量。