跨平台兼容性:编写一套 Presets 适配 Windows、Linux、macOS

说实话,跨平台构建这件事,我踩过的坑比写过的代码还多。

记得我刚接手一个项目时,团队里有人用 Windows 写代码,有人用 macOS 做 UI,服务器跑的是 Linux。每次合并代码,构建脚本就得改一遍。后来我下定决心,用 CMake Presets 把这套东西统一了。今天我就把这套方案分享给你。

为什么需要跨平台 Presets?

你想想看,不同平台的差异在哪?

  • 编译器不同:Windows 用 MSVC,Linux 用 GCC/Clang,macOS 用 Apple Clang
  • 工具链不同:Windows 有 vcpkg,Linux 有 apt/yum,macOS 有 Homebrew
  • 路径习惯不同:Windows 用反斜杠,Unix 用正斜杠
  • 构建生成器不同:Windows 常用 Ninja 或 Visual Studio,Linux 和 macOS 常用 Ninja 或 Unix Makefiles

一套 Presets 搞定所有平台?嗯,完全可以。关键在于用好 condition 字段。

核心思路:用 condition 判断当前操作系统,然后加载对应的配置。说白了,就是给每个平台写一份专属的「配方」。

基础结构:按平台分层的 Presets

我个人习惯把 Presets 分成三层:

  1. 基础层:所有平台共享的配置(如 C++ 标准、输出目录)
  2. 平台层:按操作系统区分的配置(如编译器、工具链)
  3. 变体层:同一平台下的不同构建类型(Debug/Release)

来看一个完整的例子:

{
  "version": 6,
  "configurePresets": [
    {
      "name": "base",
      "hidden": true,
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/${presetName}",
      "cacheVariables": {
        "CMAKE_CXX_STANDARD": "17",
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
      }
    },
    {
      "name": "windows-x64",
      "inherits": "base",
      "hidden": true,
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Windows"
      },
      "architecture": {
        "value": "x64",
        "strategy": "set"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "cl.exe",
        "CMAKE_CXX_COMPILER": "cl.exe",
        "VCPKG_TARGET_TRIPLET": "x64-windows"
      }
    },
    {
      "name": "linux-x64",
      "inherits": "base",
      "hidden": true,
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Linux"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "gcc",
        "CMAKE_CXX_COMPILER": "g++",
        "VCPKG_TARGET_TRIPLET": "x64-linux"
      }
    },
    {
      "name": "macos-x64",
      "inherits": "base",
      "hidden": true,
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Darwin"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "clang",
        "CMAKE_CXX_COMPILER": "clang++",
        "VCPKG_TARGET_TRIPLET": "x64-osx"
      }
    }
  ]
}

小技巧:把平台层 Presets 设为 hidden: true,这样用户不会直接看到它们。用户只需要选择最终的变体 Presets 即可。

构建变体:Debug 与 Release 的跨平台写法

有了平台基础,接下来就是每个平台的 Debug 和 Release 配置。这里有个坑——不同平台的优化选项不一样。

{
  "configurePresets": [
    {
      "name": "windows-debug",
      "inherits": "windows-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "Embedded"
      }
    },
    {
      "name": "windows-release",
      "inherits": "windows-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "ProgramDatabase"
      }
    },
    {
      "name": "linux-debug",
      "inherits": "linux-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_FLAGS_DEBUG": "-g -O0"
      }
    },
    {
      "name": "linux-release",
      "inherits": "linux-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG"
      }
    },
    {
      "name": "macos-debug",
      "inherits": "macos-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_FLAGS_DEBUG": "-g -O0"
      }
    },
    {
      "name": "macos-release",
      "inherits": "macos-x64",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG"
      }
    }
  ]
}

注意:MSVC 的调试信息格式和 GCC/Clang 不一样。我曾经在 Windows 上用了 -g 选项,结果 MSVC 直接报错。记住,MSVC 用 /Zi/Z7,GCC/Clang 用 -g

构建 Presets:统一构建命令

配置写好了,构建命令也得跟上。不同平台的构建步骤其实差不多,但有些细节要注意。

{
  "buildPresets": [
    {
      "name": "windows-debug",
      "configurePreset": "windows-debug",
      "configuration": "Debug"
    },
    {
      "name": "windows-release",
      "configurePreset": "windows-release",
      "configuration": "Release"
    },
    {
      "name": "linux-debug",
      "configurePreset": "linux-debug"
    },
    {
      "name": "linux-release",
      "configurePreset": "linux-release"
    },
    {
      "name": "macos-debug",
      "configurePreset": "macos-debug"
    },
    {
      "name": "macos-release",
      "configurePreset": "macos-release"
    }
  ]
}

看到区别了吗?Windows 的构建 Presets 多了 configuration 字段。这是因为 Visual Studio 生成器需要指定配置名称,而 Ninja 和 Unix Makefiles 不需要。嗯,这个细节很容易被忽略。

测试 Presets:跨平台测试配置

测试环节也有平台差异。比如 Windows 上你可能用 CTest 配合 --output-on-failure,Linux 上可能还需要 --parallel 参数。

{
  "testPresets": [
    {
      "name": "windows-debug",
      "configurePreset": "windows-debug",
      "configuration": "Debug",
      "output": {
        "outputOnFailure": true
      }
    },
    {
      "name": "linux-debug",
      "configurePreset": "linux-debug",
      "output": {
        "outputOnFailure": true
      },
      "execution": {
        "jobs": 4
      }
    },
    {
      "name": "macos-debug",
      "configurePreset": "macos-debug",
      "output": {
        "outputOnFailure": true
      },
      "execution": {
        "jobs": 4
      }
    }
  ]
}

建议:Linux 和 macOS 上我习惯用 jobs: 4 并行跑测试,Windows 上反而建议少开并行,因为 Windows 的进程创建开销更大。

跨平台知识体系

下面这张图帮你理清整个跨平台 Presets 的结构:

跨平台 CMake Presets 结构 基础层 (base) C++标准、输出目录、生成器 Windows (windows-x64) Linux (linux-x64) macOS (macos-x64) MSVC + vcpkg GCC + vcpkg Clang + vcpkg Debug Release Debug Release Debug Release condition 判断 → 自动选择对应平台

避坑指南:我踩过的那些坑

写跨平台 Presets 时,有几个地方特别容易出问题。我一个个说:

  • 路径分隔符:不要在 Presets 里硬编码路径。用 ${sourceDir}${presetName} 这些变量,CMake 会自动处理平台差异。
  • 编译器检测:我曾经在 macOS 上直接写 CMAKE_C_COMPILER: gcc,结果发现 macOS 的 gcc 其实是 Clang 的别名。后来我改用 clang 才搞定。
  • vcpkg 三元组:不同平台的 vcpkg 三元组不一样。Windows 是 x64-windows,Linux 是 x64-linux,macOS 是 x64-osx。写错了包就装不上。
  • 生成器选择:Windows 上我推荐用 Ninja,因为 Visual Studio 生成器在 CI 环境里配置起来很麻烦。Linux 和 macOS 上 Ninja 也是首选。

重要提醒condition 字段只在 CMake 3.22+ 版本中支持。如果你的团队还在用旧版本,记得升级。我曾经在一个项目里用了 condition,结果同事的 CMake 3.18 直接报错,场面一度很尴尬。

实战建议:团队协作中的跨平台 Presets

最后,给你几个团队协作时的建议:

  • 统一命名规范:我建议用 平台-架构-变体 的格式,比如 windows-x64-debug。这样一看就懂。
  • 写一个 README:告诉团队成员在不同平台上用什么命令。比如 Windows 上跑 cmake --preset windows-x64-debug,Linux 上跑 cmake --preset linux-x64-debug
  • CI/CD 集成:在 CI 配置里直接引用 Presets 名称,这样 CI 脚本和本地构建完全一致,减少「在我机器上能跑」的问题。
  • 定期测试:每个平台至少每周跑一次完整的构建和测试。我见过太多跨平台问题是在合并代码后才暴露的。

好了,跨平台 Presets 的核心内容就这些。说白了,就是给每个平台写一份专属配置,然后用 condition 自动选择。你试试看,真的能省不少事。

公众号:蓝海资料掘金营,微信deep3321