配置预设(configurePresets):定义编译器、缓存变量、工具链等

配置预设,说白了就是告诉 CMake:「嘿,这次构建你想怎么配置?」。它管的是编译前的那些事——用什么编译器、开哪些缓存变量、要不要指定工具链文件。我个人觉得,这是整个 Presets 体系里最核心的一块。

为什么这么说?你想想看,一个项目从拉下来到能跑,最头疼的就是配置这一步。每个人环境不一样,编译器版本不同,依赖路径各异。配置预设就是把这些差异统一管理起来,让团队所有人都用同一套规则。

配置预设的核心字段

一个典型的 configurePreset 长这样:

{
  "version": 6,
  "configurePresets": [
    {
      "name": "default",
      "displayName": "默认配置",
      "description": "使用 Ninja 生成器,Debug 模式",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/${presetName}",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_STANDARD": "17"
      }
    }
  ]
}

嗯,这里要注意几个关键字段:

  • name:预设的名字,后面引用时用这个
  • generator:生成器,比如 Ninja、Unix Makefiles
  • binaryDir:构建目录,我习惯用 ${sourceDir}/build/${presetName},这样不同预设互不干扰
  • cacheVariables:缓存变量,相当于你在 cmake 命令行里写的 -DXXX=YYY

编译器与工具链的指定

我在项目中遇到过最坑的事:同事用 GCC 编译,我用 Clang,结果链接阶段各种符号找不到。后来统一用配置预设指定编译器,问题就解决了。

指定编译器有两种方式:

方式一:通过 cacheVariables 指定

{
  "name": "clang-debug",
  "cacheVariables": {
    "CMAKE_C_COMPILER": "/usr/bin/clang-14",
    "CMAKE_CXX_COMPILER": "/usr/bin/clang++-14",
    "CMAKE_BUILD_TYPE": "Debug"
  }
}

方式二:通过 toolchainFile 指定

{
  "name": "arm-cross",
  "cacheVariables": {
    "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/toolchains/arm-gcc.cmake"
  }
}

我个人更推荐第二种方式。为什么?因为工具链文件里可以封装更多东西——编译器路径、链接器标志、系统根目录等等。你想想看,如果每个变量都写在 Presets 里,那 Presets 文件会变得又臭又长。

缓存变量的高级用法

缓存变量不只是设个值那么简单。有些变量是 CMake 内部定义的,有些是你自己项目定义的。我给大家列个常用清单:

变量名 说明 常用值
CMAKE_BUILD_TYPE 构建类型 Debug / Release / RelWithDebInfo
CMAKE_CXX_STANDARD C++ 标准 17 / 20 / 23
CMAKE_EXPORT_COMPILE_COMMANDS 导出编译命令(给 clangd 用) ON
BUILD_SHARED_LIBS 是否构建动态库 ON / OFF
CMAKE_INSTALL_PREFIX 安装路径 /usr/local 或自定义
小技巧: 我习惯把 CMAKE_EXPORT_COMPILE_COMMANDS 默认设为 ON。这样 IDE 的代码补全和静态分析工具就能直接用了,省得每次手动加参数。

环境变量与条件逻辑

配置预设还支持环境变量的设置。这个在 CI/CD 场景下特别有用:

{
  "name": "ci-release",
  "environment": {
    "CC": "gcc-12",
    "CXX": "g++-12",
    "CFLAGS": "-O2 -march=native"
  },
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Release"
  }
}

另外,你还可以用 condition 字段做条件判断。比如只在 Linux 上生效的预设:

{
  "name": "linux-only",
  "condition": {
    "type": "equals",
    "lhs": "${hostSystemName}",
    "rhs": "Linux"
  },
  "generator": "Ninja",
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Debug"
  }
}

我曾经在跨平台项目里用过这个特性。Windows 上用 Visual Studio 生成器,Linux 上用 Ninja,macOS 上用 Xcode。一个 Presets 文件搞定三个平台,团队里再也没人抱怨「我这边编译不过」了。

继承与复用

配置预设支持继承。你可以定义一个基础预设,然后其他预设继承它:

{
  "name": "base",
  "hidden": true,
  "generator": "Ninja",
  "binaryDir": "${sourceDir}/build/${presetName}",
  "cacheVariables": {
    "CMAKE_CXX_STANDARD": "20",
    "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
  }
},
{
  "name": "debug",
  "inherits": "base",
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Debug",
    "ENABLE_DEBUG_LOG": "ON"
  }
},
{
  "name": "release",
  "inherits": "base",
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Release",
    "ENABLE_DEBUG_LOG": "OFF"
  }
}

注意那个 "hidden": true。它表示这个预设不会出现在可用列表里,只作为基类被继承。这个设计很巧妙,对吧?

知识体系结构图

下面这张图总结了配置预设的核心逻辑:

配置预设(configurePresets)核心结构 configurePresets 基本配置 name / displayName generator / binaryDir description / hidden 缓存变量 (cacheVariables) CMAKE_BUILD_TYPE / CMAKE_CXX_STANDARD CMAKE_C_COMPILER / CMAKE_CXX_COMPILER CMAKE_TOOLCHAIN_FILE / 自定义变量 高级特性 environment 环境变量 condition 条件判断 inherits 继承复用 目标:统一配置流程,消除环境差异 团队共享 · 跨平台 · 可复用

避坑指南

我曾经踩过的坑:
  • 路径问题:binaryDir 里用了绝对路径,结果别人拉下来直接报错。记住用 ${sourceDir}${presetName} 变量。
  • 变量覆盖:继承时如果父预设和子预设定义了同一个变量,子预设会覆盖父预设。这个逻辑没问题,但容易忽略。
  • 工具链文件路径:工具链文件里如果用了相对路径,它是相对于 CMAKE_SOURCE_DIR 的,不是相对于 Presets 文件。我一开始就被这个坑过。

实战建议

最后,我给大家几个实战中的建议:

  1. 最少原则:配置预设里只放必须的变量。那些跟平台相关的、跟个人习惯相关的,尽量放到工具链文件里。
  2. 命名规范:预设名字要有意义。比如 linux-debugwin-release,一看就知道是干什么的。
  3. 版本控制:CMakePresets.json 一定要提交到 Git 里。这是团队共享的基础。
  4. 测试覆盖:至少准备 Debug 和 Release 两个预设。我见过有人只配了 Debug,上线前才发现 Release 构建有问题。

配置预设这东西,说白了就是「一次配置,到处使用」。花点时间把它配好,后面能省下大把时间。嗯,今天就聊到这里,大家去试试吧。


公众号:蓝海资料掘金营,微信deep3321