1. CMake

CMake 是一个跨平台的构建系统生成工具。它不直接编译代码,而是根据开发者编写的 CMakeLists.txt 配置文件,生成对应平台的构建文件(如 Unix 下的 Makefile、Windows 下的 Visual Studio 解决方案、Ninja 构建文件等),再由这些构建文件驱动编译器完成编译。

简单来说,CMake 解决的是"同一个项目如何在不同的平台和编译器上编译"的问题。


1.1 CMakeLists.txt 文件

CMakeLists.txt 是 CMake 的配置文件,所有构建规则都写在它里面。CMake 对它有严格的命名和位置要求:

1. 文件名严格区分大小写,必须精确地命名为 CMakeLists.txt,不能写成 cmakelists.txt、Cmakelists.txt 或其他变体。这是因为 CMake 在搜索配置文件时只会匹配这个准确的文件名。

2. 文件必须放在项目的根目录下(也可以放在子目录中以构建多级项目,但是也属于子项目的根目录)。

3. 每一个包含 CMakeLists.txt 的目录都是一个独立的构建单元,CMake 会从根目录开始逐层处理。

# 这是一个 CMakeLists.txt 文件中的注释,使用 # 开头

1.2 一个基础的 CMakeLists.txt 文件示例

下面是一个最简单的 CMake 配置,它定义一个名为 test 的项目,并将 main.cpp 编译为可执行文件 exename:

cmake_minimum_required(VERSION 3.1.0)   # 指定 CMake 最低版本要求
project(test)                           # 定义项目名称
add_executable(exename main.cpp)        # 将 main.cpp 编译为可执行文件 exename

三条命令各自的作用:

命令 含义
cmake_minimum_required(VERSION 版本号) 声明构建此项目所需的 CMake 最低版本,低于该版本会报错并中止配置
project(项目名) 设置项目名称,同时会隐式创建 <项目名>_SOURCE_DIR 等变量
add_executable(可执行文件名 源文件1 源文件2 ...) 定义一个可执行文件的构建目标,列出参与编译的所有源文件

2. CMake 基本命令

CMake 的典型工作流程分为两步:配置阶段和构建阶段。

假设项目结构如下:

D:\test\
    ├── CMakeLists.txt
    └── main.cpp

在终端中执行以下命令:

# 第一步:进入项目目录
cd D:\test

# 第二步:配置——让 CMake 读取 CMakeLists.txt 并生成构建文件到 build 目录
cmake -B build .

# 第三步:构建——调用底层构建工具(如 make、MSBuild)完成编译
cmake --build build

# 第四步:运行生成的可执行文件
build\exename.exe

理解要点:cmake -B build . 是"翻译"阶段——把 CMakeLists.txt 翻译成 Makefile 或 .sln;cmake --build build 是"编译"阶段——调用真正的编译器去编译源码。两步分离的设计使得同一个 CMakeLists.txt 可以在不同平台上生成不同的构建文件。


2.1 -B 参数的含义

-B 参数用于指定 构建目录(build directory),即 CMake 生成的构建文件和编译中间产物存放的位置。

cmake -B build .

这里 build 和 . 都是 相对路径:

  • build:构建目录的相对路径,CMake 会在当前目录下创建 build 文件夹
  • .:源码目录的相对路径,即 CMakeLists.txt 所在的目录(当前目录)

也可以使用绝对路径来避免路径歧义:

cmake -B D:\test\build D:\test

使用独立的 build 目录(外部构建 / out-of-source build)的好处是:所有生成的中间文件、构建产物都集中在 build 文件夹中,不会污染源码目录。如果想清理所有构建产物,只需删除 build 文件夹即可。

注意:如果不指定 -B,CMake 会在当前目录直接生成构建文件(内部构建 / in-source build),这会导致源码和构建产物混在一起,不推荐使用。


2.2 -S 参数的含义

-S 参数用于显式指定 源码目录(source directory),即 CMakeLists.txt 所在的根目录。

cmake -S D:\test -B D:\test\build
  • -S D:\test:告诉 CMake 去 D:\test 下找 CMakeLists.txt
  • -B D:\test\build:告诉 CMake 把构建文件生成到 D:\test\build

实际上,cmake -B build . 中的 . 就是一个被省略了 -S 的源码路径参数。以下三种写法是等价的:

cmake -B build .                # 省略 -S,用位置参数指定源码路径
cmake -S . -B build             # 显式指定 -S
cmake -S D:\test -B D:\test\build  # 使用绝对路径

当 CMakeLists.txt 不在当前目录时,-S 就派上了用场——你可以在任意目录执行:

cmake -S D:\test -B D:\test\build

而不需要先 cd 到项目目录。


2.3 -G 参数与工具链的选择

-G 参数用于指定 生成器(Generator),决定 CMake 要生成哪种构建系统文件。

cmake -G "生成器名称" -B build .

常见的生成器:

生成器 生成的构建文件 适用平台
"MinGW Makefiles" Makefile Windows(MinGW 环境)
"Visual Studio 17 2022" .sln 解决方案文件 Windows(Visual Studio)
"Unix Makefiles" Makefile Linux / macOS
"Ninja" build.ninja 跨平台(需安装 Ninja)
"Xcode" .xcodeproj 项目文件 macOS

如果不指定 -G,CMake 会使用平台默认生成器。例如在安装了 Visual Studio 的 Windows 上可能默认使用 MSBuild,而在 Linux 上默认使用 Unix Makefiles。

# 查看当前平台可用的生成器列表
cmake --help

2.4 -D 参数的含义

-D 参数用于在配置阶段向 CMake 传递 变量,格式为 -D 变量名=值。

cmake -D CMAKE_BUILD_TYPE=Release -B build .

常用变量举例:

变量 含义 常用值
CMAKE_BUILD_TYPE 构建类型 Debug、Release、RelWithDebInfo、MinSizeRel
CMAKE_INSTALL_PREFIX 安装路径 如 /usr/local、D:\libs
CMAKE_CXX_COMPILER 指定 C++ 编译器 如 g++、clang++
BUILD_SHARED_LIBS 是否构建动态库 ON / OFF

-D 也可以用来传递自定义变量,供 CMakeLists.txt 中的 if() 判断使用:

# CMakeLists.txt 中
if(ENABLE_TESTS)
    enable_testing()
endif()
# 命令行中开启该选项
cmake -D ENABLE_TESTS=ON -B build .

理解要点:-D 就像是给 CMakeLists.txt 传入的"命令行参数",你可以在外部灵活控制构建行为,而不需要每次都修改 CMakeLists.txt 文件本身。


2.5 –build 参数的作用

--build 参数用于触发实际的 编译过程,它调用底层构建工具来完成编译链接。

cmake --build build

这里的 build 就是之前在 -B 中指定的构建目录。CMake 会自动检测该目录中生成的构建文件类型,并调用相应的构建工具(如 make、MSBuild、ninja)。

同样,build 可以使用相对路径或绝对路径:

# 相对路径
cmake --build build

# 绝对路径
cmake --build D:\test\build

--build 的常用子选项:

选项 作用 示例
--config 指定构建配置(多配置生成器如 VS、Xcode) --config Release
--target 目标名 只构建指定目标而非全部 --target exename
-j N / --parallel N 并行编译,N 为同时编译的任务数 --parallel 4
--clean-first 构建前先清理旧的产物 —
# 以 Release 模式并行编译
cmake --build build --config Release --parallel 8

# 只编译特定目标
cmake --build build --target exename

理解要点:cmake --build build 本质上是一个"代理命令"——你不必关心底层用的是 make 还是 MSBuild 还是 ninja,CMake 会自动帮你调用正确的工具。这让你在不同平台上使用统一的构建命令。