- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
本指南以 CMake 官方策略文档 Help/policy/CMP0025.rst 为核心,系统讲解 CMake 3.0 引入、4.0 移除的兼容性策略 CMP0025:为什么 Apple Clang 不再与上游 Clang 共用同一编译器标识(Compiler ID)。文章将说明 OLD/NEW 两种行为的差异、设置时机与方式、源码级实现验证,以及项目在升级到 CMake 4.0 前后应如何正确检测 Apple Clang,帮助开发者避免因CMAKE_<LANG>_COMPILER_ID取值变化而引发的编译逻辑错误。
CMP0025 要解决的核心问题
在 CMake 3.0 之前,Apple Clang(即 Apple 基于 Clang 定制的编译器,随 Xcode 工具链分发)在 CMake 中统一被识别为Clang。然而 Apple Clang 与上游 Clang(LLVM 官方版本)在版本号体系、feature 支持、链接器行为等方面并不一致——它们的版本号是各自独立编号的。
CMake 3.0 开始认识到二者是不同的编译器,因此更倾向于在CMAKE_<LANG>_COMPILER_ID变量中报告AppleClang而非Clang。但既有项目可能仍然假设 Apple Clang 的编译器标识就是Clang(与 CMake 3.0 之前的行为一致)。CMP0025 正是为了解决这一兼容性分歧而设立:
Compiler id for Apple Clang is now
AppleClang.
该策略决定:在语言<LANG>被 project() 或 enable_language() 命令启用之后,CMAKE_<LANG>_COMPILER_ID变量对 Apple Clang 应报告哪种编译器标识。
OLD 与 NEW 行为对照
| 行为 | 报告给CMAKE_<LANG>_COMPILER_ID的值 | 适用场景 |
|---|---|---|
OLD | Clang | 维持 CMake 3.0 之前旧项目的既有假设,兼容基于Clang字符串做分支判断的旧逻辑 |
NEW | AppleClang | 让项目区分 Apple 定制 Clang 与上游 Clang,按各自版本号与特性做差异化处理 |
其中<LANG>可以是C、CXX、OBJC、OBJCXX等 CMake 支持的语言。政策文档特别强调:该策略必须在project()或enable_language()命令调用之前设置,否则语言启用后编译器标识已被写入缓存,策略将无法再影响其结果。
如何设置 CMP0025
方式一:通过 cmake_minimum_required 自动启用
cmake_minimum_required(VERSION 3.0)当cmake_minimum_required()指定的版本不低于 3.0 时,CMake 会自动把 CMP0025 置为NEW。这是绝大多数新项目采用的方式——从 Source/cmPolicies.h 的策略注册表可见,CMP0025 在 CMake 3.0 引入时默认行为即为NEW:
SELECT(POLICY, CMP0025, "Compiler id for Apple Clang is now AppleClang.", 3, 0, 0, NEW)方式二:通过 cmake_policy 显式控制
cmake_policy(SET CMP0025 NEW) # 或 OLD注意:若项目中同时使用cmake_minimum_required()与cmake_policy(SET ...),后者的显式设置优先;且两种方式都必须位于project()/enable_language()之前才有效。
查看当前策略状态
if(POLICY CMP0025) cmake_policy(GET CMP0025 policy_status) message(STATUS "CMP0025 status: ${policy_status}") endif()版本演进与警告控制
CMP0025 的生命周期可概括为三个阶段:
- 引入:CMake 3.0(
INTRODUCED_IN_CMAKE_VERSION为 3.0); - 默认不警告:在 4.0 移除之前,若项目未显式设置该策略,CMake默认不发出警告,并采用
OLD行为(即报告Clang),以保证旧项目平滑运行; - 移除:CMake 4.0(
REMOVED_IN_CMAKE_VERSION为 4.0)。自 4.0 起,OLD行为被彻底删除,策略必须通过cmake_minimum_required()或cmake_policy()显式设为NEW,见 Help/policy/include/REMOVED_PROLOGUE.rst 与 Help/policy/include/REMOVED_EPILOGUE.rst 中的通用模板说明。
在 CMake 4.0 之前的版本中,若需要控制该策略的警告(尽管默认不警告),可通过变量CMAKE_POLICY_WARNING_CMP0025(属于CMAKE_POLICY_WARNING_CMP<NNNN>系列)进行开关:
# 关闭 CMP0025 相关策略警告(仅在确实需要时使用) set(CMAKE_POLICY_WARNING_CMP0025 FALSE)源码级实现验证
1. 策略注册表
Source/cmPolicies.h中 CMP0025 的SELECT条目完整记录了策略编号、标题、引入版本(3.0)与默认行为(NEW)。这是 CMake 生成策略警告信息、查询策略状态的元数据来源。
2. 编译器标识的定义
CMAKE_<LANG>_COMPILER_ID变量的取值表见 Help/variable/CMAKE_LANG_COMPILER_ID.rst,其中明确列出:
| 编译器标识 | 对应编译器 |
|---|---|
AppleClang | Apple Clang |
Clang | Clang 系列(含 Apple Clang 之前的归属) |
3. AppleClang 独立模块族
仓库中为 Apple Clang 建立了独立的编译器模块与链接器模块,这是其区别于上游 Clang 的具体工程实现:
- 编译器模块:
Modules/Compiler/AppleClang-C.cmake、Modules/Compiler/AppleClang-CXX.cmake、Modules/Compiler/AppleClang-CXX-FeatureTests.cmake、Modules/Compiler/AppleClang-C-FeatureTests.cmake、Modules/Compiler/AppleClang-OBJC.cmake、Modules/Compiler/AppleClang-OBJCXX.cmake等; - 链接器模块:
Modules/Platform/Linker/Apple-AppleClang.cmake及其按语言派生的Apple-AppleClang-C.cmake、Apple-AppleClang-CXX.cmake、Apple-AppleClang-Fortran.cmake、Apple-AppleClang-Swift.cmake、Apple-AppleClang-CUDA.cmake等。
这些模块分别承载 Apple Clang 特有的编译选项、特性测试与链接规则,说明编译器标识的分化在 CMake 内部不仅是字符串层面的区别,而是贯穿编译与链接的全套行为差异。
4. 前端变体归类
在 Modules/CMakeDetermineCompilerId.cmake 中,AppleClang与GNU、FujitsuClang、IBMClang、TIClang等并列归入CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT = "GNU"分支——即 Apple Clang 沿用了 GCC 风格的前端变体,这与上游 Clang 的处理一致,也印证了二者共享大量 GNU 兼容命令行选项的事实:
elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xGNU" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xAppleClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xFujitsuClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xIBMClang" OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xTIClang") set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "GNU")5. 同类策略的兼容性换算模式
在 Source/cmGlobalGenerator.cxx 的CheckCompilerIdCompatibility()中可以看到与 CMP0025 同构的处理模式:当编译器标识为XLClang时,依据 CMP0089 的策略状态决定是否将其转换为XL(OLD 转换、NEW 保留);LCC依据 CMP0129 决定是否转换为GNU。可以推断,CMP0025 的 OLD 行为在早期实现中同样遵循"把 AppleClang 换算回 Clang 字符串"的兼容逻辑,而 NEW 行为则直接保留真实标识。
对项目实战的影响与迁移建议
旧代码中常见的假设问题
CMake 3.0 之前编写的项目或第三方模块中,常见这类判断:
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # 期望同时覆盖 Apple Clang 与上游 Clang endif()在 CMake 3.0+ 且启用 CMP0025 NEW 后,Apple Clang 报告为AppleClang,上述判断将不再命中,可能导致本应施加的编译选项、-stdlib选择、警告标志等被跳过。这是迁移到 NEW 行为时最需要排查的回归点。
推荐的新写法
如需同时覆盖两类编译器,应显式列出两个标识;如需区分 Apple 定制版,则单独分支:
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # 统一处理 Clang 与 AppleClang elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # 仅 Apple 定制 Clang 专属逻辑(如版本号按 Xcode 工具链判断) endif()版本号读取的注意事项
政策文档明确指出 Apple Clang 与上游 Clang版本号体系不同。因此依赖编译器版本号做条件判断的代码,应使用CMAKE_<LANG>_COMPILER_VERSION的同时明确其来源是 Apple 的版本序列(如 1500、1600 等),避免用上游 Clang 的版本阈值(如 12/13/14)去推断 Apple Clang 的能力。
升级到 CMake 4.0 前的检查清单
- 确认
cmake_minimum_required(VERSION 3.0)或更高版本已声明(自动启用 NEW); - 若项目仍依赖
Clang标识命中 Apple Clang,需在 CMake 4.0 之前完成分支逻辑改写——因为 4.0 移除 OLD 行为后,Apple Clang只会被报告为AppleClang; - 检查缓存文件与生成脚本中是否硬编码了
Clang字符串的编译器判断; - 使用
cmake_policy(GET CMP0025 ...)或--debug-policies命令行选项验证当前生效的策略状态。
小结
CMP0025 是 CMake 编译器标识体系分化过程中的关键策略:它让 Apple Clang 从Clang中独立出来,获得自己的标识、版本号与专属编译/链接模块。对开发者而言,理解 OLD/NEW 差异、把握"必须在project()之前设置"的时机要求,并掌握 4.0 移除后的显式 NEW 约束,是确保项目在 Apple 平台工具链上长期正确构建的必备知识。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
CMake跨编译器支持策略:Clang/GCC/MSVC的兼容性处理
CMake跨编译器支持策略:Clang/GCC/MSVC的兼容性处理 你是否曾在项目中遇到过"代码在Clang编译正常,GCC却报错"或"Windows下MSV
构建工具开发工具CLIFreeOTP-Android安全机制揭秘:Android Keystore加密存储技术详解
FreeOTP Android安全机制揭秘:Android Keystore加密存储技术详解 FreeOTP Android是一款开源的双因素认证应用,它采用
CMake-examples项目解析:使用Clang编译器构建C++项目
CMake examples项目解析:使用Clang编译器构建C++项目 概述 在C/C++项目开发中,编译器选择对项目构建至关重要。本文基于cmake exa
示例工程教程构建工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考