7、打包预设(packagePresets):定义 CPack 打包配置。
说实话,很多团队在 CMake 构建上花了不少功夫,但到了「打包」这一步,往往就靠手写脚本或者靠某位同事的本地记忆。我见过太多次了——换台机器打包,出来的安装包结构都不一样。
CMake Presets 里的 packagePresets,就是来解决这个问题的。它让你把 CPack 的打包配置标准化,跟构建配置一样,写进 JSON 文件里,人人共享。
为什么需要打包预设?
你想想看,一个项目最终要交付给用户,无非是几种形式:源码、二进制压缩包、安装程序(比如 NSIS 或 DMG)、或者 deb/rpm 这类系统包。CPack 本身很强大,但它依赖一大堆变量:CPACK_GENERATOR、CPACK_PACKAGE_NAME、CPACK_PACKAGE_VERSION……
这些变量散落在 CMakeLists.txt 里,或者靠命令行传参。一旦项目大了,维护起来就头疼。我个人习惯是把这些打包参数统一放到 packagePresets 里,清晰、可复用、可继承。
基本结构
一个 packagePresets 条目,长这样:
{
"packagePresets": [
{
"name": "default",
"displayName": "默认打包配置",
"description": "生成 ZIP 压缩包",
"generator": "ZIP",
"configurations": ["Release"],
"variables": {
"CPACK_PACKAGE_NAME": "MyApp",
"CPACK_PACKAGE_VERSION": "1.0.0",
"CPACK_PACKAGE_VENDOR": "MyCompany"
}
}
]
}
这里 generator 指定了打包格式,variables 里填的就是 CPack 的变量。嗯,这里要注意:configurations 字段是可选的,它指定了用哪个构建配置的产物来打包。如果你不写,默认就用当前构建目录里的东西。
继承与覆盖
打包预设也支持 inherits,这一点跟构建预设和测试预设完全一致。我经常这样用:
{
"packagePresets": [
{
"name": "base",
"hidden": true,
"generator": "ZIP",
"variables": {
"CPACK_PACKAGE_NAME": "MyApp",
"CPACK_PACKAGE_VENDOR": "MyCompany"
}
},
{
"name": "linux-deb",
"inherits": "base",
"displayName": "Debian 包",
"generator": "DEB",
"variables": {
"CPACK_PACKAGE_VERSION": "1.0.0",
"CPACK_DEBIAN_PACKAGE_MAINTAINER": "dev@mycompany.com"
}
},
{
"name": "macos-dmg",
"inherits": "base",
"displayName": "macOS DMG",
"generator": "DragNDrop",
"variables": {
"CPACK_PACKAGE_VERSION": "1.0.0"
}
}
]
}
你看,base 预设隐藏了,只作为模板。然后 linux-deb 和 macos-dmg 各自继承它,再补充平台特有的变量。这样改一次基础配置,所有打包方式都跟着变。
与构建预设的联动
打包预设里有一个 configurePreset 字段,用来指定它依赖哪个配置预设。为什么要这个?因为 CPack 需要知道构建目录在哪。你指定了 configurePreset,CMake 就能自动找到对应的构建目录。
{
"packagePresets": [
{
"name": "release-package",
"configurePreset": "release",
"generator": "ZIP",
"configurations": ["Release"],
"variables": {
"CPACK_PACKAGE_NAME": "MyApp",
"CPACK_PACKAGE_VERSION": "1.0.0"
}
}
]
}
这样你只需要运行:
cmake --build --preset release
ctest --preset release
cpack --preset release-package
三步走,干净利落。我曾经在一个跨平台项目里,用这种方式把打包流程从 20 分钟的手动操作压缩到了 30 秒的自动化命令。
常用变量一览
CPack 的变量很多,但常用的其实就这些:
| 变量名 | 说明 | 示例值 |
|---|---|---|
CPACK_PACKAGE_NAME |
包名 | MyApp |
CPACK_PACKAGE_VERSION |
版本号 | 1.0.0 |
CPACK_PACKAGE_VENDOR |
供应商 | MyCompany |
CPACK_PACKAGE_DESCRIPTION |
描述 | A useful tool |
CPACK_GENERATOR |
打包格式 | ZIP, DEB, NSIS |
CPACK_PACKAGE_FILE_NAME |
输出文件名模板 | MyApp-1.0.0-win64 |
CPACK_INSTALL_CMAKE_PROJECTS |
指定安装的项目 | (通常不用改) |
variables 里定义了 CPACK_GENERATOR,它会覆盖预设顶层 generator 字段。我个人建议统一用顶层的 generator 字段,更直观。
避坑指南
我曾经在一个项目里,打包预设里写了 generator: "DEB",但构建机器上没有安装 dpkg 相关工具。结果 CPack 报错,说找不到生成器。这个坑其实很好避免——在打包预设里加一个 vendor 字段?不,CPack 没有这个字段。但你可以用 condition 来做平台判断。
{
"packagePresets": [
{
"name": "linux-deb",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"generator": "DEB",
"variables": {
"CPACK_PACKAGE_NAME": "MyApp",
"CPACK_PACKAGE_VERSION": "1.0.0"
}
}
]
}
这样,只有在 Linux 上才会启用这个预设。Windows 和 macOS 上它会被自动忽略。嗯,这个 condition 的用法跟构建预设里是一样的,非常实用。
知识结构图
下面这张图,帮你理清打包预设在整个 CMake Presets 体系中的位置:
实际项目中的用法
最后,分享一个我实际项目里的打包预设配置。这个项目需要同时打出 Windows 安装包和 Linux 压缩包:
{
"packagePresets": [
{
"name": "win-installer",
"configurePreset": "msvc-release",
"generator": "NSIS",
"configurations": ["Release"],
"variables": {
"CPACK_PACKAGE_NAME": "DataProcessor",
"CPACK_PACKAGE_VERSION": "2.3.1",
"CPACK_PACKAGE_VENDOR": "DataTech",
"CPACK_NSIS_DISPLAY_NAME": "数据处理工具",
"CPACK_NSIS_INSTALL_ROOT": "$PROGRAMFILES64"
}
},
{
"name": "linux-tarball",
"configurePreset": "gcc-release",
"generator": "TGZ",
"configurations": ["Release"],
"variables": {
"CPACK_PACKAGE_NAME": "dataprocessor",
"CPACK_PACKAGE_VERSION": "2.3.1",
"CPACK_PACKAGE_FILE_NAME": "dataprocessor-2.3.1-linux-x64"
}
}
]
}
你看,两个预设共用同一个版本号,但打包格式、文件名、平台特定参数都各自独立。维护起来非常方便——改版本号只需要改一个地方。
好了,关于打包预设就聊这么多。说白了,它就是给 CPack 戴上了一顶「标准化」的帽子。让团队里每个人都能用同样的方式打出同样的包,不再出现「我机器上能打包,你机器上就不行」这种尴尬事。
公众号:蓝海资料掘金营,微信deep3321