构建预设(buildPresets):定义构建类型、生成器、构建目录等
好,咱们接着聊。前面我们把configurePresets讲透了,说白了就是给CMake喂参数、定工具链。那配置完了之后呢?当然是开始编译了。这就是buildPresets的活儿。
我个人习惯把构建预设看作是「编译阶段的配置清单」。你想想看,配置阶段决定了「用什么编译器、开哪些选项」,而构建阶段决定了「怎么编译、编译成什么、产物放哪儿」。这两者配合起来,才是一个完整的构建流程。
buildPresets 的核心字段
先看一个最基础的例子,感受一下:
{
"version": 6,
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug",
"configuration": "Debug",
"jobs": 8,
"targets": ["my_app", "my_tests"]
}
]
}
这里每个字段都有它的用意。我一个个说。
name 与 configurePreset
name 是预设的名字,这个不用多讲。configurePreset 是关键——它指定了当前构建预设要「绑定」哪个配置预设。说白了,你得先配置好,才能构建。这个字段就是建立这种依赖关系。
重要:一个构建预设必须关联一个配置预设。CMake 会先确保对应的配置预设已经执行过,然后再执行构建。
configuration:构建类型
这个字段对应的是 CMAKE_BUILD_TYPE。常见的值有 Debug、Release、RelWithDebInfo、MinSizeRel。
我在项目中遇到过一个问题:团队里有人用 Debug 构建,有人用 Release,结果联调的时候符号对不上,折腾了半天。后来我强制在 buildPresets 里固定了构建类型,再也没出过这种幺蛾子。
| 构建类型 | 优化级别 | 调试信息 | 典型用途 |
|---|---|---|---|
| Debug | -O0 | 完整 | 开发调试 |
| Release | -O3 | 无 | 正式发布 |
| RelWithDebInfo | -O2 | 有 | 性能分析 |
| MinSizeRel | -Os | 无 | 嵌入式/空间受限 |
generator:生成器指定
这个字段可以覆盖配置预设里指定的生成器。比如你配置时用了 Ninja,但某个构建预设想用 Visual Studio 17 2022,可以在这里改。
{
"name": "vs-release",
"configurePreset": "base",
"generator": "Visual Studio 17 2022",
"configuration": "Release"
}
小技巧:我个人建议尽量在配置预设里统一指定生成器,构建预设里少覆盖。除非你有特殊需求,比如跨平台测试时需要切换生成器。
buildDirectory:构建目录
这个字段控制编译产物的存放位置。默认情况下,CMake 会把构建目录放在源码目录下的 build 文件夹里。但你可以自定义。
{
"name": "out-of-source",
"configurePreset": "base",
"buildDirectory": "${sourceDir}/_build/${presetName}"
}
嗯,这里要注意:${sourceDir} 是 CMake Presets 支持的宏,会自动展开成源码路径。${presetName} 则是当前预设的名字。用这种组合,你可以轻松实现「每个预设一个独立构建目录」,互不干扰。
构建预设的继承机制
和配置预设一样,构建预设也支持 inherits。你可以定义一个基础预设,然后派生出多个变体。
{
"buildPresets": [
{
"name": "base",
"configurePreset": "base",
"configuration": "Debug",
"jobs": 4
},
{
"name": "release-x64",
"inherits": ["base"],
"configuration": "Release",
"environment": {
"ARCH": "x64"
}
},
{
"name": "release-arm",
"inherits": ["base"],
"configuration": "Release",
"environment": {
"ARCH": "arm64"
}
}
]
}
你看,base 预设定义了通用的配置,release-x64 和 release-arm 只改了自己需要改的部分。这样既减少了重复代码,又保持了清晰的结构。
注意:继承时,子预设会覆盖父预设的同名字段。但像 environment 这种字典类型,默认是合并而不是覆盖。如果你想要完全替换,需要显式设置。
构建目标与并行度
两个很实用的字段:targets 和 jobs。
- targets:指定要编译的目标列表。不指定的话,默认编译 all target。
- jobs:并行编译的线程数。相当于
make -j或ninja -j。
我曾经在 CI 环境里踩过一个坑:机器有 32 核,我设了 jobs: 32,结果内存爆了。后来我改成 jobs: 8,编译时间虽然长了一点,但系统稳定多了。所以啊,jobs 不是越大越好,得看你的机器扛不扛得住。
环境变量与缓存变量
构建预设里也可以设置环境变量和缓存变量,作用和配置预设类似。但要注意作用域:构建预设的环境变量只在构建阶段生效,不会影响配置阶段。
{
"name": "test-env",
"configurePreset": "base",
"environment": {
"MY_BUILD_FLAG": "1"
},
"cacheVariables": {
"ENABLE_TESTS": "ON"
}
}
知识体系总览
下面这张图,是我梳理的 buildPresets 核心逻辑。你看一眼,基本就全明白了。
实际项目中的用法
最后,我分享一个我在实际项目中用过的 buildPresets 配置。这个项目是个跨平台的 C++ 库,需要同时支持 Windows、Linux 和 macOS。
{
"buildPresets": [
{
"name": "dev",
"configurePreset": "dev",
"configuration": "Debug",
"jobs": 4,
"targets": ["all"]
},
{
"name": "ci-linux",
"configurePreset": "ci-linux",
"configuration": "RelWithDebInfo",
"jobs": 8,
"targets": ["my_lib", "my_tests"]
},
{
"name": "ci-windows",
"configurePreset": "ci-windows",
"configuration": "RelWithDebInfo",
"generator": "Visual Studio 17 2022",
"jobs": 4
},
{
"name": "release",
"inherits": ["ci-linux"],
"configuration": "Release",
"environment": {
"ENABLE_LOGGING": "0"
}
}
]
}
你看,dev 是给本地开发用的,ci-linux 和 ci-windows 是给 CI 用的,release 则继承了 ci-linux 并覆盖了构建类型和环境变量。每个预设各司其职,清晰明了。
我的建议:刚开始用 buildPresets 时,不要贪多。先定义两三个最常用的预设(比如 debug 和 release),等团队用顺手了再慢慢加。一口吃不成胖子,构建配置也一样。
好了,关于构建预设的核心内容就这些。说白了,它就是让你把「怎么编译」这件事标准化、可复用。下一节我们聊聊测试预设,那个也挺有意思的。
公众号:蓝海资料掘金营,微信deep3321