- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
option()是 CMake 中定义用户可选布尔开关的核心命令,广泛用于开关功能特性(如USE_SSL、BUILD_TESTING)或让用户通过cmake -D传入配置值。本文以 Help/command/option.rst 为主干,结合 源码实现 与相关策略文档、测试用例,系统讲解option()的语法、默认值、缓存行为、脚本模式差异,以及CMP0077、CMP0126等策略对命令行为的影响,帮助你写出可预测、可维护的配置脚本。
一、命令语法与基本用法
option()的完整语法为:
option(<variable> "<help_text>" [value])其中:
| 参数 | 含义 |
|---|---|
<variable> | 要定义的布尔选项变量名 |
<help_text> | 选项说明文字,会作为该缓存变量的帮助字符串(HELPSTRING),显示在cmake-gui/ccmake等界面中 |
[value] | 可选的初始值。省略时默认值为OFF;传入时建议使用ON/OFF(也支持其他真/假写法,见下文) |
典型用法:
option(USE_SSL "Enable SSL support in the project" OFF) option(BUILD_TESTING "Build the testing tree" ON)<help_text>必须提供(源码 cmOptionCommand.cxx 要求参数个数为 2 或 3,即<variable>与<help_text>为必填项),它是用户在图形界面中理解该选项的唯一文字说明,建议写成一句简短、明确的描述。
二、默认值语义:OFF 与真值解析
从源码可见,初始值的默认处理如下(cmOptionCommand.cxx):
- 若未提供
[value],以字符串"Off"作为初始值; - 若提供
[value],则使用该字符串; - 最终通过
cmIsOn()判定真伪,将结果统一规范化为"ON"或"OFF"写入缓存。
也就是说,即使你写option(FOO "desc" 1)或option(FOO "desc" TRUE),缓存中最终保存的也会是规范化的ON/OFF字符串(cmIsOn会把1、YES、TRUE、ON等视为真,把0、NO、FALSE、OFF等视为假)。这一点保证了所有option()创建的条目类型都是BOOL,后续用if(<variable>)判断时行为一致。
三、已存在变量时“不做任何事”:CMP0077 策略
原文档明确指出:如果<variable>已作为普通变量或缓存变量存在,则option()命令不做任何事(见策略 CMP0077)。这是 CMake 3.13 引入的策略,其细节值得深挖。
3.1 普通变量已存在(NEW 行为)
当策略CMP0077为NEW时,如果当前作用域已存在同名普通变量,option()会直接返回、完全不修改任何状态(cmOptionCommand.cxx):不创建缓存条目、不删除普通变量、不更新任何值。
测试用例 Tests/RunCMake/option/CMP0077-NEW.cmake 验证了这一行为:先set(OPT_LOCAL_VAR FALSE)再option(OPT_LOCAL_VAR "TEST_VAR" ON),随后断言该变量仍为假、且缓存中不存在该条目,否则报FATAL_ERROR。
这一设计解决了"项目内嵌子项目时希望硬编码子项目选项"的经典场景:父项目可以先set(SUBPROJ_OPT OFF),再add_subdirectory(...),子项目里的option(SUBPROJ_OPT ...)就不会覆盖父项目的设置。
3.2 OLD 行为与警告
在OLD行为下(CMake 3.12 及更早版本的历史行为),当缓存中不存在该条目(或存在但无类型,例如仅通过命令行-D<name>=ON设置过)时,option()会创建 BOOL 缓存条目并删除同名普通变量。
- 若策略为
WARN(默认兼容模式),且检测到同名普通变量已存在,命令会执行 OLD 行为并发出策略警告(cmOptionCommand.cxx),提示 "option is clearing the normal variable ..."。 - 测试 Tests/RunCMake/option/CMP0077-WARN.cmake 与 CMP0077-OLD.cmake、CMP0077-SECOND-PASS.cmake(分别覆盖首轮与后续轮次配置)共同验证了这些分支。
3.3 缓存变量已存在
源码中另一条关键路径是(cmOptionCommand.cxx):若缓存中已存在该名称且有类型的条目,option()只更新其HELPSTRING(帮助文本)然后返回,不会改变用户已经设置的值。这正是option()作为"用户可覆盖配置项"的本质——首次配置后,用户通过-D<name>=OFF/ON或 GUI 修改的值会在后续配置中被保留。
3.4 相关策略 CMP0126
CMP0126(CMake 3.21 引入)与CMP0077相似但针对set(CACHE):当策略为NEW时set(CACHE)不再删除同名普通变量。注意两者的关键差异(见 Help/policy/CMP0126.rst):
set(CACHE)在缓存条目原本不存在时总是会创建缓存变量,与CMP0126设置无关;- 而
option()在CMP0077=NEW且存在同名普通变量时不会创建缓存变量。
此外在CMP0126=NEW且CMP0077非 NEW 的组合下,option()创建缓存条目后会移除同名普通定义(cmOptionCommand.cxx),这也是容易踩坑的细节。
四、Project 模式与 Script 模式的差异
原文档强调:在 CMake project 模式(配置项目)下,option()创建的是 BOOL 类型缓存变量;在 script 模式(cmake -P脚本)下,创建的则是普通布尔变量。
两种模式的差异直接影响用法:
- Project 模式:值存入缓存(
CMakeCache.txt),用户可跨配置运行保留、通过-D或 GUI 修改,适合作为持久化的构建开关; - Script 模式:仅作为普通变量存在于脚本执行作用域,不进入缓存,适合一次性脚本内部逻辑判断。
从源码实现看,option()最终调用AddCacheDefinition(...)写入缓存(cmOptionCommand.cxx);脚本模式下没有持久化缓存,变量生命周期仅限于脚本执行过程。
五、命令行覆盖与缓存交互
option()与命令行-D的交互遵循以下规则:
- 首次配置时,若命令行指定了
-D<name>=ON/OFF,该值会先进入缓存;随后执行到option()时,因为缓存条目已存在且有类型,option()只更新帮助文本、不覆盖用户值; - 若命令行以无类型形式(如
-D<name>=blah,不带:BOOL后缀)传入,则缓存条目存在但无类型,此时option()会将其规范化为ON/OFF并补上 BOOL 类型(对应 CMP0077 OLD 路径的"缓存条目存在但无类型"分支); - 后续配置运行时,
option()永远不覆盖已持久化的用户值——这保证了构建配置的可重复性与用户控制权。
六、依赖选项:CMakeDependentOption 模块
原文档 See Also 指向 Modules/CMakeDependentOption.cmake。该模块提供的cmake_dependent_option()让布尔选项的可见性与默认值依赖于其他条件:
include(CMakeDependentOption) option(USE_SSL "Enable SSL in the project" OFF) cmake_dependent_option(USE_SSL_GNUTLS "Use GnuTLS for SSL" ON USE_SSL OFF)- 当条件为真时,创建 BOOL 缓存变量并显示在 GUI 中(内部实际调用
option()并FORCE写入缓存); - 当条件为假时,选项从 GUI 隐藏,并将同名局部变量设置为
<else-value>; - 条件在后缀配置中变假时,原值保留为
INTERNAL缓存条目、局部变量被覆盖为<else-value>;条件重新变真时,用户此前设置的值会被保留。
<condition>参数支持单个条件、分号分隔的条件列表,以及(CMake 3.22 起,受 CMP0127 策略约束)完整的if()条件语法,例如:
cmake_dependent_option(USE_FOO "Use Foo" ON "USE_A AND (USE_B OR USE_C)" OFF)其宏实现位于 Modules/CMakeDependentOption.cmake,通过cmake_language(EVAL CODE)逐条求值条件并据此切换选项的可见性与缓存类型。
七、最佳实践与常见陷阱
- 为每个选项提供清晰的帮助文本:它是 GUI 中唯一的解释来源,直接决定用户能否正确选择;
- 显式写出初始值:
option(FOO "desc")等价于option(FOO "desc" OFF),但显式书写意图更明确,也避免他人误读; - 利用"已存在则不动"语义做默认值覆盖:父项目通过普通变量预置子项目选项(结合
CMP0077=NEW)可避免子项目默认值覆盖父项目意图; - 区分普通变量与缓存变量:不要用
set(FOO ON)之后又写option(FOO ...)期待覆盖——在CMP0077=NEW下后者会被静默忽略,这是最常见的认知误区(测试 CMP0077-NEW.cmake 正是为了锁定该行为); - 警惕策略组合:
CMP0077与CMP0126组合时option()可能移除普通变量定义,若项目依赖该普通变量需显式设置策略; - 对旧版本兼容:若需在 CMake 3.12 及以下行为的环境中保持一致性,可在脚本中显式
cmake_policy(SET CMP0077 NEW),或通过 CMAKE_POLICY_DEFAULT_CMP0077 变量为第三方子项目统一设置策略而不修改其源码。
八、总结
option()表面是一个简单的布尔开关命令,其背后却涉及缓存变量类型、普通变量优先级、策略兼容(CMP0077 / CMP0126)与 project/script 模式差异等多层语义。理解 源码 中"已存在则仅更新帮助文本""按 CMP0077 决定是否忽略"两条核心路径,配合 RunCMake 测试套件 验证的行为契约,你就能精准控制构建开关的默认值、覆盖规则与 GUI 表现,让配置脚本既直观又具备良好的可复用性。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
OpenTaco高级配置技巧:自定义命令和缓存策略终极指南
OpenTaco高级配置技巧:自定义命令和缓存策略终极指南 OpenTaco作为一款开源基础设施即代码编排工具,提供了强大的自定义命令和智能缓存策略,让您的CI
DevOps云原生后端Brave浏览器终极缓存指南:HTTP缓存与自定义策略深度解析
Brave浏览器终极缓存指南:HTTP缓存与自定义策略深度解析 在当今快速发展的互联网时代,浏览器性能优化已成为用户体验的关键因素。Brave浏览器作为一款注重
桌面应用uv 缓存机制全解:缓存语义、tool.uv.cache-keys 配置、缓存清理与 CI 优化策略
uv 缓存机制全解:缓存语义、tool.uv.cache keys 配置、缓存清理与 CI 优化策略 uv 的激进的缓存策略是其“比 pip 更快”的核心支柱之
包管理器开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考