- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
导读
CMP0047 是 CMake 自 3.0 起引入的一项兼容性策略,它解决了一个在 QNX 嵌入式开发中非常具体的问题:当使用 QNX 的qcc编译器驱动时,CMake 应当把CMAKE_<LANG>_COMPILER_ID报告为QCC还是旧有的GNU。本文以 CMP0047.rst 文档为主体,结合本仓库中 cmPolicies.h 的策略注册与 Modules/Compiler/QCC-*.cmake 的编译器实现,完整讲解该策略的 OLD/NEW 行为、设置时机、迁移方式以及 QNX 工具链的底层细节。读完本文,你将掌握如何在旧项目与现代 CMake 之间正确对齐 QNX 交叉编译的 Compiler ID,并理解这一变更对特性检测、编译旗标选择的影响。
一、策略背景:为什么 qcc 需要自己的 Compiler ID
QNX 实时操作系统(RTOS)的官方编译器驱动名为qcc(针对 C/C++,其底层通常封装了 GCC 兼容的代码生成后端)。在 CMake 3.0 之前,CMake 只把qcc当作 GNU 编译器家族的一员处理,CMAKE_<LANG>_COMPILER_ID一律报告为GNU。这种简化在早期可行,但存在隐患:
qcc驱动在命令行语义上与裸gcc有明显差异(例如 C++ 语言模式需要显式传入-lang-c++或-x c++这样的驱动级旗标,而不是由 gcc 默认推断);- 项目无法通过 Compiler ID 精确区分"真正的 GCC"和"QNX 的 qcc 封装",导致针对性的工具链适配逻辑无从下手。
CMake 3.0 起,CMake 正式承认 QNXqcc驱动与 GNU 编译器是两回事,并引入独立编译器 IDQCC(参见CMAKE_ _COMPILER_ID变量文档中QCC— "QNX C/C++ compiler" 这一取值)。但考虑到存量项目可能依然假设 qcc 的 ID 是GNU,直接改默认值会破坏这些项目的条件判断,于是通过策略 CMP0047 让新旧行为平滑过渡。
二、策略定义:CMP0047 的 OLD 与 NEW 行为
根据 CMP0047.rst,该策略的语义如下:
| 行为 | 效果 |
|---|---|
OLD(旧行为) | 对 qcc 与 QCC 两类编译器驱动,CMAKE_<LANG>_COMPILER_ID报告为GNU |
NEW(新行为) | 对 qcc 与 QCC 两类编译器驱动,CMAKE_<LANG>_COMPILER_ID报告为QCC |
该策略在语言<LANG>被 project() 或 enable_language() 命令启用之后,决定CMAKE_<LANG>_COMPILER_ID变量中报告哪一个 Compiler ID。
在 CMake 源码中,该策略注册于 cmPolicies.h,注册摘要字符串即为"Use QCC compiler id for the qcc drivers on QNX.",与文档标题完全一致。策略注册位于cmPolicies.h的策略表中,意味着它在 CMake 的cmake_minimum_required版本判定体系内是标准策略之一,可通过常规的 cmake_policy() 机制被读取与设置。
关键约束:必须在使用前设置
文档明确指出:该策略必须在project()或enable_language()被调用之前设置,因为语言启用过程会读取编译器信息、写入CMAKE_<LANG>_COMPILER_ID,策略在那一刻才决定用哪个 ID。错过时机再调用cmake_policy(SET CMP0047 ...)将不会对已启用的语言生效。
推荐的设置方式:
cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0047 NEW) # 显式选择 QCC 编译器 ID project(MyQnxProject C CXX) # 语言启用发生在策略设置之后或直接依赖版本声明隐式启用:
cmake_minimum_required(VERSION 3.5) # >= 3.0 的策略默认即 NEW需要说明:在 CMake 3.0 至 4.0 之前的版本中,若项目没有显式设置该策略,CMake 会按默认策略行为处理,并可通过CMAKE_POLICY_WARNING_CMP0047变量(参见 CMAKE_POLICY_WARNING_CMP )控制是否输出策略警告。默认情况下该策略在 3.0 引入时不主动告警(文档中WARNED_OR_DID_NOT_WARN替换为 "didnotwarn by default")。
三、策略生命周期:CMake 4.0 起 OLD 行为被移除
文档开头的序言(include 自 REMOVED_PROLOGUE.rst)表明:该策略的OLD行为已在CMake 4.0中被移除,策略必须通过cmake_minimum_required或cmake_policy设置为NEW。
这带来两个直接影响:
- 最低版本 ≥ 4.0 的项目:
cmake_minimum_required(VERSION 4.0)会让 CMP0047 自动处于NEW状态,QNX 下 Compiler ID 必然是QCC,无需额外处理; - 最低版本 < 4.0 的存量项目:若代码中仍存在针对
GNUID 的 QNX 分支(例如if(CMAKE_C_COMPILER_ID STREQUAL "GNU")),在升级 CMake 到 4.0 后该分支将不再命中,需要迁移为检查QCC。
这正是该策略与其他"已移除 OLD 行为"策略(如 CMP0001–CMP00xx 系列中同样标注 REMOVED 的策略)一致的演进路径:先以默认旧行为兼容存量,再通过警告提醒,最终在新大版本中强制新行为。
四、源码纵深:QCC 编译器模块在仓库中的真实实现
策略 CMP0047 的落地不止于cmPolicies.h中的一条注册记录,还配套了一整套以QCC命名的编译器模块。在 Modules/Compiler 目录下可以看到:
QCC-C.cmake、QCC-CXX.cmake、QCC-ASM.cmake:各语言的编译/链接规则入口QCC-C-FeatureTests.cmake、QCC-CXX-FeatureTests.cmake:语言特性探测QCC.cmake:公共宏__compiler_qcc的定义
以 QCC-C.cmake 为例,其实现非常精简:
# To include compiler feature detection include(Compiler/GNU-C) include(Compiler/QCC) __compiler_qcc(C)注意第一行:QCC 的 C 语言特性检测直接复用了Compiler/GNU-C。这一点与策略背景相互印证——qcc 的代码生成后端与 GCC 兼容,因此绝大多数 GNU 编译旗标与特性宏依然适用,但 Compiler ID 却独立为QCC,使项目既能复用 GCC 特性语义,又能识别出驱动差异。
QCC-CXX.cmake 则展示了更实质的驱动差异——C++ 语言模式旗标随 qcc 版本变化:
# If the toolchain uses qcc for CMAKE_CXX_COMPILER instead of QCC, the # default for the driver is not c++. if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.2.0") # QNX 8.0 toolchain set(_cmake_qcc_cxx_lang_compile_flag "-lang-c++") set(_cmake_qcc_cxx_lang_link_flag "-lang-c++") else () set(_cmake_qcc_cxx_lang_compile_flag "-x c++") set(_cmake_qcc_cxx_lang_link_flag "") endif () set(CMAKE_CXX_COMPILE_OBJECT "<CMAKE_CXX_COMPILER> ${_cmake_qcc_cxx_lang_compile_flag} <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> ${_cmake_qcc_cxx_lang_link_flag} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")从中可以读出两个关键事实:
- 驱动不是 gcc:qcc 驱动默认并不自动选择 C++ 语言模式,CMake 必须显式传入语言旗标,这正是 Compiler ID 必须区别于
GNU的底层原因; - 版本分支:qcc 12.2.0(QNX 8.0 工具链)之前的版本使用
-lang-c++,之后的版本使用-x c++且链接阶段无需语言旗标。这说明CMAKE_CXX_COMPILER_VERSION在 QNX 工具链上同样可用,项目可以安全地用它做版本判断。
此外,QCC-ASM.cmake 仅包含include(Compiler/QCC)与__compiler_qcc(ASM)两行,表明 QCC 编译器 ID 同样适用于 ASM 语言;而 Modules/Platform/QNX-Initialize.cmake 则负责 QNX 平台层的初始化,与编译器层的 QCC 模块协同完成整套 QNX 交叉编译支持。
五、实战:迁移旧项目到 QCC Compiler ID
假设你有一个面向 QNX 的存量 CMake 工程,其配置文件中曾有如下分支:
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # 误把 qcc 当成 GNU 处理的旧逻辑 add_compile_options(-fno-exceptions) endif()在 CMake 3.0+ 中,若项目仍隐式处于 CMP0047 的 OLD 行为,上述分支在 qcc 下依然命中,但语义是"碰巧"的;一旦切到 NEW 行为或升级到 CMake 4.0,该分支将失效。正确迁移步骤:
- 声明策略:在
project()之前显式cmake_policy(SET CMP0047 NEW),或将cmake_minimum_required提升到 3.0 以上(推荐直接显式 SET,明确意图); - 改写条件:将所有针对 QNX 工具链的
GNU判断改为QCC:
if(CMAKE_CXX_COMPILER_ID STREQUAL "QCC") # QNX qcc 专用逻辑,例如: add_compile_options(-fno-exceptions) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # 真正的桌面/嵌入式 GCC 逻辑 endif()- 验证 Compiler ID:在配置阶段打印确认:
message(STATUS "QNX Compiler ID = ${CMAKE_CXX_COMPILER_ID}") message(STATUS "QNX Compiler Version = ${CMAKE_CXX_COMPILER_VERSION}")- 回归测试特性检测:由于 QCC-C/CXX 模块复用了 GNU 的特性测试(见 QCC-C-FeatureTests.cmake),
check_cxx_compiler_flag、write_compiler_detection_header等依赖 Compiler ID 的机制会以QCC作为 ID 前缀生成检测头,请同步检查任何硬编码了GNU的检测头消费代码。
常见问题
- Q:不设置该策略会怎样?在 CMake 3.x–3.27 等版本中默认沿用旧行为(报告
GNU),策略本身默认不告警,因此存量项目不会立即报错;但跨过 CMake 4.0 后旧行为被移除,编译器 ID 会突然变为QCC,提前迁移可避免升级冲击。 - Q:
CMAKE_CXX_COMPILER_ID为QCC与CMAKE_CXX_COMPILER指向qcc/QCC有区别吗?Compiler ID 是 CMake 通过编译探测(CompilerId 工程)识别出的厂商标识,qcc与QCC两个驱动都会得到QCC这个 ID;而QCC-CXX.cmake中的版本分支正是为了兼容"驱动名为 qcc 但版本口径不同"的两种工具链形态。 - Q:策略设置时机为什么如此严格?因为
project()/enable_language()在启用语言的同时完成编译器探测并固化CMAKE_<LANG>_COMPILER_ID,CMP0047 正是这一探测结果的"显示器",自然必须先行设置。这一约束在 CMP0047.rst 文档中已明确强调。
六、与其他 QNX 相关机制的关系
CMP0047 只是 CMake QNX 支持的一部分。从仓库结构看,QNX 支持由多层协同构成:
- 策略层:CMP0047.rst 决定 Compiler ID 的取值口径;
- 编译器层:Modules/Compiler/QCC-*.cmake 系列模块按 ID
QCC提供各语言的编译/链接规则与特性检测; - 平台层:Modules/Platform/QNX-Initialize.cmake 完成系统级初始化;
- 变量层:CMAKE_ _COMPILER_ID文档中
QCC被登记为 "QNX C/C++ compiler" 的官方取值,可供所有项目在条件判断中引用。
理解这一层次关系后,遇到 QNX 工具链问题时可以按"ID 是否对 → 规则是否生效 → 平台初始化是否正确"的顺序排查,而 CMP0047 正是第一环。
总结
CMP0047 是 CMake 3.0 引入、并在 CMake 4.0 中强制收口的 QNX 编译器识别策略:
- OLD:qcc/QCC 驱动报告
GNU编译器 ID; - NEW:qcc/QCC 驱动报告独立的
QCC编译器 ID; - 必须在
project()/enable_language()之前设置; - 配套实现见 cmPolicies.h 与 Modules/Compiler/QCC-*.cmake,其中 QCC-CXX 的版本分支(qcc 12.2.0 前后使用不同的 C++ 语言旗标)说明了 ID 独立化的根本动机——qcc 并非 GNU 编译器,只是与其兼容的 QNX 驱动。
对于维护 QNX 交叉编译工程的开发者,建议在 CMake 4.0 落地前完成 Compiler ID 的显式对齐,把所有针对 qcc 的GNU判断迁移为QCC,从而让工具链识别更精确、升级路径更平滑。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
Meson 对 QNX qcc/q++ 编译器驱动的支持:交叉编译配置、实现原理与已知限制
Meson 对 QNX qcc/q++ 编译器驱动的支持:交叉编译配置、实现原理与已知限制 Meson Build System 自本版本起正式支持 QNX S
构建工具CMake 策略 CMP0025 深度解析:Apple Clang 编译器标识从 Clang 到 AppleClang 的演进
CMake 策略 CMP0025 深度解析:Apple Clang 编译器标识从 Clang 到 AppleClang 的演进 本指南以 CMake 官方策略文
构建工具开发工具CLICMake CMP0129 政策解析:MCST LCC 编译器识别从 GNU 到 LCC 的迁移指南
CMake CMP0129 政策解析:MCST LCC 编译器识别从 GNU 到 LCC 的迁移指南 导读 本文深入剖析 CMake 3.23 引入的策略 CM
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考