第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++里的局部变量。
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 知识体系总览
下面这张图总结了三种变量的关系和使用场景:
1.7 实战建议
说了这么多,到底怎么用?我总结了几条经验:
- 临时计算用普通变量:比如拼接路径、计算版本号,用完就丢。
- 用户配置用缓存变量:比如开关选项、安装路径,需要持久化。
- 系统信息用环境变量:比如编译器路径、系统根目录,从外部获取。
- 开关用
option():代码更清晰,别人一看就知道是开关。 - 修改默认值后删缓存:避免“改了没生效”的坑。
message()打印变量值,看看是不是你想要的。我经常在CMakeLists.txt里写一堆message(),调试完再删掉。
好了,这一章的内容就到这里。变量和缓存是CMake的基石,理解透了,后面的学习会顺畅很多。记住:普通变量是临时工,缓存变量是正式员工,环境变量是外包人员。各司其职,别搞混了。
公众号:蓝海资料掘金营,微信deep3321