跨平台兼容性:编写一套 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 分成三层:
- 基础层:所有平台共享的配置(如 C++ 标准、输出目录)
- 平台层:按操作系统区分的配置(如编译器、工具链)
- 变体层:同一平台下的不同构建类型(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 的结构:
避坑指南:我踩过的那些坑
写跨平台 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 自动选择。你试试看,真的能省不少事。