4. 变量与缓存:set()命令、变量作用域、缓存变量(CACHE)、环境变量读取
CMake 里的变量系统,说实话,刚接触时挺容易懵的。
我记得第一次用 CMake 做跨平台项目,就栽在变量作用域上。明明在顶层设了个变量,子目录里死活读不到。后来才发现,CMake 的变量传递机制跟 C++ 的命名空间有点像,但又不太一样。
这一节,咱们就把变量这块彻底捋清楚。
4.1 set() 命令:变量的基本操作
set() 是 CMake 里最常用的命令。说白了,就是给变量赋值。
set(MY_VAR "hello")
set(MY_LIST "a" "b" "c") # 列表
set(MY_STRING "a;b;c") # 分号分隔的字符串
嗯,这里要注意:CMake 里没有真正的数组类型。列表就是用分号分隔的字符串。你写 "a" "b" "c",CMake 内部存成 "a;b;c"。
我在项目中遇到过一个问题:用 set() 给变量赋了值,但后面用 if() 判断时总是不对。后来发现是变量名拼错了。CMake 不会报错,只会返回空字符串。所以,变量名一定要写对。
MY_VAR 和 my_var 是两个不同的变量。
4.2 变量作用域:函数、目录与全局
CMake 的变量作用域分三层:
- 函数作用域:在
function()内部定义的变量,外部不可见 - 目录作用域:每个
CMakeLists.txt文件有自己的作用域 - 全局作用域:在顶层 CMakeLists.txt 中定义的变量,默认是全局的
为什么会这样?因为 CMake 的设计哲学是「隔离」。每个子目录应该独立,互不干扰。
举个例子:
# 顶层 CMakeLists.txt
set(TOP_VAR "我是顶层")
add_subdirectory(sub)
message("TOP_VAR = ${TOP_VAR}") # 可以访问
message("SUB_VAR = ${SUB_VAR}") # 空,子目录的变量不可见
# sub/CMakeLists.txt
set(SUB_VAR "我是子目录")
message("TOP_VAR = ${TOP_VAR}") # 可以访问,父目录变量默认向下传递
我个人习惯在顶层定义所有「公共」变量,比如项目版本号、安装路径等。子目录里只定义局部变量。
set(PARENT_SCOPE)。但我不建议频繁使用,容易让变量流变得混乱。
4.3 缓存变量(CACHE):持久化存储
缓存变量是 CMake 里一个很特别的存在。它会被写入 CMakeCache.txt 文件,下次运行 CMake 时自动加载。
我刚开始用 CMake 时,不理解为什么有些变量设了值,重新 cmake 后又变回去了。后来才明白,那是普通变量。缓存变量才是「记住」的。
set(MY_CACHE_VAR "默认值" CACHE STRING "这是一个缓存变量")
缓存变量的典型用途:
- 用户可配置的选项(如
BUILD_SHARED_LIBS) - 编译器的路径
- 第三方库的安装位置
我曾经踩过一个坑:在子目录里用 set(... CACHE) 定义了一个缓存变量,结果顶层 CMakeLists.txt 里也定义了同名变量。两个值互相覆盖,调试了半天才发现。
cmake-gui 或 ccmake 里就能看到。如果你不想让用户修改,可以用 INTERNAL 类型:set(... CACHE INTERNAL "")。
4.4 环境变量读取:与系统交互
CMake 可以读取系统的环境变量。语法很简单:
set(ENV_VAR $ENV{HOME})
message("HOME = ${ENV_VAR}")
也可以设置环境变量(只在 CMake 运行期间有效):
set(ENV{MY_ENV} "临时值")
我在项目中常用环境变量来做两件事:
- 读取
PATH来查找工具链 - 读取
CMAKE_PREFIX_PATH来定位第三方库
但要注意,环境变量是「只读」的。你可以在 CMake 里改,但不会影响父进程的环境。
- 普通变量:当前作用域有效,不会持久化
- 缓存变量:跨 CMake 运行持久化,用户可配置
- 环境变量:从系统读取,CMake 内可修改但不影响外部
4.5 知识体系图:变量与缓存的核心逻辑
下面这张图,帮你理清 CMake 变量系统的全貌:
4.6 实战建议:变量命名与组织
最后,分享几个我多年积累的变量管理习惯:
- 命名规范:项目级变量用
PROJECT_前缀,模块级用MODULE_前缀 - 缓存变量:只在顶层 CMakeLists.txt 中定义,子目录不要碰
- 环境变量:读取时加默认值,避免空指针
- 调试技巧:用
message(STATUS "VAR = ${VAR}")打印变量值
嗯,变量这块就讲到这里。记住一句话:普通变量是临时的,缓存变量是永久的,环境变量是外来的。搞清这三者的关系,CMake 变量系统就没什么难的了。