11、缓存变量(cacheVariables):定义 CMake 缓存变量,如 CMAKE_BUILD_TYPE

缓存变量,说白了就是 CMake 里那些「记住一次,下次不用再问」的变量。你想想看,每次运行 cmake 都要重新指定构建类型、安装路径,是不是很烦?缓存变量就是来解决这个问题的。

我个人习惯把缓存变量理解成「CMake 的配置文件」。第一次配置时设好值,它们会被写入 CMakeCache.txt。下次再跑 cmake,这些值自动生效,不用重复输入。嗯,这里要注意:缓存变量和普通变量不一样,普通变量只在当前 CMakeLists.txt 作用域有效,缓存变量是全局的、持久的。

11.1 缓存变量的本质

在 CMake Presets 里,cacheVariables 字段就是用来预设这些缓存变量的。它的语法很简单:

{
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Release",
    "CMAKE_INSTALL_PREFIX": "${sourceDir}/install",
    "MY_PROJECT_ENABLE_TEST": "ON"
  }
}

每个键值对对应一个缓存变量。值可以是字符串、布尔值、路径等。CMake 会自动识别类型。

核心要点:缓存变量一旦设置,除非手动清除或强制覆盖,否则一直保留。这既是优点也是坑点。

11.2 常见的缓存变量

我在项目中遇到过不少团队,对缓存变量的理解停留在「不就是 CMAKE_BUILD_TYPE 嘛」。其实远不止这些。下面这张图帮你理清常见的缓存变量分类:

常见缓存变量分类 cacheVariables 构建类型 CMAKE_BUILD_TYPE Debug / Release / RelWithDebInfo 安装路径 CMAKE_INSTALL_PREFIX /usr/local 或自定义路径 编译工具链 CMAKE_C_COMPILER CMAKE_CXX_COMPILER 项目功能开关 ENABLE_TEST / USE_GPU 等 其他常用变量 CMAKE_PREFIX_PATH 等

从图中可以看到,缓存变量覆盖了构建的方方面面。我个人最常用的是前三类:构建类型、安装路径和编译工具链。

11.3 CMAKE_BUILD_TYPE 的细节

这个变量可能是大家最熟悉的。但有个坑:它只对单配置生成器有效,比如 Makefile 和 Ninja。对于 Visual Studio 和 Xcode 这种多配置生成器,CMAKE_BUILD_TYPE 会被忽略。

我曾经踩过的坑:有一次团队里有人用 Ninja 生成器,但 Presets 里没设 CMAKE_BUILD_TYPE。结果默认是空字符串,编译出来的东西既不是 Debug 也不是 Release,优化级别乱七八糟。排查了半天才发现是缓存变量没设对。

正确的做法是:

{
  "cacheVariables": {
    "CMAKE_BUILD_TYPE": "Release"
  }
}

如果你用的是多配置生成器,就别设这个变量了。改用 configuration 字段来指定。

11.4 布尔值缓存变量

很多项目会用缓存变量来控制功能开关。比如:

{
  "cacheVariables": {
    "BUILD_SHARED_LIBS": "ON",
    "ENABLE_UNIT_TESTS": "OFF",
    "USE_OPENMP": "ON"
  }
}

这里有个小技巧:CMake 里布尔值用 ON/OFF,而不是 true/false。虽然 CMake 也认 TRUE/FALSE,但行业惯例是 ON/OFF。我建议统一用这个风格,团队协作时少一些歧义。

11.5 路径类缓存变量

路径变量要注意绝对路径和相对路径的区别。在 Presets 里,我推荐用 ${sourceDir}${sourceParentDir} 这类宏来构建路径:

{
  "cacheVariables": {
    "CMAKE_INSTALL_PREFIX": "${sourceDir}/output",
    "THIRD_PARTY_DIR": "${sourceParentDir}/third_party"
  }
}

这样做的好处是:项目换个目录,路径自动跟着变。不用手动改。

个人经验:我见过有人把绝对路径写死在 Presets 里,比如 /home/zhangsan/project/install。结果别人拉下来根本跑不了。用 ${sourceDir} 这种宏,才是团队协作的正确姿势。

11.6 缓存变量的覆盖规则

缓存变量有优先级。理解这个优先级,能帮你避免很多莫名其妙的 bug:

优先级 来源 说明
最高 命令行 -D 参数 cmake -DCMAKE_BUILD_TYPE=Debug ...
Presets 中的 cacheVariables 当前 Preset 配置
CMakeLists.txt 中的 set(... CACHE ...) 项目源码中的默认值
最低 CMake 内置默认值 比如 CMAKE_BUILD_TYPE 默认为空

为什么会这样?因为命令行 -D 是用户主动指定的,理应优先级最高。Presets 是团队约定,次之。源码里的默认值,只是兜底方案。

11.7 实战:一个完整的例子

来看一个我实际项目中用过的 Preset 配置:

{
  "configurePresets": [
    {
      "name": "release",
      "displayName": "Release 构建",
      "description": "生产环境发布版本",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/release",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_CXX_STANDARD": "17",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/release",
        "BUILD_SHARED_LIBS": "ON",
        "ENABLE_LOGGING": "OFF",
        "ENABLE_PROFILING": "OFF"
      }
    },
    {
      "name": "debug",
      "displayName": "Debug 构建",
      "description": "开发调试版本",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_STANDARD": "17",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/debug",
        "BUILD_SHARED_LIBS": "ON",
        "ENABLE_LOGGING": "ON",
        "ENABLE_PROFILING": "ON"
      }
    }
  ]
}

你看,Release 和 Debug 两个 Preset,通过 cacheVariables 区分了构建类型和功能开关。团队成员只需要 cmake --preset releasecmake --preset debug,就能得到完全一致的构建环境。

核心总结:缓存变量是 CMake Presets 的「记忆系统」。用好它,你的构建配置就能做到「一次定义,到处复用」。我个人建议把项目所有可配置项都通过缓存变量暴露出来,然后在 Presets 里统一管理。这样既清晰,又便于团队协作。

嗯,关于缓存变量就聊这么多。记住一点:缓存变量设好了,构建就稳了一半。另一半是什么?等你用到自然就知道了。