变量与缓存:普通变量、缓存变量、环境变量
各位同学,今天我们来聊聊 CMake 里最基础但也最容易踩坑的东西——变量。说实话,我见过太多工程师在变量作用域上翻车了。我自己刚用 CMake 那会儿,也曾经因为一个缓存变量没搞清楚,折腾了整整一个下午。
变量这东西,说白了就是给值起个名字。但 CMake 的变量系统有点特别,它分了三种:普通变量、缓存变量、环境变量。每种都有自己的脾气。
普通变量:最常用的那个
普通变量就是最常见的变量。你用 set() 设置它,用 ${} 引用它。作用域默认是当前 CMakeLists.txt 文件以及它调用的子目录。
set(MY_VAR "hello")
message("${MY_VAR}") # 输出 hello
嗯,这里要注意:普通变量只在当前作用域有效。如果你在根 CMakeLists.txt 里设了一个变量,子目录里是看不到的——除非你用 PARENT_SCOPE 或者 CACHE。
缓存变量:跨文件传数据的利器
缓存变量就不一样了。它会被写入 CMakeCache.txt,所以整个构建树都能看到。你想想看,这多方便——你在根目录设一个 CMAKE_BUILD_TYPE,所有子目录都能读到。
set(MY_CACHE_VAR "world" CACHE STRING "这是一个缓存变量")
这里有个坑:如果你第二次运行 cmake,缓存变量已经存在了,set() 默认不会覆盖它。除非你加 FORCE。
环境变量:跟系统打交道
环境变量就是操作系统的环境变量。用 $ENV{} 读取,用 set(ENV{}) 设置。但注意,你只能在当前 cmake 进程里改环境变量,不会影响父进程。
message("PATH = $ENV{PATH}")
set(ENV{MY_ENV} "test")
说实话,环境变量我用的不多。只有在需要读取系统路径、或者跟外部工具交互时才会碰它。
set() 与 unset():成对出现的好兄弟
set() 用来设变量,unset() 用来删变量。用法很简单:
set(FOO "bar")
unset(FOO) # 删掉 FOO
但有个细节:unset() 也可以删缓存变量。你只要加 CACHE 关键字就行:
unset(MY_CACHE_VAR CACHE)
嗯,这个我经常用。比如调试时想重置某个缓存变量,直接 unset 再重新 cmake 就好,不用手动删 CMakeCache.txt。
变量作用域:搞懂它,少踩一半坑
作用域是 CMake 变量系统里最绕的部分。我画了张图,帮你理清关系:
你看这张图,根作用域设的普通变量,子目录能读到,但子目录改了也不会影响根。函数内部更独立,除非你用 PARENT_SCOPE。缓存变量则是个例外——它全局可见,谁都能改。
实战中的变量管理
我一般这样管理变量:
| 变量类型 | 用途 | 作用域 | 我的建议 |
|---|---|---|---|
| 普通变量 | 临时计算、循环、函数内部 | 当前作用域 | 别跨文件用 |
| 缓存变量 | 全局配置、用户可调参数 | 全局 | 加注释说明 |
| 环境变量 | 系统路径、外部工具 | 进程级 | 尽量少用 |
核心原则:普通变量做局部,缓存变量做全局。别混着用,否则你会后悔的。
避坑指南
最后,分享几个我踩过的坑:
- 缓存变量不更新:第二次 cmake 时,
set()默认不覆盖缓存变量。加FORCE或者先unset()。 - 变量名拼写错误:CMake 不会报错,只会返回空字符串。我建议用
message()打印出来看看。 - 作用域搞混:函数里改了变量,外面没变。用
PARENT_SCOPE或者缓存变量。 - 环境变量改了没生效:你只能在当前 cmake 进程里改,不会影响终端。需要的话,用
set(ENV{})配合execute_process()。
好了,变量这块就讲这么多。记住一句话:普通变量是临时的,缓存变量是永久的,环境变量是系统的。搞清楚了,你的 CMake 项目就能少一半的 bug。