目录
1.简介
在 CMake 中,set
命令是设置变量(或属性)的核心工具,用于管理项目配置、编译选项、路径信息等。它支持多种变量类型(普通变量、缓存变量、环境变量等),并能控制变量的作用域和可见性。
基本语法:
cpp
set(<variable> <value>... [PARENT_SCOPE]) #设置普通变量
set(<variable> <value>... CACHE <type> <docstring> [FORCE]) #设置缓存条目
set(ENV{<variable>} [<value>]) #设置环境变量
其中 <variable>
是变量名,<value>
是变量值(支持多个值,用空格分隔),[OPTIONS]
是可选参数,控制变量的类型和行为。
set的值<value>...表示可以给变量设置0个或者多个值,当设置多个值时(大于2个),多个值会通过分号连接符连接成一个真实的值赋值给变量,当设置0个值时,实际上是把变量变为未设置状态,相当于调用unset命令。
如果使用了PARENT_SCOPE
选项,意味着该变量的作用域会传递到上一层(也就是上一层目录或者当前函数的调用者 ,如果是函数则传递到函数的调用者,如果是目录则传递到上一层目录),并且在当前作用域 该变量不受带PARENT_SCOPE
选项的set
命令的影响(如果变量之前没有定义,那么在当前作用域仍然是无定义的;如果之前有定义值,那么值和之前定义的值保持一致)。
2.变量类型与核心参数
set
命令支持以下变量类型,通过不同的参数区分:
1. 普通变量(最常用)
存储项目内部使用的临时值(如源文件列表、路径等),作用域为当前目录及子目录 (通过 add_subdirectory
引入的子目录可继承)。
cpp
set(<variable> <value1> <value2> ...) # 多个值会被合并为列表(用分号分隔)
关于变量的作用域:每一个新的目录或者函数都会创建一个新的作用域,普通变量的作用域,如果不使用PARENT_SCOPE选项,只能从外层往内层传递。
示例如下:
cpp
# 设置源文件列表
set(SOURCES main.cpp utils.cpp) //输出: main.cpp;utils.cpp
# 设置头文件路径
set(INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
2.缓存变量(CACHE 变量)
存储需要用户自定义的配置选项(如编译器路径、功能开关),会被写入 CMakeCache.txt
文件,支持通过 cmake-gui
或命令行修改。
cpp
set(<variable> <value> CACHE <type> <docstring> [FORCE])
将缓存条目variable设置为值<value> ...,除非用户进行设置或使用了选项FORCE,默认情况下缓存条目的值不会被覆盖。缓存条目可以通过CMAKE的GUI界面的add entry按钮来增加。缓存条目的实质为可以跨层级进行传递的变量,类似于全局变量。
-
参数说明:
<type>
:变量类型(影响cmake-gui
中的输入方式),可选值:
BOOL
(布尔值)、FILEPATH
(文件路径)、PATH
(目录路径)、STRING
(字符串)、INTEGER
(整数),INTERNAL(文本行)。<docstring>
:变量描述(在cmake-gui
中显示)。FORCE
:强制覆盖已存在的缓存变量(即使已被用户修改过)。
type各类型详细说明:
- BOOL:布尔值ON/OFF,CMAKE的GUI界面对此类缓存条目会提供一个复选框。
- FILEPATH:文件路径,CMAKE的GUI界面对此类缓存条目会提供一个文件选择框。
- PATH:目录路径,CMAKE的GUI界面对此类缓存条目会提供一个目录选择框。
- STRING / STRINGS:文本行,CMAKE的GUI界面对此类缓存条目会提供一个文本框(对应STRING)或下拉选择框(对应STRINGS)。
- INTERNAL:文本行,但是只用于内部,不对外呈现。主要用于运行过程中存储变量,因此使用该type意味着使用FORCE。
缓存条目的几个注意事项:
- 如果变量先前未定义或者使用了FORCE选项,则缓存条目会直接被赋值。
- 可以在使用cmake构建的使用通过-D选项来给缓存条目赋值,这样CMakeLists.txt内的set命令只会为缓存条目添加类型。
- 如果变量类型是目录或者文件路径,通过-D选项传入的若只是相对路径,那么set会给这个相对路径前添加当前的工作目录以变成绝对路径(如果已经是绝对路径则不会处理)。
示例如下:
cpp
cmake_minimum_required (VERSION 3.10.2)
project (set_test)
set (cache_entry_val ON OFF CACHE BOOL "choose ON to enable")
message (">>> value = ${cache_entry_val}")
set (cache_entry_val2 ON CACHE BOOL "choose ON to enable" FORCE)
message (">>> value2 = ${cache_entry_val2}")
set (cache_entry_val3 ON)
set (cache_entry_val3 OFF CACHE BOOL "choose ON to enable")
message (">>> value3 = ${cache_entry_val3}")
set (cache_entry_input OFF CACHE BOOL "choose ON to enable")
message (">>> value4 = ${cache_entry_input}")
set (mypath "test" CACHE FILEPATH "choose a file path")
message (">>> value5 = ${mypath}")
cpp
# 输入cmake构建,使用-D选项
cmake . -Dcache_entry_input=ON -Dmypath=sub
# 输出
>>> value = ON;OFF
>>> value2 = ON
>>> value3 = ON
>>> value4 = ON
>>> value5 = /XXX/XXX/XXX/sub
3.环境变量(ENV 变量)
设置构建过程中使用的环境变量(如编译器标志、库路径),通过 ENV{<var>}
访问。
cpp
set(ENV{<variable>} <value>) # 设置环境变量(覆盖原有值)
set(ENV{<variable>} "$ENV{<variable>};${value}") # 追加值到环境变量
示例如下:
cpp
# 设置编译器标志(覆盖原有值)
set(ENV{CXXFLAGS} "-O2 -Wall")
# 追加库路径到 LD_LIBRARY_PATH
set(ENV{LD_LIBRARY_PATH} "/usr/local/lib:$ENV{LD_LIBRARY_PATH}")
4.属性变量(Target/Directory/Global 属性)
设置目标(Target)、目录(Directory)或全局(Global)的属性(如编译选项、链接标志)。
cpp
set_property(<scope> <target> PROPERTY <prop> <value>) # 更推荐使用 set_property
# 等效于(但 `set` 仅支持部分属性):
set(<target>_<prop> <value>)
示例如下:(不推荐直接用 set
,优先用 set_property
)
cpp
# 设置目标的 C++ 标准(等效于 target_compile_features)
set_property(TARGET my_target PROPERTY CXX_STANDARD 17)
3.关键参数详解
1.PARENT_SCOPE
将变量设置到父作用域(当前目录的上一级目录)。子目录的 CMakeLists.txt 需将变量传递给父目录。示例如下:
cpp
# 子目录(subdir/CMakeLists.txt)中设置变量
set(SUBDIR_SOURCE "sub.cpp" PARENT_SCOPE)
# 父目录可获取该变量
message("Subdir source: ${SUBDIR_SOURCE}") # 输出 "sub.cpp"
注意:在函数内使用选项PARENT_SCOPE,对应的作用域只能传递到调用它的函数。
场景1 :在函数内使用选项PARENT_SCOPE定义变量,在函数定义的文件中(非另一个函数中)使用该变量。
结果 :变量无定义。
结论:函数内定义的变量,在函数定义的文件中调用,找不到变量的定义。
cpp
cmake_minimum_required (VERSION 3.10.2)
project (set_test)
function (test_fn arg1)
set (normal_var_in_fn ${arg1} PARENT_SCOPE)
endfunction (test_fn)
message (">>> in directory, value = ${normal_var_fn}")
输出为:
cpp
>>> in directory, value =
>>> in function, value =
场景2 :在函数内使用选项PARENT_SCOPE定义变量,在函数内使用该变量。
结果 :变量无定义。
结论:函数内使用选项PARENT_SCOPE定义的变量,在函数内也是无定义的。
cpp
cmake_minimum_required (VERSION 3.10.2)
project (set_test)
function (test_fn arg1)
set (normal_var_in_fn ${arg1} PARENT_SCOPE)
message (">>> in function, value = ${normal_var_fn}")
endfunction (test_fn)
test_fn (hello)
输出为:
cpp
>>> in function, value =
场景3:在函数内使用选项PARENT_SCOPE定义变量,在函数内使用该变量,并且使用set命令不带PARENT_SCOPE选项定义过该变量。
结果:函数内的变量值为不带PARENT_SCOPE选项的set命令所定义的。
结论:选项PARENT_SCOPE定义的变量作用域在上一层函数,当前函数的变量必须使用不带选项PARENT_SCOPE定义。
cpp
cmake_minimum_required (VERSION 3.10.2)
project (set_test)
function (test_fn arg1)
set (normal_var_in_fn nohello)
set (normal_var_in_fn ${arg1} PARENT_SCOPE)
message (">>> in function, value = ${normal_var_in_fn}")
endfunction (test_fn)
test_fn (hello)
输出为:
cpp
>>> in function, value = nohello
场景4 :在函数(示例中为test_fn)内使用选项PARENT_SCOPE定义变量,在另一个函数(调用者,示例中为test_fn_parent)内调用该函数。
结果:调用者函数内有该变量的定义。
结论:选项PARENT_SCOPE将变量传递到上一层调用函数。
cpp
cmake_minimum_required (VERSION 3.10.2)
project (set_test)
function (test_fn arg1)
set (normal_var_in_fn nohello)
set (normal_var_in_fn ${arg1} PARENT_SCOPE)
message (">>> in function, value = ${normal_var_in_fn}")
endfunction (test_fn)
function (test_fn_parent arg1)
test_fn (${arg1})
message (">>> in parent function, value = ${normal_var_in_fn}")
endfunction (test_fn_parent)
test_fn_parent (hello)
输出为:
cpp
>>> in function, value = nohello
>>> in parent function, value = hello
2.STRINGS
为缓存变量(CACHE
类型)提供候选值(仅对 STRING
类型有效)。示例如下:
cpp
set(BUILD_TYPE "Release" CACHE STRING "Build type (Debug/Release)")
set_property(CACHE BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
- 在
cmake-gui
中,BUILD_TYPE
会显示为下拉列表,选项为指定的候选值。
4.变量作用域与生命周期
变量类型 | 作用域 | 生命周期 |
---|---|---|
普通变量 | 当前目录及子目录(通过 add_subdirectory 继承) |
仅在 CMake 配置阶段有效,不写入缓存 |
缓存变量(CACHE) | 全局(所有目录可见) | 写入 CMakeCache.txt ,跨配置阶段保留 |
环境变量(ENV) | 构建过程(如 make 或 ninja 阶段) |
仅在当前 CMake 进程中有效 |
5.常见应用场景
1. 管理源文件与路径
cpp
# 设置源文件列表(普通变量)
set(SOURCES
main.cpp
utils/string_utils.cpp
utils/file_utils.cpp
)
# 设置头文件路径(普通变量)
set(INCLUDE_DIRS
"${PROJECT_SOURCE_DIR}/include"
"${PROJECT_BINARY_DIR}/generated"
)
# 添加到目标(结合 target_include_directories)
target_include_directories(my_target PUBLIC ${INCLUDE_DIRS})
2.控制编译选项(缓存变量)
cpp
# 定义布尔开关(默认开启优化)
set(ENABLE_OPTIMIZATION ON CACHE BOOL "Enable compiler optimizations")
# 根据开关设置编译标志
if(ENABLE_OPTIMIZATION)
target_compile_options(my_target PRIVATE -O3)
else()
target_compile_options(my_target PRIVATE -O0)
endif()
3.自定义用户配置(缓存变量)
cpp
# 定义安装路径(默认 /usr/local)
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Installation prefix")
# 定义第三方库路径(用户可手动指定)
set(ZLIB_ROOT "" CACHE PATH "Path to ZLIB installation")
find_package(ZLIB REQUIRED PATHS ${ZLIB_ROOT})
4.环境变量设置(构建阶段)
cpp
# 设置编译器搜索路径(覆盖系统默认)
set(ENV{PATH} "/opt/gcc-11/bin:$ENV{PATH}")
6.注意事项
1.避免滥用全局变量
- 普通变量应尽量限制在局部作用域(如子目录或函数内),避免与其他变量冲突。
- 缓存变量仅用于用户需要自定义的选项(如功能开关、路径配置),而非内部逻辑。
2. 优先使用现代 CMake 命令
- 对于目标属性(如头文件路径、编译选项),优先使用
target_include_directories
、target_compile_options
等命令,而非直接设置变量。 - 示例(反模式):
cpp
# 不推荐:直接设置全局变量
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
# 推荐:通过目标属性设置(仅影响特定目标)
target_compile_options(my_target PRIVATE -Wall)
3. 缓存变量的类型与文档
- 缓存变量必须指定
<type>
和<docstring>
,否则cmake-gui
无法正确显示和修改。 - 示例(错误):
cpp
set(ENABLE_LOGGING ON CACHE) # 缺少类型和文档,会报错
4.变量作用域的陷阱
- 子目录中定义的普通变量不会自动传递给父目录,需通过
PARENT_SCOPE
显式传递。 - 示例(错误):
cpp
# 子目录中设置变量
set(SUB_VAR "value")
# 父目录无法直接获取 SUB_VAR(值为空)
7.总结
set
命令是 CMake 中管理变量和配置的基础工具,核心用途包括:
- 存储项目内部临时数据(普通变量);
- 定义用户可配置选项(缓存变量);
- 控制构建环境(环境变量)。
合理使用 set
命令需注意变量类型、作用域和现代 CMake 最佳实践,避免全局变量滥用,优先通过目标属性(target_*
命令)管理编译逻辑。
相关链接
- CMake 官网 CMake - Upgrade Your Software Build System
- CMake 官方文档:CMake Tutorial --- CMake 4.0.2 Documentation
- CMake 源码:GitHub - Kitware/CMake: Mirror of CMake upstream repository
- CMake 源码:Sign in · GitLab