3、变量与缓存:set()与unset()、普通变量与缓存变量、CMake缓存机制、CMake GUI与ccmake的使用
CMake 里的变量系统,说实话,刚接触的时候容易让人懵。普通变量、缓存变量、环境变量……它们到底有什么区别?什么时候该用哪个?
我个人习惯把 CMake 的变量系统比作「两层楼」:一楼是普通变量,二楼是缓存变量。普通变量只在当前 CMakeLists.txt 和它调用的子目录里有效;缓存变量则会被写入 CMakeCache.txt,跨构建目录持久保存。
好,咱们一层一层拆开看。
3.1 set() 与 unset():变量的基本操作
先看最常用的 set() 命令。它的基本语法是:
set(<variable> <value>... [CACHE <type> <docstring> [FORCE]])
如果不加任何选项,那就是设置一个普通变量:
set(MY_VAR "hello")
message(STATUS "MY_VAR = ${MY_VAR}")
输出:MY_VAR = hello
如果要删除一个变量,用 unset():
unset(MY_VAR)
message(STATUS "MY_VAR = ${MY_VAR}") # 输出空字符串
嗯,这里要注意:unset() 之后,变量就变成未定义了。如果你在 if() 里判断它,会得到 FALSE。
set(VAR "" CACHE STRING "") 来初始化一个空缓存变量,而不是用 unset()。因为 unset() 在某些旧版 CMake 里行为不太一致。
3.2 普通变量 vs 缓存变量
普通变量是「一次性」的。每次运行 cmake 配置阶段,它们都会被重新计算。缓存变量则会被写入 CMakeCache.txt,下次运行 cmake 时仍然存在。
看个对比:
# 普通变量
set(MY_VAR "world")
message("普通变量: ${MY_VAR}")
# 缓存变量
set(MY_CACHE_VAR "hello" CACHE STRING "这是一个缓存变量")
message("缓存变量: ${MY_CACHE_VAR}")
第一次运行 cmake,两者都能输出。第二次运行 cmake,普通变量会被重新设置,缓存变量则保留上次的值——除非你用 FORCE 强制覆盖。
set() 覆盖(除非加 FORCE)。我曾经踩过这个坑:在项目根目录设置了一个缓存变量,然后在子目录里想改它,结果死活改不掉。后来才发现,子目录的 set() 默认不会覆盖父目录的缓存变量。
那什么时候用缓存变量?我总结了几种场景:
- 用户可配置的选项:比如
BUILD_SHARED_LIBS、CMAKE_BUILD_TYPE - 跨目录共享的变量:比如第三方库的路径
- 需要持久化的变量:比如编译器的路径、系统特性检测结果
3.3 CMake 缓存机制:CMakeCache.txt 的秘密
每次你运行 cmake -B build,CMake 都会在 build 目录下生成一个 CMakeCache.txt。这个文件是纯文本的,你可以直接打开看。
它的格式很简单:
// 变量的文档字符串
变量名:类型=值
举个例子:
// 构建类型
CMAKE_BUILD_TYPE:STRING=Release
// 是否构建共享库
BUILD_SHARED_LIBS:BOOL=ON
缓存变量的类型决定了 CMake GUI 里显示什么控件:
| 类型 | 说明 | GUI 控件 |
|---|---|---|
BOOL |
布尔值(ON/OFF) | 复选框 |
STRING |
字符串 | 文本框 |
PATH |
路径 | 文件选择器 |
FILEPATH |
文件路径 | 文件选择器 |
INTERNAL |
内部变量(不显示在 GUI 中) | 无 |
但缓存也有副作用。比如你升级了某个库,但 CMakeCache.txt 里还记录着旧路径,CMake 就不会重新查找。这时候你需要手动删除缓存文件,或者用 cmake --fresh 强制重新配置。
3.4 CMake GUI 与 ccmake:可视化配置工具
说实话,我平时很少用 GUI,但不得不承认,对于不熟悉 CMake 的开发者来说,GUI 确实更友好。
cmake-gui 是图形界面工具。你打开它,设置源码目录和构建目录,点击 Configure,然后就能看到所有缓存变量。你可以修改它们,再点击 Generate 生成构建系统。
它的界面大致分三块:
- 左侧:变量列表,按分组显示(如 CMAKE、项目自定义变量)
- 右侧:变量的值,可以直接编辑
- 底部:Configure、Generate 按钮,以及日志输出
ccmake 是终端里的交互式工具。运行 ccmake -B build 就能进入一个类似 ncurses 的界面。用上下键选择变量,回车编辑,按 c 配置,按 g 生成。
我个人更喜欢 ccmake,因为不用离开终端。而且它有个好处:修改变量后,它会高亮显示哪些变量被改了,方便你追踪。
ccmake 来快速调整编译选项。比如切换 Debug/Release 模式、开关某些功能。等确定下来后,再把这些选项固化到 CMakePresets.json 里。
最后,咱们用一张图来总结本章的知识体系:
这张图把本章的核心内容串起来了:变量分三类,缓存变量是核心,GUI 和 ccmake 是操作工具。你想想看,理解了这张图,CMake 的变量系统基本就掌握了八成。
set(VAR "value" CACHE STRING "") 设置了一个缓存变量,然后在另一个 CMakeLists.txt 里用 set(VAR "new_value") 想覆盖它。结果发现根本没用——因为缓存变量优先级更高。正确的做法是加 FORCE:set(VAR "new_value" CACHE STRING "" FORCE)。或者干脆用 unset() 先删掉再重新设置。
好了,关于变量与缓存,咱们就聊到这儿。记住:普通变量是「一次性」的,缓存变量是「持久化」的,环境变量是「系统级」的。搞清楚了这三者的区别,你在 CMake 里处理配置问题就会游刃有余。