CMake 变量与缓存
本章将介绍 CMake 的变量系统和缓存机制,这是理解 CMake 配置过程的关键。变量是 CMake 中存储和传递数据的基本单位。
1. 变量基础
1.1 变量定义与引用
CMake 变量使用 set() 命令定义,使用 ${变量名} 语法引用。变量名区分大小写。
# 定义变量
set(MY_VARIABLE "Hello World")
# 引用变量
message("Value: ${MY_VARIABLE}") # 输出: Value: Hello World
# 变量可以包含空格和特殊字符
set(COMPLEX_VAR "This is a complex value with spaces")
# 未定义的变量返回空字符串
message("Undefined: ${UNDEFINED_VAR}") # 输出: Undefined:
1.2 变量取消定义
使用 unset() 命令可以取消变量定义,使其变为空字符串。
set(MY_VAR "some_value")
message("Before: ${MY_VAR}") # 输出: Before: some_value
unset(MY_VAR)
message("After: ${MY_VAR}") # 输出: After:
1.3 变量引用嵌套
CMake 支持变量引用的嵌套,这在动态变量名时很有用。
set(VAR_NAME "MESSAGE")
set(${VAR_NAME} "Hello") # 等同于 set(MESSAGE "Hello")
message("${MESSAGE}") # 输出: Hello
# 动态变量名示例
set(PREFIX "MY")
set(SUFFIX "VAR")
set(${PREFIX}_${SUFFIX} "Dynamic Value")
message("${MY_VAR}") # 输出: Dynamic Value
2. 设置变量
2.1 set 命令详解
set() 命令有多种形式,用于不同场景。
# 基本形式
set(VAR value)
# 设置多个值(创建列表)
set(LIST_VAR a b c d) # 等同于 "a;b;c;d"
# 设置缓存变量
set(CACHE_VAR "default" CACHE STRING "Description")
# 设置环境变量(仅在当前 CMake 运行期间有效)
set(ENV{PATH} "/new/path:$ENV{PATH}")
# 强制设置缓存变量
set(FORCED_VAR "value" CACHE STRING "" FORCE)
2.2 数学运算
使用 math() 命令进行数学运算,结果存储在变量中。
math(EXPR RESULT "1 + 2 * 3")
message("Result: ${RESULT}") # 输出: Result: 7
# 支持的运算符: + - * / % & | ^ ~ << >>
math(EXPR HEX_RESULT "0xFF & 0x0F")
message("Hex: ${HEX_RESULT}") # 输出: Hex: 15
2.3 布尔值处理
CMake 中的布尔值有特殊的处理方式。
# 真值: 1, ON, YES, TRUE, Y, 或任何非零数字
# 假值: 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串
set(MY_BOOL ON)
if(MY_BOOL)
message("Boolean is true")
endif()
# 使用 option 命令定义布尔选项
option(ENABLE_FEATURE "Enable feature X" ON)
3. 环境变量
3.1 读取环境变量
使用 $ENV{变量名} 语法读取系统环境变量。
# 读取 PATH 环境变量
set(MY_PATH "$ENV{PATH}")
# 在条件中使用环境变量
if(DEFINED ENV{HOME})
message("Home directory: $ENV{HOME}")
endif()
# 获取环境变量,如果未定义则使用默认值
if(NOT DEFINED ENV{MY_CONFIG})
set(MY_CONFIG "default_value")
else()
set(MY_CONFIG "$ENV{MY_CONFIG}")
endif()
3.2 设置环境变量
可以在 CMake 运行期间设置环境变量,影响子进程。
# 设置环境变量(仅影响当前 CMake 进程及其子进程)
set(ENV{MY_VAR} "value")
# 在 execute_process 中使用环境变量
execute_process(
COMMAND my_program
ENVIRONMENT "MY_VAR=value"
)
# 添加到 PATH
set(ENV{PATH} "/custom/path:$ENV{PATH}")
3.3 跨平台环境变量
# 跨平台处理路径分隔符
if(WIN32)
set(ENV{PATH} "C:\\mylibs;$ENV{PATH}")
else()
set(ENV{PATH} "/usr/local/mylibs:$ENV{PATH}")
endif()
4. 缓存变量
4.1 缓存变量基础
缓存变量存储在 CMakeCache.txt 文件中,在多次运行 CMake 时保持值。
# 基本语法
set(VAR_NAME value CACHE TYPE "description")
# 缓存类型:
# BOOL - 布尔值(ON/OFF)
# STRING - 字符串
# PATH - 文件路径
# FILEPATH- 文件路径(带文件选择器)
# INTERNAL- 内部变量(不在 GUI 中显示)
4.2 option 命令
option() 命令是定义布尔缓存变量的快捷方式。
# 定义布尔选项
option(ENABLE_TESTS "Enable unit tests" ON)
option(USE_OPENSSL "Use OpenSSL for encryption" OFF)
# 在条件中使用
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()
4.3 缓存变量操作
# 检查变量是否在缓存中定义
if(NOT DEFINED CACHE{MY_CACHE_VAR})
set(MY_CACHE_VAR "default" CACHE STRING "My variable")
endif()
# 从缓存中读取变量
set(LOCAL_COPY "$CACHE{MY_CACHE_VAR}")
# 强制更新缓存变量
set(MY_CACHE_VAR "new_value" CACHE STRING "" FORCE)
# 标记为高级选项(在 GUI 中默认隐藏)
mark_as_advanced(MY_CACHE_VAR)
4.4 交互式配置
# 使用缓存变量进行交互式配置
set(SERVER_PORT "8080" CACHE STRING "Server port number")
set(LOG_LEVEL "INFO" CACHE STRING "Logging level (DEBUG, INFO, WARN, ERROR)")
set_property(CACHE LOG_LEVEL PROPERTY STRINGS "DEBUG;INFO;WARN;ERROR")
# 提示用户输入
if(NOT DEFINED CACHE{API_KEY})
message("Please set API_KEY in the cache")
endif()
5. 内置变量参考
5.1 项目信息变量
# 项目相关变量
PROJECT_NAME # 当前项目名称
PROJECT_VERSION # 项目完整版本号
PROJECT_VERSION_MAJOR # 主版本号
PROJECT_VERSION_MINOR # 次版本号
PROJECT_VERSION_PATCH # 补丁版本号
PROJECT_SOURCE_DIR # 项目源码根目录
PROJECT_BINARY_DIR # 项目构建目录
# 示例
project(MyApp VERSION 1.2.3)
message("Building ${PROJECT_NAME} version ${PROJECT_VERSION}")
5.2 CMake 系统变量
# CMake 相关变量
CMAKE_SOURCE_DIR # 顶层源码目录
CMAKE_BINARY_DIR # 顶层构建目录
CMAKE_CURRENT_SOURCE_DIR # 当前 CMakeLists.txt 所在目录
CMAKE_CURRENT_BINARY_DIR # 当前构建目录
CMAKE_VERSION # CMake 版本号
CMAKE_MAJOR_VERSION # CMake 主版本号
CMAKE_MINOR_VERSION # CMake 次版本号
CMAKE_PATCH_VERSION # CMake 补丁版本号
CMAKE_COMMAND # CMake 可执行文件路径
CMAKE_ROOT # CMake 安装目录
5.3 构建配置变量
# 编译器变量
CMAKE_C_COMPILER # C 编译器路径
CMAKE_CXX_COMPILER # C++ 编译器路径
CMAKE_C_COMPILER_ID # C 编译器标识(GNU, MSVC, Clang 等)
CMAKE_CXX_COMPILER_ID # C++ 编译器标识
# 构建类型
CMAKE_BUILD_TYPE # 构建类型(Debug, Release, RelWithDebInfo, MinSizeRel)
# 编译标志
CMAKE_C_FLAGS # C 编译标志
CMAKE_CXX_FLAGS # C++ 编译标志
CMAKE_C_FLAGS_DEBUG # Debug 模式 C 编译标志
CMAKE_CXX_FLAGS_RELEASE # Release 模式 C++ 编译标志
CMAKE_EXE_LINKER_FLAGS # 可执行文件链接标志
CMAKE_SHARED_LINKER_FLAGS # 共享库链接标志
5.4 平台检测变量
# 操作系统变量
CMAKE_SYSTEM_NAME # 操作系统名称(Linux, Windows, Darwin)
CMAKE_SYSTEM_VERSION # 操作系统版本
CMAKE_SYSTEM_PROCESSOR # 处理器架构
# 平台布尔变量
WIN32 # Windows 平台(包括 64 位)
UNIX # Unix-like 平台(Linux, macOS, BSD)
APPLE # Apple 平台(macOS, iOS)
MSVC # Microsoft Visual C++ 编译器
MINGW # MinGW 编译器
CYGWIN # Cygwin 环境
5.5 安装路径变量
# 安装前缀
CMAKE_INSTALL_PREFIX # 安装根目录(默认 /usr/local 或 C:/Program Files)
# GNU 安装目录标准
CMAKE_INSTALL_BINDIR # 可执行文件目录(bin)
CMAKE_INSTALL_LIBDIR # 库文件目录(lib)
CMAKE_INSTALL_INCLUDEDIR # 头文件目录(include)
CMAKE_INSTALL_DATADIR # 数据文件目录(share)
CMAKE_INSTALL_MANDIR # 手册页目录(share/man)
# 示例
include(GNUInstallDirs)
install(TARGETS myapp DESTINATION ${CMAKE_INSTALL_BINDIR})
6. 变量作用域
6.1 作用域规则
CMake 中的变量有特定的作用域规则,理解这些规则对于正确管理变量至关重要。
# 全局变量(在所有作用域可见)
set(GLOBAL_VAR "global_value" CACHE INTERNAL "Global variable")
# 目录作用域变量(在当前目录及子目录可见)
set(DIRECTORY_VAR "directory_value")
# 函数作用域变量(仅在函数内可见)
function(my_function)
set(FUNCTION_VAR "function_value")
message("Inside: ${FUNCTION_VAR}") # 可以访问
endfunction()
# 调用函数后,FUNCTION_VAR 不可见
my_function()
message("Outside: ${FUNCTION_VAR}") # 空字符串
6.2 PARENT_SCOPE 修改
使用 PARENT_SCOPE 可以修改父作用域中的变量。
set(PARENT_VAR "original")
function(modify_parent)
# 修改父作用域变量
set(PARENT_VAR "modified" PARENT_SCOPE)
# 注意:PARENT_SCOPE 修改不影响当前作用域
message("Inside function: ${PARENT_VAR}") # 仍然是 "original"
endfunction()
modify_parent()
message("Outside function: ${PARENT_VAR}") # 现在是 "modified"
6.3 块作用域
CMake 3.25+ 支持块作用域,使用 block() 和 endblock()。
# 需要 CMake 3.25 或更高版本
block()
set(BLOCK_VAR "block_value")
message("Inside block: ${BLOCK_VAR}")
endblock()
message("Outside block: ${BLOCK_VAR}") # 空字符串
6.4 缓存与普通变量交互
# 普通变量遮蔽缓存变量
set(MY_VAR "local_value")
set(MY_VAR "cached_value" CACHE STRING "Description")
# 此时普通变量优先级更高
message("Value: ${MY_VAR}") # 输出: local_value
# 清除普通变量后,缓存变量生效
unset(MY_VAR)
message("Value: ${MY_VAR}") # 输出: cached_value
7. 字符串操作
7.1 字符串基本操作
CMake 提供了丰富的字符串操作命令。
# 字符串长度
string(LENGTH "Hello World" len) # len = 11
# 子字符串
string(SUBSTRING "Hello World" 0 5 sub) # sub = "Hello"
# 查找子字符串
string(FIND "Hello World" "World" pos) # pos = 6
string(FIND "Hello World" "world" pos) # pos = -1(未找到)
# 替换子字符串
string(REPLACE "World" "CMake" result "Hello World") # result = "Hello CMake"
7.2 字符串变换
# 大小写转换
string(TOLOWER "HELLO" lower) # lower = "hello"
string(TOUPPER "hello" upper) # upper = "HELLO"
# 去除空白
string(STRIP " hello " stripped) # stripped = "hello"
# 生成 UUID
string(UUID my_uuid NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8 NAME "myapp" TYPE MD5)
7.3 正则表达式操作
# 正则匹配
set(MY_STRING "Version 1.2.3")
if(MY_STRING MATCHES "Version ([0-9]+)\\.([0-9]+)\\.([0-9]+)")
message("Major: ${CMAKE_MATCH_1}") # Major: 1
message("Minor: ${CMAKE_MATCH_2}") # Minor: 2
message("Patch: ${CMAKE_MATCH_3}") # Patch: 3
endif()
# 正则替换
string(REGEX REPLACE "([0-9]+)" "NUM:\\1" result "abc123def456")
# result = "abcNUM:123defNUM:456"
# 正则匹配所有
string(REGEX MATCHALL "[0-9]+" numbers "abc123def456")
# numbers = "123;456"
7.4 配置文件生成
# 使用变量生成配置文件
set(APP_NAME "MyApp")
set(APP_VERSION "1.0.0")
set(APP_AUTHOR "John Doe")
# 在 config.h.in 中使用 @VAR@ 语法
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h
)
8. 列表操作
8.1 列表基础
CMake 中的列表是用分号分隔的字符串。
# 创建列表
set(MY_LIST a b c d) # "a;b;c;d"
set(MY_LIST "a;b;c;d") # 同上
set(MY_LIST a;b;c;d) # 同上
# 列表长度
list(LENGTH MY_LIST len) # len = 4
# 获取元素(0-based 索引)
list(GET MY_LIST 0 first) # first = "a"
list(GET MY_LIST 1 2 subset) # subset = "b;c"
# 查找元素
list(FIND MY_LIST "c" index) # index = 2
8.2 列表修改操作
# 追加元素
list(APPEND MY_LIST e f) # MY_LIST = "a;b;c;d;e;f"
# 插入元素
list(INSERT MY_LIST 0 x) # MY_LIST = "x;a;b;c;d;e;f"
# 移除元素
list(REMOVE_ITEM MY_LIST b) # MY_LIST = "x;a;c;d;e;f"
list(REMOVE_AT MY_LIST 0) # MY_LIST = "a;c;d;e;f"
# 移除重复项
list(REMOVE_DUPLICATES MY_LIST)
# 清空列表
list(REMOVE_ITEM MY_LIST a c d e f)
8.3 列表排序与变换
set(NUMBERS 3 1 4 1 5 9 2 6)
# 排序
list(SORT NUMBERS) # NUMBERS = "1;1;2;3;4;5;6;9"
# 反转
list(REVERSE NUMBERS) # NUMBERS = "9;6;5;4;3;2;1;1"
# 过滤(CMake 3.22+)
set(FILES file1.txt file2.cpp file3.h file4.cpp)
list(FILTER FILES INCLUDE REGEX "\\.cpp$") # FILES = "file2.cpp;file4.cpp"
list(FILTER FILES EXCLUDE REGEX "file2") # 移除包含 "file2" 的项
# 转换(CMake 3.22+)
list(TRANSFORM NUMBERS PREPEND "item_") # "item_9;item_6;..."
list(TRANSFORM NUMBERS TOLOWER)
8.4 列表与函数
# 将列表作为函数参数传递
function(process_list ITEMS)
foreach(item IN LISTS ITEMS)
message("Processing: ${item}")
endforeach()
endfunction()
set(MY_ITEMS apple banana cherry)
process_list("${MY_ITEMS}")
# 返回列表
function(generate_list OUTPUT_VAR)
set(result)
foreach(i RANGE 1 5)
list(APPEND result "item_${i}")
endforeach()
set(${OUTPUT_VAR} "${result}" PARENT_SCOPE)
endfunction()
generate_list(MY_GENERATED_LIST)
message("Generated: ${MY_GENERATED_LIST}")
小结
本章我们学习了 CMake 变量系统的核心内容:
- 变量基础:变量定义、引用、嵌套和取消定义
- 设置变量:set 命令、数学运算、布尔值处理
- 环境变量:读取和设置系统环境变量
- 缓存变量:持久化存储、option 命令、交互式配置
- 内置变量参考:项目信息、系统变量、构建配置、平台检测
- 变量作用域:目录、函数、块作用域及 PARENT_SCOPE
- 字符串操作:查找、替换、正则表达式、配置文件
- 列表操作:创建、修改、排序、过滤、转换
练习
- 创建一个项目,使用缓存变量配置编译选项(调试模式、优化级别等)
- 编写函数,使用列表管理源文件,自动排除测试文件
- 使用字符串操作解析版本号字符串(如 "1.2.3")
- 实现一个宏,根据环境变量和缓存变量决定是否启用功能
- 创建配置文件模板,使用变量替换生成最终配置