构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载endif是 CMake 中最基础也最常用的块结构命令之一用于结束由if命令开启的条件命令块。本文以 endif 官方文档 为核心骨架系统讲解endif的语法、可选的向后兼容参数、与elseif/else的协作关系并结合本仓库源码cmIfCommand.cxx、cmListFileCache.cxx深入剖析if/endif配对的底层实现同时给出完整的条件表达式速查与实战示例帮助读者彻底掌握 CMake 条件分支的正确写法。endif 命令语法与核心作用endif命令的官方定义为结束if块中的命令列表Ends a list of commands in an if block。它的完整语法为endif([condition])其中condition参数是可选的仅用于向后兼容。如果提供该参数它必须是开头if子句中条件参数的逐字重复a verbatim repeat of the argument of the openingifclause即连空白、引号写法都必须完全一致。在绝大多数现代 CMake 工程中推荐写法是直接使用不带参数的endif()这样既清晰又避免出错。典型块结构一个标准的if/elseif/else/endif块如下语法定义见 if 命令文档if(condition) commands elseif(condition) # 可选可重复多次 commands else() # 可选 commands endif()执行流程为CMake 先求值if子句的condition若为真则执行if块内命令否则依次按相同方式处理可选的elseif块若所有条件都不为真则执行可选的else块。无论哪个分支被选中最终都统一由endif收尾闭合。向后兼容的 参数规则与陷阱从 if 命令文档 可知历史上else与endif都允许附带一个可选的条件参数这是为了兼容早期 CMake 脚本风格if(WIN32) message(STATUS Windows) endif(WIN32) # 参数必须与开头的 if 条件逐字相同现代 CMake 已不再要求也不鼓励这种写法官方文档明确该参数仅供向后兼容。同时else命令的可选参数同样仅用于向后兼容且不会被求值。源码如何校验参数匹配在 Source/cmIfCommand.cxx 中cmIfFunctionBlocker::ArgumentsMatch定义了endif与if的参数匹配规则bool cmIfFunctionBlocker::ArgumentsMatch(cmListFileFunction const lff, cmMakefile) const { return lff.Arguments().empty() || lff.Arguments() this-Args; }即当endif的参数为空Arguments().empty()或与打开该块的if命令的参数完全相等lff.Arguments() this-Args时才判定为匹配并闭合该块。这意味着endif()永远合法endif(WIN32)只有在配套的if(WIN32)时才匹配若endif带了一个与if条件不同的参数如if(WIN32) ... endif(UNIX)块无法正确闭合CMake 会报告配对错误。源码级实现if/endif 如何配对与执行if不是普通的即时执行命令而是通过Function Blocker函数阻塞器机制实现的。理解这一机制就能明白为什么endif的参数匹配如此严格。Function Blocker 机制在 cmIfCommand.cxx 中定义了一个cmIfFunctionBlocker它记录该if块的起始命令名与结束命令名class cmIfFunctionBlocker : public cmFunctionBlocker { public: cm::string_view StartCommandName() const override { return if_s; } cm::string_view EndCommandName() const override { return endif_s; } ... };if命令在求值条件后cmIfCommand 函数会把一个cmIfFunctionBlocker压入 cmMakefile::AddFunctionBlocker 维护的栈中只有当执行到endif且参数匹配时该阻塞器才会被弹出对应RemoveFunctionBlocker的实现见 cmMakefile.cxx。在块未闭合期间阻塞器决定哪些命令被跳过elseif/else分支切换或真正执行Replay逻辑见 cmIfCommand.cxx。解析阶段的嵌套校验在 CMake 读取脚本的解析阶段cmListFileCache.cxx 的 CheckNesting 函数 还会用栈结构校验if/elseif/else/endif的嵌套平衡} else if (name endif) { if (!TopIs(stack, NestingStateEnum::If) !TopIs(stack, NestingStateEnum::Else)) { return cmListFileContext::FromListFileFunction(func, this-FileName); } stack.pop_back(); }即endif只有在栈顶状态是If或Else时才被接受否则立即返回出错的上下文位置。elseif只能在If状态下出现else之后不允许再有elseif。这一机制保证了任何多出的endif或缺失的endif都会在解析期被精确定位报错。此外cmCommands.cxx 将endif注册为合法命令而在 cmMakefile.cxx 的 CMP0000 兼容命令白名单中if、endif、else、elseif均被列为永久的简单命令即使脚本未调用cmake_minimum_required也允许使用。条件表达式速查endif 所闭合的求值规则endif本身不参与条件求值但它闭合的if/elseif条件支持一整套表达式语法完整定义见 if 命令文档。掌握这些规则是正确使用if/endif的前提。复合条件的优先级条件按以下优先级求值括号(condition)最内层先求值一元测试存在性检查与文件操作二元测试比较、版本比较、路径比较、IN_LIST、IS_NEWER_THAN一元逻辑运算符NOT二元逻辑运算符AND、OR从左到右求值不短路。示例if((condition) AND (condition OR (condition))) ... endif()常量与真假判定真值常量大小写不敏感1、ON、YES、TRUE、Y以及任意非零数字含浮点数假值常量0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串以及以-NOTFOUND结尾的值变量已定义且值不是假常量时为真未定义则为假注意宏参数不是变量if(ENV{some_var})恒为假字符串带引号的字符串除非其值为真常量否则恒为假空参数if()恒为假。存在性检查表达式说明if(COMMAND name)名称是可调用的命令、宏或函数if(DEFINED name|CACHE{name}|ENV{name})变量/缓存变量/环境变量已定义CACHE{}支持自 3.14 起if(EXISTS path)文件或目录存在且可读解析符号链接if(TARGET name)已创建的逻辑目标add_executable/add_library/add_custom_targetif(TEST name)已由 add_test 创建的测试3.3 起if(POLICY id)存在CMPNNNN形式的策略if(x IN_LIST list)元素在列表变量中3.3 起文件操作与路径判断if(IS_READABLE path) # 3.29 起可读 if(IS_WRITABLE path) # 3.29 起可写 if(IS_EXECUTABLE path) # 3.29 起可执行 if(IS_DIRECTORY path) # 是否为目录 if(IS_SYMLINK path) # 是否为符号链接 if(IS_ABSOLUTE path) # 是否为绝对路径 if(file1 IS_NEWER_THAN file2) # 时间戳比较相同时为真注意文件类判断仅对显式完整路径有明确定义行为前导~/不会被展开为主目录。数值 / 字符串 / 版本比较数值比较按 Cdouble解析实数LESS、GREATER、EQUAL、LESS_EQUAL、GREATER_EQUAL后两者 3.7 起字符串比较字典序STRLESS、STRGREATER、STREQUAL、STRLESS_EQUAL、STRGREATER_EQUAL正则匹配if(var|str MATCHES regex)括号分组会捕获到CMAKE_MATCH_n变量版本比较VERSION_LESS、VERSION_GREATER、VERSION_EQUAL及_EQUAL变体格式为major[.minor[.patch[.tweak]]]缺失组件按 0 处理非整数部分会被截断。路径比较PATH_EQUAL / PATH_IS_PREFIXPATH_EQUAL3.24 起按组件逐段比较路径而不访问文件系统多个路径分隔符会被折叠PATH_IS_PREFIX4.5 起判断左侧路径是否是右侧路径的前缀注意它是纯词法判断不做归一化# PATH_EQUAL 比较结果为 TRUE而 STREQUAL 为 FALSE if (/a//b/c PATH_EQUAL /a/b/c) ... endif() # PATH_IS_PREFIX 判断 if (/a/b PATH_IS_PREFIX /a/b/c) ... endif()变量自动求值Variable Expansionif命令诞生早于${}语法为方便起见会自动对其参数中命名的变量求值详见 if 文档的 Variable Expansion 一节。例如set(var1 OFF) set(var2 var1) if(${var2}) # 等价于 if(var1)结果为假 if(var2) # 直接写变量名var2 已定义且值不是假常量结果为真自 3.1 起配合策略 CMP0054用引号或括号包裹的变量名会被当作字符串而非解引用。环境变量与缓存变量不会自动求值必须显式写$ENV{name}或$CACHE{name}。嵌套使用与配对注意事项if/endif块可以自由嵌套源码的CheckNesting栈机制正是为处理任意深度的嵌套设计的。嵌套时必须保证每个if都有对应的endifif(BUILD_SHARED_LIBS) if(MSVC) message(STATUS Building shared libs with MSVC) endif() else() message(STATUS Building static libs) endif()实践建议统一使用endif()形式避免带参数写法带来的拼写不一致风险条件较长时可用endif()加行尾注释说明闭合的是哪个条件例如endif() # BUILD_SHARED_LIBS但注意注释不能替代参数的逐字匹配规则在函数function/endfunction与宏macro/endmacro内部使用if/endif时同样遵循本规则且函数内未闭合的块会导致语法错误。常见错误与排查方法缺失 endifCMake 在解析阶段通过CheckNesting检测到栈不平衡报错信息会精确指向未闭合的if所在文件与行号多余的 endif栈顶不是If/Else状态时被拒绝提示该endif没有对应的ifendif 参数不匹配如if(WIN32) ... endif(UNIX)ArgumentsMatch返回假块无法闭合而报错else 后再用 elseif源码中Replay逻辑cmIfCommand.cxx会抛出 A duplicate ELSE command 或 An ELSEIF command was found after an ELSE command 的致命错误条件类型混淆例如用STREQUAL与数字比较混用、未加引号的字符串被当作变量解引用可参考上文条件表达式速查逐一核对。调试时可用cmake --trace跟踪命令执行if分支的实际走向会在跟踪输出中体现。总结endif虽只有一行语法却是 CMake 条件逻辑的收口点。正确理解其三点核心无参数的标准用法、仅供向后兼容且必须逐字匹配的可选参数、以及源码层面基于 Function Blocker 栈与解析期嵌套校验的双重配对机制cmIfCommand.cxx、cmListFileCache.cxx有助于写出结构清晰、跨版本健壮的 CMake 脚本。配合 if 命令文档 中完整的条件表达式体系即可自如地驾驭 CMake 中一切条件分支场景。赞分享构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载相关推荐CMake 条件控制流详解else 命令与 if/elseif/else/endif 块机制CMake 条件控制流详解else 命令与 if/elseif/else/endif 块机制 导读 else 是 CMake 条件控制流中的关键命令用于启动构建工具开发工具CLICMake endfunction 命令详解函数块收尾、向后兼容参数与底层实现机制CMake endfunction 命令详解函数块收尾、向后兼容参数与底层实现机制 endfunction 是 CMake 中与 function 成对出现的构建工具开发工具CLICMake elseif 命令全解if 块分支条件求值与源码级实现原理CMake elseif 命令全解if 块分支条件求值与源码级实现原理 导读 elseif 是 CMake 中用于在 if 块内追加备选分支的关键命令它让构构建工具开发工具CLI上一篇SOCD Cleaner终极指南4种智能模式彻底解决键盘输入冲突下一篇Hitboxer深度解析5大核心功能彻底解决游戏键盘输入冲突创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考