第1章:变量与缓存——CMake的“内存”与“便签本”

各位同学,欢迎来到CMake构建系统的第一课。今天我们要聊的,是CMake里最基础、也最容易让人迷糊的概念——变量与缓存。

说实话,我刚开始学CMake时,被普通变量、缓存变量、环境变量这三兄弟搞得晕头转向。明明都是“变量”,为什么行为差这么多?后来在项目里踩了几次坑,才慢慢摸清它们的脾气。今天我就把这些经验分享给你。

1.1 普通变量:最常用的“临时工”

普通变量,说白了就是CMake脚本里的临时存储。它在当前CMakeLists.txt及其子目录中有效,一旦cmake命令执行完毕,它就消失了。

设置普通变量用set()命令:

set(MY_VAR "hello")
set(MY_LIST a b c)  # 列表
set(MY_VAR "world" CACHE STRING "描述")  # 这其实是缓存变量,后面讲

读取变量用${}

message("MY_VAR = ${MY_VAR}")

作用域规则:普通变量遵循“父传子,子不传父”的原则。父目录定义的变量,子目录能读到;子目录定义的变量,父目录看不到。嗯,这有点像C++里的局部变量。

我的习惯:在顶层CMakeLists.txt里定义全局配置,用set()加上PARENT_SCOPE关键字可以向上传递变量。但说实话,我很少这么干,容易把作用域搞乱。

1.2 缓存变量:CMake的“永久便签本”

缓存变量就不一样了。它会被写入CMakeCache.txt文件,下次运行cmake时还能读到。你可以把它想象成CMake的“永久便签本”——记上去的东西,下次打开还在。

设置缓存变量:

set(MY_CACHE_VAR "default_value" CACHE STRING "这是描述")

注意这个CACHE关键字,它告诉CMake:这个变量要存到缓存里。后面的STRING是变量类型,"这是描述"会显示在cmake-gui里。

关键行为:如果缓存里已经有这个变量,set()不会覆盖它!除非你加上FORCE

set(MY_CACHE_VAR "new_value" CACHE STRING "描述" FORCE)

我曾经在项目里遇到一个坑:明明改了set()里的默认值,但构建时还是旧值。排查了半天,才发现是缓存里已经存了旧值,set()根本不会覆盖它。从那以后,我每次修改缓存变量默认值,都会顺手删掉CMakeCache.txt

避坑指南:我曾经因为缓存变量没更新,导致整个项目链接了错误的库版本。排查了整整一个下午。记住:CMakeCache.txt是持久化的,不会自动更新。如果你改了set()里的默认值,要么删缓存,要么加FORCE

1.3 环境变量:来自操作系统的“外部消息”

环境变量是CMake从操作系统那里“借”来的变量。用$ENV{}读取:

message("PATH = $ENV{PATH}")
message("HOME = $ENV{HOME}")

设置环境变量用set(ENV{...})

set(ENV{MY_ENV_VAR} "hello")

但要注意:这个设置只影响当前CMake进程,不会改变系统的环境变量。你想想看,如果CMake能随便改系统环境变量,那不乱套了?

实际用途:我经常用环境变量来传递构建机器的配置,比如编译器路径、安装目录等。这样CMakeLists.txt可以保持通用,不用硬编码路径。

1.4 option()命令:开关变量的“语法糖”

option()其实是set()的简化版,专门用来定义布尔类型的缓存变量:

option(USE_FEATURE_X "启用特性X" ON)

等价于:

set(USE_FEATURE_X ON CACHE BOOL "启用特性X")

为什么要有option()?说白了就是为了让代码更清晰。看到option(),你就知道这是个开关,不是普通配置。

我个人的习惯是:所有需要用户通过cmake -D来配置的开关,都用option()。这样CMakeLists.txt的可读性会好很多。

1.5 CMakeCache.txt:CMake的“配置文件”

CMakeCache.txt是CMake自动生成的文件,位于构建目录的根目录。它记录了所有缓存变量的值。

你可以直接编辑这个文件,但我不建议这么做。原因有二:

  • 格式敏感,容易改错
  • 下次cmake运行时可能会被覆盖

正确的做法是用cmake -D来修改变量:

cmake -DUSE_FEATURE_X=OFF -B build

或者用cmake-gui图形界面修改。

核心要点CMakeCache.txt是CMake的“记忆体”。它记住了你上次的配置,下次构建时直接复用。如果你改了CMakeLists.txt里的默认值,记得清理缓存,否则CMake会“固执地”使用旧值。

1.6 知识体系总览

下面这张图总结了三种变量的关系和使用场景:

CMake变量体系总览 普通变量 set(MY_VAR "val") 缓存变量 set(... CACHE ...) 环境变量 $ENV{...} 特性 普通变量 缓存变量 环境变量 持久化 ❌ 临时 ✅ 永久 ⚠ 进程级 作用域 目录级 全局 系统级 修改方式 set() set() / -D set(ENV{}) 典型用途 临时计算 用户配置 系统路径 箭头方向:普通变量 → 缓存变量 → 环境变量(作用域逐渐扩大)

1.7 实战建议

说了这么多,到底怎么用?我总结了几条经验:

  1. 临时计算用普通变量:比如拼接路径、计算版本号,用完就丢。
  2. 用户配置用缓存变量:比如开关选项、安装路径,需要持久化。
  3. 系统信息用环境变量:比如编译器路径、系统根目录,从外部获取。
  4. 开关用option():代码更清晰,别人一看就知道是开关。
  5. 修改默认值后删缓存:避免“改了没生效”的坑。
小技巧:调试时可以用message()打印变量值,看看是不是你想要的。我经常在CMakeLists.txt里写一堆message(),调试完再删掉。

好了,这一章的内容就到这里。变量和缓存是CMake的基石,理解透了,后面的学习会顺畅很多。记住:普通变量是临时工,缓存变量是正式员工,环境变量是外包人员。各司其职,别搞混了。


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