- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
CMP0194 是 CMake 4.1 引入的一项兼容性策略,它修正了 Windows 平台下启用ASM语言时误将 MSVC 的cl编译器当作汇编器的历史缺陷。本指南将围绕该策略的来龙去脉、OLD/NEW 两种行为差异、底层源码实现以及项目迁移方案展开讲解,读完你可以在自己的 Windows 构建工程中准确判断该策略的影响,并正确选择兼容或新行为。
策略背景:cl 为什么会被当成汇编器
在 CMake 中启用ASM语言时,CMake 会把 C 编译器的驱动程序(driver)作为汇编器候选来探测。这一设计在 GCC/Clang 生态中是合理的——gcc/clang既能编译 C 源码,也能直接汇编.s/.asm源文件。然而在 Windows + MSVC 工具链上,这一假设出了问题:
- CMake 4.0 及更早版本会意外地把 MSVC 的
cl编译器选为CMAKE_ASM_COMPILER; - 从而允许在 Windows 上成功启用
ASM语言,即使cl根本不支持汇编源文件; - 项目虽然配置成功,但一旦真正编译汇编源,就会失败或行为异常。
CMake 4.1 起,官方决定优先拒绝把cl作为汇编器候选(这是 CMP0194 的 NEW 行为)。但社区中存在不少在 Windows 上无条件启用ASM、却从不添加汇编源文件的项目,这些项目依赖旧行为才能继续配置通过。CMP0194 正是为这类项目提供兼容通道而引入的。
策略行为:OLD 与 NEW 的完整对比
依据 Help/policy/CMP0194.rst 的官方定义:
| 行为 | 含义 | 适用场景 |
|---|---|---|
OLD | 即使cl是唯一可用的候选,也成功启用ASM语言 | 在 Windows 上无条件enable_language(ASM)、但不真正编译汇编源的老项目 |
NEW | 不再把cl作为ASM语言的汇编器候选 | 使用真实汇编器(如ml/ml64宏汇编器),或通过交叉编译指定了其他汇编器的项目 |
从源码看,两种行为不仅影响"是否允许cl进入候选列表",还影响编译器身份(ID)识别的判定标准:
在 Modules/CMakeDetermineASMCompiler.cmake 中,NEW行为下 CMake 使用更严格的过滤正则:
if(_CMAKE_ASM_CMP0194 STREQUAL "NEW") set(_CMAKE_ASM_REGEX_MSVC "^(MSVC)$") set(_CMAKE_ASM_REGEX_CL "(^|/)[Cc]Ll") set(_CMAKE_ASM_MAYBE_CL "") else() # OLD:任何情况都不排除,且把 cl 加入候选 set(_CMAKE_ASM_REGEX_MSVC "CMP0194_OLD_MSVC_NOT_EXCLUDED") set(_CMAKE_ASM_REGEX_CL "CMP0194_OLD_MSVC_NOT_EXCLUDED") set(_CMAKE_ASM_MAYBE_CL "cl") endif()解读这段代码:
- NEW 行为:如果已加载的 C/C++ 编译器 ID 匹配
^(MSVC)$,或编译器路径匹配cl文件名正则(^|/)[Cc]Ll,则该编译器不会被放进CMAKE_ASM_COMPILER_LIST;默认候选列表里也不追加cl,而是从cc、gcc、xlc等真实汇编器/编译器驱动中探测。 - OLD 行为:
_CMAKE_ASM_REGEX_MSVC/_CMAKE_ASM_REGEX_CL被设置为一个不可能匹配的哨兵字符串(CMP0194_OLD_MSVC_NOT_EXCLUDED),相当于不做任何排除;同时cl被显式追加进默认候选列表,从而复现 CMake 4.0 及更早版本的行为。
编译器 ID 识别差异
OLD/NEW 还影响 MSVC 厂商识别(vendor detection)正则在 Modules/CMakeDetermineASMCompiler.cmake:
list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS MSVC) set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_MSVC "-?") if(_CMAKE_ASM_CMP0194 STREQUAL "NEW") # 仅当探测输出包含 “Macro Assembler” 字样(ml/ml64)才算 MSVC 厂商 set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_MSVC "Microsoft.*Macro Assembler") else() # OLD:任何 “Microsoft” 输出(包括 cl)都会被视为 MSVC 厂商 set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_MSVC "Microsoft") endif()也就是说,在 NEW 行为下,只有当候选汇编器运行-?参数输出包含Microsoft ... Macro Assembler(即真正的ml/ml64宏汇编器)时,其编译器 ID 才会被识别为MSVC;而cl的-?输出只有Microsoft (R) C/C++ Optimizing Compiler,不再匹配,从而被正确排除。
源码实现剖析:策略如何被读取与告警
CMP0194 的完整生命周期在仓库中由三处代码协作完成:
- 策略注册与默认告警级别:Source/cmPolicies.h 中注册了该策略:
SELECT(POLICY, CMP0194, "MSVC is not an assembler for language ASM.", 4, 1, 0, WARN)其中4, 1表示策略在 CMake 4.1 引入,WARN表示未显式设置该策略时 CMake 会发出警告。
- 策略读取:Modules/CMakeDetermineASMCompiler.cmake 在探测汇编器前执行:
cmake_policy(GET CMP0194 _CMAKE_ASM_CMP0194)将策略值存入内部变量_CMAKE_ASM_CMP0194,后续所有分支(候选列表构造、厂商识别)都依据该变量取值(NEW/OLD/ 空)决定行为。
- 警告触发:探测完成后,如果最终选出的汇编器 ID 恰为
MSVC且策略未设置(空值),则触发策略警告 Modules/CMakeDetermineASMCompiler.cmake:
if("ASM${ASM_DIALECT}" STREQUAL "ASM" AND CMAKE_ASM_COMPILER_ID STREQUAL "MSVC" AND _CMAKE_ASM_CMP0194 STREQUAL "") cmake_policy(ISSUE_WARNING CMP0194) endif()由此可以推断出完整的告警链路:cl在 OLD/未设置状态下被选为汇编器 → ID 被识别为 MSVC → 策略未设置 →cmake_policy(ISSUE_WARNING CMP0194)发出警告,同时按未设置时的默认行为(即 OLD)继续配置。
如何在项目中设置与切换该策略
CMP0194 可通过标准的策略机制进行设置(对应文档见 Help/policy/include/STANDARD_ADVICE.rst):
方式一:cmake_minimum_required(推荐)
在CMakeLists.txt顶部声明最小版本:
cmake_minimum_required(VERSION 4.1)由于 CMP0194 于 4.1 引入,cmake_minimum_required(VERSION 4.1)及以上会将所有 4.1 及更早的策略默认设为 NEW(包括 CMP0194)。如果你的项目本就不依赖cl充当汇编器,这是最干净的迁移路径。
方式二:cmake_policy(SET) 显式设置
# 维持旧行为:允许 cl 作为 ASM 汇编器候选(仅限确需兼容的场景) cmake_policy(SET CMP0194 OLD) # 采用新行为:排除 cl cmake_policy(SET CMP0194 NEW)对应的命令语法定义在 Help/command/cmake_policy.rst(cmake_policy(SET CMP<NNNN> NEW|OLD)与cmake_policy(GET CMP<NNNN> <variable>))。注意cmake_policy(SET ...)必须在任何受影响的命令执行之前调用,且应在目录顶层尽早完成。
方式三:CMAKE_POLICY_DEFAULT_CMP0194 外部覆盖
如果项目本身未更新、而你作为构建方希望临时决定策略走向,可在配置命令行通过缓存变量注入默认值(见 Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst):
# 强制采用新行为(排除 cl) cmake -S . -B build -DCMAKE_POLICY_DEFAULT_CMP0194=NEW # 保持旧行为并抑制警告 cmake -S . -B build -DCMAKE_POLICY_DEFAULT_CMP0194=OLD该变量专门用于外部指定"项目自身尚未更新"的策略默认值,官方文档明确建议项目不要在 CMake 代码中通过它来设置自己的策略,而应使用cmake_policy(SET)。
迁移建议与适用前提
结合策略定义与源码实现,可以给出以下决策路径:
- 项目在 Windows 上启用 ASM 并真正编译汇编源:应依赖
ml/ml64(MSVC 宏汇编器)或 MASM 方言(ASM_MASM),并显式cmake_policy(SET CMP0194 NEW)。此时cl被排除,ml/ml64会被正确识别为MSVC厂商(匹配Microsoft.*Macro Assembler正则)。 - 项目在 Windows 上无条件
enable_language(ASM)但从不添加汇编源:这是策略文档点名的典型存量场景。可选择保持 OLD 行为继续配置,但请注意:- OLD 行为按官方定义(Help/policy/include/DEPRECATED.rst)是被弃用的,未来版本可能被移除;
- 更稳妥的做法是去掉无用的
enable_language(ASM),或在代码中按平台条件启用,从根本上消除对cl充当汇编器的依赖。
- 交叉编译或使用独立汇编器:NEW 行为不影响通过
CMAKE_ASM_COMPILER/CMAKE_ASM_COMPILER_INIT显式指定的汇编器,候选列表仅在未显式指定时参与探测,可放心启用 NEW。
版本与告警行为汇总
| 属性 | 取值 | 依据 |
|---|---|---|
| 引入版本 | CMake 4.1 | Help/policy/CMP0194.rst 中.. versionadded:: 4.1 |
| 未设置时行为 | 发出警告,并按 OLD 处理 | Source/cmPolicies.h 中WARN标志 |
| NEW 核心变更 | cl不再作为ASM汇编器候选 | Modules/CMakeDetermineASMCompiler.cmake |
| OLD 语义 | cl可继续当选,ASM照常启用 | Modules/CMakeDetermineASMCompiler.cmake |
总而言之,CMP0194 是对 CMake 4.0 及更早版本在 Windows 平台ASM语言探测缺陷的一次行为修正,同时为存量项目保留了显式兼容开关。理解其 OLD/NEW 语义和 Modules/CMakeDetermineASMCompiler.cmake 中的正则逻辑,就能在升级到 CMake 4.1+ 时对 Windows 汇编语言支持做出准确、可控的决策。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
CMake 的 ASM\<DIALECT\> 环境变量:为不同汇编方言指定首选编译器的完整指南
CMake 的 ASM\<DIALECT\ 环境变量:为不同汇编方言指定首选编译器的完整指南 导读 ASM<DIALECT 是 CMake 中用于指定汇编语言方
构建工具开发工具CLICMake 策略 CMP0223 详解:空路径不再被视为任何路径的前缀
CMake 策略 CMP0223 详解:空路径不再被视为任何路径的前缀 导读 CMake 4.5 引入的 CMP0223 策略用于修正路径前缀判定的边界语义:在
构建工具开发工具CLICMake 策略 CMP0198 深度解析:CMakeLists.txt 中不再定义 CMAKE_PARENT_LIST_FILE
CMake 策略 CMP0198 深度解析:CMakeLists.txt 中不再定义 CMAKE_PARENT_LIST_FILE 导读 本文围绕 CMake
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考