跳到主要内容

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 变量系统的核心内容:

  1. 变量基础:变量定义、引用、嵌套和取消定义
  2. 设置变量:set 命令、数学运算、布尔值处理
  3. 环境变量:读取和设置系统环境变量
  4. 缓存变量:持久化存储、option 命令、交互式配置
  5. 内置变量参考:项目信息、系统变量、构建配置、平台检测
  6. 变量作用域:目录、函数、块作用域及 PARENT_SCOPE
  7. 字符串操作:查找、替换、正则表达式、配置文件
  8. 列表操作:创建、修改、排序、过滤、转换

练习

  1. 创建一个项目,使用缓存变量配置编译选项(调试模式、优化级别等)
  2. 编写函数,使用列表管理源文件,自动排除测试文件
  3. 使用字符串操作解析版本号字符串(如 "1.2.3")
  4. 实现一个宏,根据环境变量和缓存变量决定是否启用功能
  5. 创建配置文件模板,使用变量替换生成最终配置