目录

1.背景

2.CMake 策略的核心作用

3.策略的基本使用

4.向后兼容的关键原则

5.策略迁移的 “渐进式” 原则

5.处理兼容性的最佳实践

6.总结


1.背景

        CMake相当重视向后兼容性,并且受益此,能够持续不断的改进和增加新特性,而几乎不会破环古来的代码仓库。CMake的策略机制就是为解决向后兼容问题而生的。

        有了策略机制,CMake可以基本确保基于旧版本CMake编写的目录程序可以被新版本的CMake配置生成,同时,如果程序中使用了已经弃用的特性,它也能针对地给出警告信息,鼓励或要求用户重构CMake程序。

        CMake不是一味的妥协,也会在较长时间后逐渐移除这些"遗产",使得CMake的代码能够有机会焕然一新,而不是总在积累技术债务。当然,策略机制也一定会告诉们使用了已彻底移除的特性,从而可以让我们根据产生的错误信息,对CMake程序进行重构。

        是不是感觉到有些神奇,CMake的策略机制到底如何实现的呢?下面就一起来了解一下吧。

2.CMake 策略的核心作用

        在 CMake 中,策略(Policy) 是用于处理 CMake 版本间行为变化的机制,其核心目的是在保持向后兼容的同时,允许引入新的、更合理的功能或行为。

        CMake 策略(Policy Policy Policy)用于管理不同版本之间的行为差异。每个策略对应一个具体的行为变更,编号格式为 CMP<NNNN>(如 CMP0048 控制项目版本设置)。

  • 旧行为(OLD):保持与 CMake 旧版本一致的行为。
  • 新行为(NEW):启用新版本引入的改进行为(可能不兼容旧版本)。

策略的核心目的是:允许旧项目逐步迁移到新行为,同时避免突然的兼容性断裂

        常见策略示例:

  • CMP0048:控制 project() 命令的版本参数行为。旧行为忽略版本号,新行为会将版本号存储到变量中(如 PROJECT_VERSION)。
  • CMP0091:控制 MSVC 编译器的运行时库选择。新行为允许通过 CMAKE_MSVC_RUNTIME_LIBRARY 变量显式指定(如 /MD 或 /MT),旧行为则依赖于默认设置。
  • CMP0063:控制变量的作用域。新行为使 PROJECT() 等命令定义的变量仅在当前作用域生效,避免意外污染全局变量。

3.策略的基本使用

1.查看策略状态

通过 cmake_policy(GET <policy> <var>) 查看策略当前状态(OLD 或 NEW):

cmake_policy(GET CMP0048 CMP0048_STATUS)
message("CMP0048 状态: ${CMP0048_STATUS}")

2.设置单个策略

通过 cmake_policy(SET <policy> <OLD|NEW>) 强制指定策略行为:

# 强制启用 CMP0048 的新行为(项目版本设置)
cmake_policy(SET CMP0048 NEW)

# 强制使用 CMP0020 的旧行为(目标链接方式)
cmake_policy(SET CMP0020 OLD)

3.批量设置策略版本

通过 cmake_minimum_required(VERSION <major>.<minor>[.<patch>] [FATAL_ERROR]) 批量设置策略默认值:

  • 当指定 VERSION 3.10 时,所有编号小于等于 CMP0074(3.10 版本最后一个策略)的策略默认启用 NEW 行为(若该策略在 3.10 及之前引入)。
  • 未指定的更高版本策略(如 3.11 引入的 CMP0075)默认使用 OLD 行为,避免意外启用新特性。

示例:

# 要求 CMake 最低版本为 3.10,同时将 3.10 及之前的策略默认设为 NEW
cmake_minimum_required(VERSION 3.10)

4.向后兼容的关键原则

CMake 维护向后兼容的核心原则包括:

1.策略的版本绑定

每个策略都绑定到特定的 CMake 版本,例如:

  • CMP0048 引入于 3.0 版本,控制 project() 命令的版本参数行为。
  • CMP0091 引入于 3.15 版本,控制 MSVC 编译器的运行时库选择。

当项目使用 cmake_minimum_required(VERSION 3.15) 时,这些策略会自动启用新行为。

2.旧版本行为的保留

即使引入新策略,CMake 仍会保留旧行为(通过 OLD 选项),直到明确宣布废弃。例如:

  • CMP0026(禁止隐式依赖文件 glob)在 3.0 引入,旧行为允许 file(GLOB) 隐式依赖,新行为则需要显式声明。
  • 项目可通过 cmake_policy(SET CMP0026 OLD) 继续使用旧行为,直到准备好迁移。

3.废弃警告与过渡期

对于计划废弃的行为,CMake 会先通过警告提示用户,给予足够的迁移时间:

  • 例如,CMP0042(启用 MACOSX_RPATH)在 3.0 中设为默认 NEW,但旧行为仍可通过策略保留,直到用户主动迁移。
  • 警告信息会明确指出受影响的策略和迁移方法。

5.策略迁移的 “渐进式” 原则

大型项目直接升级 CMake 版本并启用所有新策略,风险极高(可能引发成百上千处构建错误)。实践中更有效的是 “渐进式迁移”:

1.cmake_minimum_required为基准

先将cmake_minimum_required从低版本(如 2.8)逐步提升到 3.5→3.10→3.16,每次升级后只处理该版本引入的策略(如升级到 3.10 时,优先处理CMP0063CMP0069等 3.10 前的策略)。

例:某项目从 3.5 升级到 3.16 时,分 3 次完成:3.5→3.10(处理 CMP0060-CMP0074)→3.14(处理 CMP0075-CMP0083)→3.16(处理 CMP0084-CMP0097),每次迭代只改 10 个以内的策略,降低测试成本。

2.“先 OLD 后 NEW” 的验证流程

对不确定影响的策略,先显式设置为OLD(保持旧行为),确保项目稳定;再在单独分支中测试NEW行为,对比构建产物(如二进制大小、链接依赖)和运行结果,确认无差异后再合并。

例:测试CMP0063(符号可见性控制)时,需对比启用NEW后动态库的导出符号是否变化,避免因符号隐藏导致下游项目链接失败。

3.利用CMAKE_POLICY_DEFAULT_CMP<NNNN>批量控制

CMake 3.12 + 支持通过-DCMAKE_POLICY_DEFAULT_CMP<NNNN>=NEW在命令行临时启用新策略,方便 CI 中做兼容性测试,无需修改CMakeLists.txt

例:在 CI 中添加一个 “新策略测试” 任务:

cmake -DCMAKE_POLICY_DEFAULT_CMP0077=NEW -DCMAKE_POLICY_DEFAULT_CMP0091=NEW ..

5.处理兼容性的最佳实践

1. 明确指定最低版本

始终在 CMakeLists.txt 开头设置 cmake_minimum_required(VERSION <version>),既声明项目依赖的最低版本,又自动配置策略默认值:

# 声明最低支持 3.16,并启用该版本及之前策略的新行为
cmake_minimum_required(VERSION 3.16)
project(MyProject)

2.逐步迁移到新策略

对于旧项目,可通过以下步骤迁移:

  1. 升级 CMake 版本后,观察构建时的策略警告(如 Policy CMP00XX is not set)。
  2. 逐个研究警告中的策略说明(通过 cmake --help-policy CMP00XX 查看详情)。
  3. 测试并启用新行为(cmake_policy(SET CMP00XX NEW)),确保项目功能不受影响。

3.避免依赖未声明的行为

  • 不依赖 CMake 未明确文档化的 “隐藏行为”(如某些命令的未公开参数)。
  • 对跨版本可能变化的功能(如链接顺序、变量作用域),使用显式配置(如 target_link_libraries 替代旧的 link_libraries)。

4.测试多个版本

在 CI 流程中测试多个 CMake 版本(如最低版本、最新稳定版),确保兼容性:

# .github/workflows/cmake.yml 示例
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        cmake-version: ["3.10", "3.25", "latest"]
    steps:
      - uses: actions/checkout@v4
      - uses: cmake/setup-cmake@v3
        with:
          cmake-version: ${{ matrix.cmake-version }}

6.总结

CMake 的策略机制是平衡 “功能升级” 与 “向后兼容” 的核心设计:

  • 策略通过 OLD/NEW 选项允许精细控制行为变更。
  • cmake_minimum_required 批量管理策略默认值,简化配置。
  • 开发者应通过明确版本声明、逐步迁移策略、测试多版本来确保项目兼容性。

合理利用策略系统,既能享受 CMake 新版本的功能改进,又能保证旧项目的稳定构建。

相关链接

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐