CMakePresets.json 文件结构:顶层结构解析
好,咱们正式开始聊 CMakePresets 的核心——那个 JSON 文件到底长什么样。
我第一次接触 CMakePresets 时,说实话有点懵。一个 JSON 文件,要管配置、构建、测试,还得兼容不同编译器、不同平台。但摸清楚顶层结构后,你会发现它其实很规整。说白了,就是四个顶层字段撑起整个体系。
核心要点:CMakePresets.json 的顶层结构由四个字段组成——schemaVersion、configurePresets、buildPresets、testPresets。其中只有 schemaVersion 是必填项,其他三个按需使用。
顶层结构总览
先看一个最简的完整示例,感受一下骨架:
{
"version": 6,
"schemaVersion": 6,
"configurePresets": [
{
"name": "default",
"displayName": "Default Config",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}"
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default"
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "default"
}
]
}
嗯,这里要注意:version 和 schemaVersion 在 CMake 3.23 之后其实是同一个东西。我个人习惯只写 version,省得混淆。
下面这张图能帮你快速建立整体认知:
schemaVersion:版本声明
这个字段最简单,但也最容易踩坑。
你想想看,CMake 版本在迭代,Presets 规范也在变。CMake 3.21 支持 version 4,3.23 支持 version 6。如果你写了个 version 6 的 Presets,但团队里有人用 CMake 3.20,那直接报错。
我曾经遇到过:一个项目在 CI 上跑得好好的,换到同事的本地环境就崩了。查了半天,发现 CI 的 CMake 是 3.25,同事的是 3.19。Presets 文件里写了 "version": 6,3.19 根本不认识。从那以后,我养成了习惯——在项目 README 里明确标注最低 CMake 版本要求。
版本对应关系如下:
| CMake 版本 | 最高支持 schemaVersion | 关键变化 |
|---|---|---|
| 3.19 - 3.20 | 3 | 基础支持,功能有限 |
| 3.21 - 3.22 | 4 | 新增 vendor 字段、条件支持 |
| 3.23 - 3.24 | 6 | 引入 workflowPresets,version 与 schemaVersion 统一 |
| 3.25+ | 6 | 稳定,推荐使用 |
我的建议:新项目直接用 "version": 6。如果必须兼容旧版 CMake,降到 version 4 也够用。version 3 就别用了,少了很多好东西。
configurePresets:配置阶段的指挥中心
这是三个 Presets 里最复杂的,也是我花时间最多的部分。
configurePresets 是一个数组,每个元素定义一套配置方案。你想想看,一个项目可能有 Debug、Release、RelWithDebInfo、MinSizeRel 四种配置,再加上不同的编译器、不同的平台,组合起来十几个 Preset 很正常。
每个 configurePreset 的核心字段:
- name:唯一标识符,其他 Preset 通过它来引用
- displayName:给人看的名字,CMake GUI 里会显示这个
- description:描述这个 Preset 的用途
- generator:指定构建系统生成器,比如 Ninja、Visual Studio 17 2022
- binaryDir:构建目录,支持
${sourceDir}、${presetName}等变量 - cacheVariables:设置 CMake 缓存变量,相当于
-D参数 - environment:设置环境变量
- inherits:继承其他 Preset,实现复用
看个实际例子:
"configurePresets": [
{
"name": "debug",
"displayName": "Debug (Ninja)",
"description": "Debug build using Ninja",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_FLAGS_DEBUG": "-g -O0 -DDEBUG"
}
},
{
"name": "release",
"displayName": "Release (Ninja)",
"description": "Release build using Ninja",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG"
}
}
]
这里有个小技巧——binaryDir 里用 ${presetName} 变量,可以自动匹配 Preset 名字。我习惯写成 "${sourceDir}/build/${presetName}",这样新增 Preset 时不用手动改路径。
buildPresets:构建阶段的快捷方式
buildPresets 相对简单。它不定义构建参数,而是引用一个 configurePreset,然后附加构建阶段的选项。
核心字段:
- name:唯一标识符
- configurePreset:关联的 configurePreset 名字
- jobs:并行任务数,相当于
-j参数 - targets:指定构建目标,相当于
--target - nativeToolOptions:传递给底层构建工具的参数
举个例子:
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"jobs": 8,
"targets": ["my_app", "my_tests"]
},
{
"name": "release",
"configurePreset": "release",
"jobs": 8
}
]
使用方式很简单:cmake --build --preset debug。它会自动找到关联的 configurePreset,读取构建目录,然后执行构建。
我个人习惯:buildPreset 的名字和 configurePreset 保持一致。这样 cmake --preset debug 和 cmake --build --preset debug 一一对应,不容易搞混。
testPresets:测试阶段的标准化
testPresets 是三个 Presets 里最晚加入的,CMake 3.20 才开始支持。它的结构和 buildPresets 类似,也是引用一个 configurePreset。
核心字段:
- name:唯一标识符
- configurePreset:关联的 configurePreset
- configuration:测试配置名称(多配置生成器用)
- output:控制测试输出,比如
{"outputOnFailure": true} - filter:测试用例过滤,支持正则表达式
看个例子:
"testPresets": [
{
"name": "debug",
"configurePreset": "debug",
"output": {
"outputOnFailure": true,
"maxTestNameWidth": 60
},
"filter": {
"include": {
"name": "unit_*"
}
}
},
{
"name": "release",
"configurePreset": "release",
"output": {
"outputOnFailure": false
}
}
]
使用方式:ctest --preset debug。它会自动定位到 configurePreset 指定的构建目录,然后执行测试。
我曾经踩过的坑:testPreset 的 configurePreset 字段必须和 buildPreset 的 configurePreset 指向同一个 Preset,否则测试会找不到构建产物。说白了,配置、构建、测试这三个 Preset 通过 configurePreset 字段串成一条链,断了一环就全废了。
三个 Preset 的协作关系
你想想看,这三个 Preset 不是孤立的。它们通过 configurePreset 字段串联起来,形成一条完整的流水线:
- 配置阶段:
cmake --preset debug→ 读取 debug configurePreset,生成构建系统 - 构建阶段:
cmake --build --preset debug→ 读取 debug buildPreset,找到关联的 configurePreset,进入构建目录执行编译 - 测试阶段:
ctest --preset debug→ 读取 debug testPreset,找到关联的 configurePreset,进入构建目录执行测试
这种设计的好处很明显——你只需要记住 Preset 的名字,不用关心构建目录在哪、生成器是什么、测试过滤条件怎么写。一切都在 Presets 文件里定义好了。
总结一下:CMakePresets.json 的顶层结构其实就四个字段。schemaVersion 是版本声明,configurePresets 管配置,buildPresets 管构建,testPresets 管测试。后三个通过 configurePreset 字段串联,形成完整的构建流水线。掌握了这个骨架,后面的细节填充就水到渠成了。