源码及CMakeLists.txt沿用第1篇或第2篇的均可,本文沿用第2篇的相关文件
1. CMake变量的基础操作
1.1 定义与修改变量
在CMakeLists.txt后面加入如下代码:
cpp
# 定义普通变量
set(MY_VAR "Hello World")
# 修改变量值
set(MY_VAR "New Value")
# 列表变量(分号分隔)
set(MY_LIST a.cpp b.cpp c.cpp)
# 或显式列表
set(MY_LIST "a.cpp;b.cpp;c.cpp")
# 打印变量内容
message("变量值: ${MY_VAR}") # 输出: 变量值: New Value
# 打印列表内容
message("列表内容: ${MY_LIST}") # 输出: 列表内容: a.cpp;b.cpp;c.cpp
执行cmake --build .
可在结果编译的输出信息中找到对应的输出,如下图:
1.2 删除变量
cpp
unset(MY_VAR) # 删除当前作用域的变量
2. 变量的作用域层级
修改目录结构如下:
plaintext
hello_cmake/
├── CMakeLists.txt
├── include/
│ └── utils.h
├── src/
│ ├── utils.cpp
│ └── main.cpp
└── build/
└── subdir/
│ └── CMakeLists.txt
2.1 目录作用域(Directory Scope)
cpp
# 父目录的CMakeLists.txt
set(PARENT_VAR "Parent Value")
add_subdirectory(subdir)
message("父目录中读取被改变的变量: ${PARENT_VAR}") # 可读取
cpp
# 子目录subdir/CMakeLists.txt
message("子目录读取父变量: ${PARENT_VAR}") # 可读取
set(PARENT_VAR "Child Modified" PARENT_VAR) # 修改父作用域的变量
输出结果如下:
作用域规则
- 父目录的变量在子目录中是只读副本,子目录对变量的修改不会影响父目录。
- 每个
add_subdirectory()
都会创建一个新的作用域(类似函数调用)。
修改父目录变量的方法
- 在子目录中使用
set(... PARENT_SCOPE)
将变量传递到父作用域。 - 缓存变量是全局的,所有作用域共享。
cpp
# 父目录 CMakeLists.txt
set(MY_CACHE_VAR "初始值" CACHE STRING "说明")
# 子目录 subdir/CMakeLists.txt
set(MY_CACHE_VAR "新值" CACHE STRING "说明" FORCE) # 强制修改缓存变量
message("子目录中的 MY_CACHE_VAR = ${MY_CACHE_VAR}") # 输出 "新值"
- 使用全局属性(
set_property
)
cpp
# 父目录 CMakeLists.txt
set_property(GLOBAL PROPERTY MY_GLOBAL_VAR "初始值")
# 子目录 subdir/CMakeLists.txt
get_property(tmp GLOBAL PROPERTY MY_GLOBAL_VAR)
set_property(GLOBAL PROPERTY MY_GLOBAL_VAR "${tmp}_子目录修改")
2.2 函数/宏作用域
cpp
function(my_function)
set(LOCAL_VAR "Local Value" PARENT_SCOPE) # 修改上级作用域
endfunction()
my_function()
message("函数外部: ${LOCAL_VAR}") # 输出: 函数外部: Local Value
2.3 目标作用域(Target Scope)
cpp
add_library(mylib STATIC mylib.cpp)
# 仅对mylib目标生效
target_compile_definitions(mylib PRIVATE DEBUG_MODE=1)
3. 缓存变量(CACHE Variables)详解
3.1 定义缓存变量
cpp
# 语法:set(<变量名> <值> CACHE <类型> <描述> [FORCE])
set(BUILD_TESTS OFF CACHE BOOL "是否启用测试")
# 高级用法:下拉选项
set(COMPILER_TYPE "GCC" CACHE STRING "选择编译器")
set_property(CACHE COMPILER_TYPE PROPERTY STRINGS "GCC;Clang;MSVC")
以上的修改在纯命令行编译时无法体型,在CMake GUI中可以看到会增加对应的可选项。
3.2 修改与覆盖
命令行覆盖
bash
cmake -DBUILD_TESTS=ON ..
脚本强制修改
cpp
set(BUILD_TESTS ON CACHE BOOL "覆盖值" FORCE)
3.3 缓存变量类型
类型 | 描述 | 示例 | 适用场景 | GUI显示形式 |
---|---|---|---|---|
BOOL | 布尔值,表示开关选项(ON/OFF)。 | set(USE_FEATURE_X ON CACHE BOOL "...") | 控制功能开关(如 ENABLE_TESTING)。 | 复选框(Checkbox)。 |
STRING | 普通字符串,可配合 STRINGS 属性限定可选值。 | set(BUILD_TYPE "Debug" CACHE STRING "...") | 配置构建类型(如 Debug/Release)。 | 下拉框(若定义了 STRINGS 属性)。 |
PATH | 表示文件系统目录路径,CMake GUI 提供路径选择按钮。 | set(INSTALL_PREFIX "/usr/local" CACHE PATH "...") | 指定安装目录、依赖库路径等。 | 路径选择对话框(带浏览按钮)。 |
FILEPATH | 表示文件路径,CMake GUI 提供文件选择按钮。 | set(CONFIG_FILE "config.cfg" CACHE FILEPATH "...") | 指定配置文件、输入文件路径。 | 文件选择对话框(带浏览按钮)。 |
INTERNAL | 内部变量,不在 GUI 中显示,但值会持久化到缓存。 | set(INTERNAL_CACHE "secret" CACHE INTERNAL "...") | 存储内部配置(如哈希值、临时标记)。 | 不显示。 |
STATIC | 静态描述文本,仅用于在 GUI 中分组或注释(实际不存储值)。 | set(HELP_TEXT "Options:" CACHE STATIC "...") | GUI 界面中的分组标题或说明文本。 | 只读文本标签。 |
4. 环境变量操作
4.1 读取系统环境变量
cpp
# 读取PATH环境变量
message("系统PATH: $ENV{PATH}")
# 在代码中使用(通过编译定义传递)
add_definitions(-DMY_ENV_PATH="$ENV{PATH}")
4.2 设置临时环境变量
cpp
# 仅影响当前CMake进程
set(ENV{CFLAGS} "-O2")
5. 实战技巧与避坑指南
5.1 变量优先级规则
cpp
普通变量 > 缓存变量(无FORCE时) > 环境变量
5.2 避免变量污染
cpp
# 错误示例:全局变量污染
set(SOURCES main.cpp) # 全局作用域
# 正确做法:限定作用域
function(add_module)
set(SOURCES a.cpp b.cpp) # 函数作用域
add_library(mymodule ${SOURCES})
endfunction()
5.3 类型转换技巧
cpp
# 字符串转列表
string(REPLACE ";" " " MY_STR "${MY_LIST}") # "a.cpp b.cpp c.cpp"
# 列表操作
list(APPEND MY_LIST d.cpp) # 添加元素
list(REMOVE_ITEM MY_LIST a.cpp) # 删除元素
6. 常见问题解答
Q1:CACHE变量和普通变量有什么区别?
- CACHE变量:持久化存储(写入CMakeCache.txt),可跨多次配置
- 普通变量:临时存在,作用域结束后销毁
Q2:如何强制刷新缓存变量?
- 删除build目录或使用cmake -U <变量名>清除
- 在脚本中用FORCE关键字
Q3:为什么我的变量在子目录中不可见?
- 默认变量传递是向下传递的,向上修改需用PARENT_SCOPE
- 跨目录共享变量建议用CACHE变量