跳到主要内容

CMake 预设配置

CMake 预设(CMake Presets)是 CMake 3.19 引入的特性,允许你通过 JSON 文件定义项目的配置、构建和测试选项。这使得开发者可以轻松共享构建配置,而无需记忆复杂的命令行参数。

为什么需要预设配置?

传统方式的问题

在没有预设配置之前,开发者需要记忆或记录各种构建命令:

# 配置项目
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++

# 构建项目
cmake --build build -j4

# 运行测试
cd build && ctest --output-on-failure

这些命令不仅难以记忆,而且在团队协作中难以保持一致性。

预设配置的优势

使用预设配置后,构建变得简单:

# 配置
cmake --preset debug

# 构建
cmake --build --preset debug

# 测试
ctest --preset debug

预设配置的好处:

  1. 一致性:团队所有成员使用相同的配置
  2. 可维护性:配置集中管理,易于修改
  3. IDE 支持:VS Code、Visual Studio、CLion 等都支持读取预设
  4. 版本控制CMakePresets.json 可以提交到 Git

CMakePresets.json 结构

基本结构

{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [],
"buildPresets": [],
"testPresets": []
}

版本说明

版本CMake 最低版本新增特性
13.19基础配置预设
23.20构建/测试预设
33.21条件预设、工具链文件
43.23include 引用、包预设
53.24流程预设
63.25包预设、输出覆盖

配置预设(Configure Presets)

基础示例

{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25
},
"configurePresets": [
{
"name": "default",
"displayName": "Default Config",
"description": "默认配置,使用 Ninja 生成器",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}

关键字段说明

name(必需)

预设的唯一标识符,用于命令行引用:

cmake --preset default

displayName 和 description

用于在 IDE 和命令行中显示友好信息:

{
"name": "debug",
"displayName": "Debug 配置",
"description": "用于开发调试的配置,包含调试符号"
}

generator

指定构建系统生成器:

{
"generator": "Ninja"
}

常用生成器:

  • Ninja - 快速的单配置生成器
  • Ninja Multi-Config - 多配置生成器
  • Unix Makefiles - 传统 Makefile
  • Visual Studio 17 2022 - VS 2022 项目
  • Xcode - Xcode 项目

binaryDir

指定构建目录:

{
"binaryDir": "${sourceDir}/build/${presetName}"
}

可用变量:

  • ${sourceDir} - 项目根目录
  • ${presetName} - 当前预设名称
  • ${generator} - 生成器名称

cacheVariables

设置 CMake 缓存变量:

{
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BUILD_TESTS": {
"type": "BOOL",
"value": "ON"
}
}
}

预设继承

使用 inherits 字段复用配置:

{
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}"
},
{
"name": "debug",
"displayName": "Debug",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"displayName": "Release",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}

说明

  • hidden: true 表示该预设不会显示在 IDE 中,仅用于继承
  • 子预设会继承父预设的所有配置
  • 子预设可以覆盖继承的配置

条件预设

根据平台或环境条件性地启用预设:

{
"configurePresets": [
{
"name": "windows-only",
"displayName": "Windows 专用",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "linux-debug",
"displayName": "Linux Debug",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
}
]
}

条件类型

类型说明
equals相等比较
notEquals不等比较
inList值在列表中
notInList值不在列表中
matches正则匹配
notMatches正则不匹配
anyOf任一条件为真
allOf所有条件为真
not条件取反

环境变量

{
"configurePresets": [
{
"name": "with-env",
"environment": {
"MY_VAR": "custom_value",
"PATH": "$env{HOME}/bin:$penv{PATH}"
}
}
]
}

环境变量语法

  • $env{VAR} - 引用预设中定义的环境变量
  • $penv{VAR} - 引用父进程的环境变量
  • ${sourceDir} - 项目目录

工具链文件

指定交叉编译工具链:

{
"configurePresets": [
{
"name": "arm-cross",
"displayName": "ARM 交叉编译",
"toolchainFile": "${sourceDir}/toolchains/arm-linux.cmake"
}
]
}

构建预设(Build Presets)

基本结构

{
"buildPresets": [
{
"name": "debug",
"displayName": "Debug 构建",
"configurePreset": "debug",
"configuration": "Debug"
}
]
}

常用字段

{
"buildPresets": [
{
"name": "debug-verbose",
"configurePreset": "debug",
"jobs": 4,
"verbose": true,
"targets": ["myapp", "tests"],
"nativeToolOptions": ["--", "VERBOSE=1"]
}
]
}

字段说明

  • configurePreset - 关联的配置预设
  • jobs - 并行任务数(对应 -j
  • verbose - 详细输出
  • targets - 指定构建目标
  • cleanFirst - 构建前清理
  • configuration - 多配置生成器的构建类型

测试预设(Test Presets)

基本结构

{
"testPresets": [
{
"name": "debug",
"displayName": "Debug 测试",
"configurePreset": "debug",
"output": {
"outputOnFailure": true,
"verbosity": "default"
},
"execution": {
"noTestsAction": "error",
"stopOnFailure": false
}
}
]
}

输出配置

{
"output": {
"shortProgress": false,
"verbosity": "default",
"debug": false,
"outputOnFailure": true,
"quiet": false,
"outputLogFile": "test.log",
"outputJUnitFile": "junit.xml"
}
}

执行配置

{
"execution": {
"noTestsAction": "error",
"stopOnFailure": true,
"parallelJobs": 4,
"repeat": {
"mode": "until-pass",
"count": 3
},
"timeout": 300
}
}

筛选配置

{
"testPresets": [
{
"name": "unit-tests",
"configurePreset": "debug",
"filter": {
"include": {
"name": "Unit.*"
},
"exclude": {
"label": "slow"
}
}
}
]
}

完整示例

多配置项目示例

{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"BUILD_TESTING": "ON"
}
},
{
"name": "debug",
"displayName": "Debug",
"inherits": "base",
"cacheVariables": {
"ENABLE_DEBUG_SYMBOLS": "ON"
}
},
{
"name": "release",
"displayName": "Release",
"inherits": "base",
"cacheVariables": {
"ENABLE_LTO": "ON"
}
},
{
"name": "release-with-debug",
"displayName": "RelWithDebInfo",
"inherits": "release"
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug"
},
{
"name": "release",
"configurePreset": "release",
"configuration": "Release"
},
{
"name": "release-with-debug",
"configurePreset": "release-with-debug",
"configuration": "RelWithDebInfo"
}
],
"testPresets": [
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug",
"output": {
"outputOnFailure": true
}
},
{
"name": "release",
"configurePreset": "release",
"configuration": "Release"
}
]
}

跨平台配置示例

{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25
},
"configurePresets": [
{
"name": "common",
"hidden": true,
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"BUILD_TESTING": "ON"
}
},
{
"name": "windows-base",
"hidden": true,
"inherits": "common",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Visual Studio 17 2022"
},
{
"name": "unix-base",
"hidden": true,
"inherits": "common",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Ninja"
},
{
"name": "debug",
"displayName": "Debug",
"inherits": ["windows-base", "unix-base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"displayName": "Release",
"inherits": ["windows-base", "unix-base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}

带依赖下载的配置

{
"version": 6,
"configurePresets": [
{
"name": "with-deps",
"displayName": "With Dependencies",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"FETCHCONTENT_FULLY_DISCONNECTED": "OFF"
}
}
]
}

CMakeUserPresets.json

CMakeUserPresets.json 用于本地开发者的个人配置,不应提交到版本控制:

{
"version": 6,
"configurePresets": [
{
"name": "local-dev",
"inherits": "debug",
"cacheVariables": {
"MY_LOCAL_PATH": "/home/user/local"
}
}
]
}

使用规则

  • CMakeUserPresets.json 自动继承 CMakePresets.json
  • 不要将 CMakeUserPresets.json 提交到 Git
  • .gitignore 中添加 CMakeUserPresets.json

命令行使用

列出预设

# 列出所有配置预设
cmake --list-presets

# 列出构建预设
cmake build --list-presets

# 列出测试预设
ctest --list-presets

使用预设

# 配置
cmake --preset debug

# 构建
cmake --build --preset debug

# 测试
ctest --preset debug

# 清理构建
cmake --build --preset debug --target clean

覆盖选项

# 覆盖缓存变量
cmake --preset debug -DMY_VAR=custom

# 指定其他源目录
cmake --preset debug -S /path/to/source

IDE 集成

VS Code

安装 CMake Tools 扩展后,VS Code 会自动读取预设:

  1. 打开命令面板(Ctrl+Shift+P
  2. 输入 "CMake: Select Configure Preset"
  3. 从列表中选择预设

Visual Studio

Visual Studio 2019 及以上版本支持预设:

  1. 打开 文件 > 打开 > CMake
  2. 选择 CMakeLists.txt
  3. IDE 自动读取 CMakePresets.json

CLion

CLion 2021.3 及以上版本支持:

  1. 打开 Settings > Build > CMake
  2. 启用 "Use CMake Presets"
  3. 预设会自动显示在配置列表中

最佳实践

1. 使用隐藏基础预设

{
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}"
},
{
"name": "debug",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}

2. 按功能分组命名

{
"configurePresets": [
{ "name": "linux-debug" },
{ "name": "linux-release" },
{ "name": "windows-debug" },
{ "name": "windows-release" },
{ "name": "arm-cross-debug" }
]
}

3. 添加清晰的描述

{
"name": "asan",
"displayName": "Address Sanitizer",
"description": "用于检测内存错误的配置,启用 AddressSanitizer",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-fsanitize=address -fno-omit-frame-pointer"
}
}

4. 使用 include 分离配置

对于大型项目,可以将配置拆分到多个文件:

CMakePresets.json

{
"version": 6,
"include": [
"presets/common.json",
"presets/linux.json",
"presets/windows.json"
]
}

常见问题

问题 1:预设不被识别

检查:

  1. JSON 语法是否正确
  2. version 字段是否符合 CMake 版本
  3. 文件是否在项目根目录

问题 2:IDE 不显示预设

确保:

  1. IDE 版本支持预设功能
  2. hidden 字段不是 true
  3. condition 条件满足

问题 3:缓存变量不生效

缓存变量在首次配置后会被缓存,需要清理构建目录或强制更新:

rm -rf build
cmake --preset debug

小结

本章我们学习了:

  1. 预设配置的优势:一致性、可维护性、IDE 支持
  2. 配置预设:generator、binaryDir、cacheVariables、inherits
  3. 构建预设:关联配置预设、并行构建、目标指定
  4. 测试预设:输出配置、执行配置、测试筛选
  5. 跨平台配置:条件预设、多继承
  6. IDE 集成:VS Code、Visual Studio、CLion

预设配置是现代 CMake 项目的推荐配置方式,它简化了构建流程并提高了团队协作效率。

练习

  1. 创建一个包含 Debug、Release、RelWithDebInfo 三种配置的预设文件
  2. 配置跨平台的预设,Windows 使用 Visual Studio,Linux/macOS 使用 Ninja
  3. 为项目添加测试预设,配置失败时输出详细信息

参考资源