- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
导读
endblock()是 CMake 3.25 起随block()命令一同引入的配套命令,用于结束一组由block()开启的命令列表,并移除该块创建的所有作用域(变量、策略、诊断)。本文以 Help/command/endblock.rst 为骨架,结合block()官方文档、block 源码实现 与 RunCMake/block 测试集,完整讲解endblock()的语法规则、作用域生命周期、参数约束、嵌套校验机制以及break()/continue()/return()的交互行为,帮助你写出作用域隔离、行为可预期的 CMake 脚本。
1. 基本语法与核心作用
官方文档对endblock的定义只有一句话:它结束block()中的一组命令,并移除由block()创建的作用域。其语法形式为:
endblock()几个关键事实:
- 引入版本:
block()/endblock()均在 CMake 3.25 中新增(见 Help/command/endblock.rst 与 Help/command/block.rst)。 - 不接受任何参数:
endblock()必须无参调用。源码中cmBlockFunctionBlocker::EndCommandSupportsArguments()返回false,cmFunctionBlocker.cxx 会检测到带有参数的endblock(...)并报错 "A logical block closing on the line ... has unexpected arguments.";Tests/RunCMake/block/EndWithArgument.cmake 正是针对该错误路径的测试。 - 不能单独出现:
endblock()必须与block()配对使用。解析阶段会用嵌套栈校验配对关系(详见第 4 节)。
2. 与 block() 的配对工作流程:先记录、后回放
block()与endblock()的协作机制是理解本命令的关键。按照 Help/command/block.rst 的说明:
block()与匹配的endblock()之间的所有命令只被记录、不会立即执行;- 当
endblock()被求值时,记录的命令列表才在指定的作用域内被逐一调用; - 之后,
block()创建的作用域被移除。
这一"记录–回放"机制在源码中有非常清晰的体现:
- cmBlockCommand.cxx 的
Replay()遍历收集到的cmListFileFunction并逐一调用mf.ExecuteCommand(fn, status),从而在endblock()到达时真正执行块内命令; - 回放期间会透传
return()、break()、continue()以及致命错误/退出码状态(详见第 5 节); - 块内收集到的命令由 cmFunctionBlocker.cxx 的
Functions向量暂存,block()/endblock()的嵌套深度通过ScopeDepth计数器维护,深度归零时移除函数拦截器并触发回放。
因此,endblock()表面上是"结束标记",实际承担了触发作用域内命令回放与作用域清理的双重职责。
3. 作用域生命周期:block 创建,endblock 移除
block()可以通过SCOPE_FOR指定要创建的作用域类型,不指定时等价于:
block(SCOPE_FOR VARIABLES POLICIES DIAGNOSTICS)三种作用域与离开块时的行为如下表所示:
| SCOPE_FOR 取值 | 等价操作(开启) | 离开 block 时(由 endblock 触发) | 引入版本 |
|---|---|---|---|
VARIABLES | 创建新的变量作用域 | 变量作用域弹出,块内新定义/修改的变量不污染外层 | 3.25 |
POLICIES | cmake_policy(PUSH) | 自动cmake_policy(POP)恢复外层策略设置 | 3.25 |
DIAGNOSTICS | cmake_diagnostic(PUSH) | 自动cmake_diagnostic(POP)恢复诊断配置 | 4.4(见 Help/command/block.rst) |
从源码实现看,cmBlockCommand.cxx 的BlockScopePushPop会根据ScopeSet分别构造cmMakefile::VariablePushPop、PolicyPushPop、DiagnosticPushPop三个 RAII 助手类(定义见 cmMakefile.h);这些对象在cmBlockFunctionBlocker析构时自动析构,从而在endblock()回放完成后统一弹出对应作用域。
3.1 PROPAGATE:把块内变量带回父作用域
变量作用域默认会把块内改动隔离在外层,若确实需要将某些变量"带出去",使用PROPAGATE选项:
set(var1 "INIT1") set(var2 "INIT2") set(var3 "INIT3") block(PROPAGATE var1 var2) set(var1 "VALUE1") unset(var2) set(var3 "VALUE3") endblock() # 现在 var1 的值为 VALUE1,var2 已解除定义,var3 仍保持初始值 INIT3官方文档明确了两条规则(见 Help/command/block.rst):
PROPAGATE等价于对每个列出的变量执行set(... PARENT_SCOPE)或unset(... PARENT_SCOPE);- 仅当
block()创建了变量作用域时才能使用PROPAGATE,否则报错。源码在 cmBlockCommand.cxx 中对应检查:PROPAGATE cannot be specified without a new scope for VARIABLES。
其底层实现位于 cmMakefile.cxx 的RaiseScope(std::vector<std::string> const&):对每个变量,若块作用域内仍处于定义状态则把值提升到父作用域,否则在父作用域执行解除定义。cmBlockFunctionBlocker的析构函数(cmBlockCommand.cxx)在离开块时调用该RaiseScope,因此PROPAGATE的效果在endblock()处生效。测试用例 Tests/RunCMake/block/Workflows.cmake 验证了"块内 set + PROPAGATE 回传、块内未列出的变量不污染外层"的完整行为。
4. 嵌套与配对校验:让错误在解析期暴露
block/endblock支持任意嵌套,配对合法性在解析阶段即被检查。cmListFileCache.cxx 在解析命令时维护一个嵌套状态栈:遇到block压栈NestingStateEnum::Block,遇到endblock时若栈顶不是Block则返回出错上下文(缺少匹配的block),否则弹栈。
对应测试覆盖了各种非法配对场景(Tests/RunCMake/block/):
EndAlone.cmake:孤立出现的endblock();EndMissing.cmake:有block()但没有endblock()闭合;EndWithArgument.cmake:endblock(END_ARG)带参数;InvalidNesting1.cmake~InvalidNesting6.cmake:与function/macro/foreach/while/if等嵌套结构交叉错配。
运行时若在错误位置遇到endblock(),cmFunctionBlocker.cxx 还会发出CMD_AUTHOR级别的诊断信息,指明闭合行并说明存在意外参数。
5. 与 break() / continue() / return() 的交互
block()出现在foreach()或while()内部时,break()与continue()可以在块内直接使用(Help/command/block.rst):
while(TRUE) block() # ... # break() 会终止 while() 循环 break() endblock() endwhile()源码层面的处理在Replay()中:回放块内命令时,一旦检测到break或continue被调用,立即终止回放并把对应状态向上传播(cmBlockCommand.cxx)。同样地,return()在块内被调用时,会先通过RaiseScope把return()的返回值变量提升到外层,再向外传播返回状态(cmBlockCommand.cxx)。Tests/RunCMake/block/Workflows.cmake 分别用while+break、function+return、foreach+continue三种组合验证了这一行为,例如continue()会跳过块内其后的set(VAR${id} "BAD${id}")语句。
6. 典型使用场景与建议
- 临时作用域隔离:在大型脚本中为局部计算开辟独立变量作用域,避免临时变量泄漏到文件级/目录级作用域;需要回传结果时用
PROPAGATE显式列出变量名。 - 策略临时调整:
block(SCOPE_FOR POLICIES)内临时设置某个策略,endblock()后自动恢复,无需手工cmake_policy(PUSH/POP)配对。 - 诊断配置局部化:CMake 4.4 起可结合
SCOPE_FOR DIAGNOSTICS在块内局部调整诊断严格度,离开块自动恢复。 - 配合循环控制:在
foreach/while循环体中使用block()包裹逻辑,仍可正常使用break()/continue()。
7. 小结
endblock()虽然只占一行、不接受参数,但它承担着"回放块内记录的命令、弹出block()创建的作用域、并触发PROPAGATE变量回传"的收尾职责。理解它需要掌握三件事:块内命令"先记录、后回放"的执行模型(见 cmBlockCommand.cxx)、作用域由 RAII 助手类管理的生命周期(见 cmMakefile.h)、以及解析期嵌套栈对配对合法性的保证(见 cmListFileCache.cxx)。配合 Tests/RunCMake/block/ 中的各类用例,可以进一步验证边界行为。
参见
- Help/command/block.rst:
block()命令的完整选项(SCOPE_FOR、PROPAGATE)与示例; - Source/cmBlockCommand.cxx:
block/endblock的函数拦截器与回放实现; - Source/cmFunctionBlocker.cxx:拦截、深度计数、错误诊断与回放触发逻辑;
- Help/command/return.rst、Help/command/cmake_policy.rst:与
endblock密切相关的控制流命令。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
浏览器里解密加密音乐:Unlock-Music 三步上手教程
浏览器里解密加密音乐:Unlock Music 三步上手教程 先说一个有点反常识的结论:解密加密音乐文件,你不用安装任何软件。Unlock Music 是一个完
构建工具开发工具CLIkomorebi close-workspace 命令详解:关闭并回收空工作区的正确姿势
komorebi close workspace 命令详解:关闭并回收空工作区的正确姿势 导读 close workspace 是 komorebi 窗口管理器
桌面应用Linux 终端会话回放实战:scriptreplay 命令详解(linux-command 命令手册)
Linux 终端会话回放实战:scriptreplay 命令详解(linux command 命令手册) scriptreplay 是 Linux 上配合 sc
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考