变量与缓存:普通变量、缓存变量、环境变量

各位同学,今天我们来聊聊 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

我曾经踩过的坑:有一次我在项目里设了一个缓存变量,结果同事拉代码后编译报错。查了半天才发现,他本地 CMakeCache.txt 里还留着旧值。从那以后,我每次改缓存变量都会加个注释,提醒大家删缓存。

环境变量:跟系统打交道

环境变量就是操作系统的环境变量。用 $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 变量系统里最绕的部分。我画了张图,帮你理清关系:

根作用域(根 CMakeLists.txt) 普通变量:可见于当前文件及子目录(除非被覆盖) 缓存变量:全局可见,写入 CMakeCache.txt 子目录 A 作用域 可读取根作用域的普通变量 可修改,但修改不影响根作用域 子目录 B 作用域 可读取根作用域的普通变量 可修改,但修改不影响根作用域 函数作用域 函数内部 set() 默认只影响函数内 用 PARENT_SCOPE 可影响调用者 缓存变量(全局) 所有作用域都可读写 修改后写入 CMakeCache.txt

你看这张图,根作用域设的普通变量,子目录能读到,但子目录改了也不会影响根。函数内部更独立,除非你用 PARENT_SCOPE。缓存变量则是个例外——它全局可见,谁都能改。

实战中的变量管理

我一般这样管理变量:

变量类型 用途 作用域 我的建议
普通变量 临时计算、循环、函数内部 当前作用域 别跨文件用
缓存变量 全局配置、用户可调参数 全局 加注释说明
环境变量 系统路径、外部工具 进程级 尽量少用

核心原则:普通变量做局部,缓存变量做全局。别混着用,否则你会后悔的。

避坑指南

最后,分享几个我踩过的坑:

  • 缓存变量不更新:第二次 cmake 时,set() 默认不覆盖缓存变量。加 FORCE 或者先 unset()
  • 变量名拼写错误:CMake 不会报错,只会返回空字符串。我建议用 message() 打印出来看看。
  • 作用域搞混:函数里改了变量,外面没变。用 PARENT_SCOPE 或者缓存变量。
  • 环境变量改了没生效:你只能在当前 cmake 进程里改,不会影响终端。需要的话,用 set(ENV{}) 配合 execute_process()

好了,变量这块就讲这么多。记住一句话:普通变量是临时的,缓存变量是永久的,环境变量是系统的。搞清楚了,你的 CMake 项目就能少一半的 bug。

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