23、CPack打包类型:生成器详解(ZIP、TGZ、DEB、RPM、NSIS、DMG)、选择打包格式

打包这件事,说白了就是把你编译好的程序、库文件、配置文件、文档等一堆东西,按照目标平台的规范整理成一个安装包。CPack 之所以强大,就是因为它支持多种打包格式——也就是所谓的「生成器」。你只需要写一份 CMakeLists.txt,就能同时产出 .zip、.deb、.rpm、甚至 macOS 的 .dmg。

我个人习惯把生成器分成三类:通用归档类Linux 包管理类平台安装向导类。下面我一个一个讲。

通用归档生成器:ZIP、TGZ、STGZ

这类生成器不依赖特定操作系统,说白了就是压缩包。适合跨平台分发,或者给用户「解压即用」的场景。

生成器名称 输出格式 适用场景
ZIP .zip Windows / 跨平台通用
TGZ .tar.gz Linux / macOS 通用
STGZ .sh (自解压) Linux 下无需 root 权限安装

我在项目中遇到过这样一个坑:用 TGZ 打包时,文件权限会丢失。尤其是可执行文件,解压后没了 x 权限,用户跑不起来。后来我加了一行 INSTALL(TARGETS ... PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) 才搞定。

核心配置示例:
set(CPACK_GENERATOR "ZIP;TGZ")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}")

嗯,这里要注意:CPACK_PACKAGE_FILE_NAME 决定了压缩包的文件名。我建议把系统名加进去,比如 myapp-1.0.0-Linux.tar.gz,用户一看就知道该下载哪个。

Linux 包管理生成器:DEB、RPM

如果你要给 Ubuntu 用户分发软件,那 .deb 是首选。给 Fedora 或 CentOS 用户,.rpm 更合适。这两个生成器会生成符合各自包管理规范的安装包,支持依赖声明、预安装脚本、后安装脚本等高级功能。

DEB 打包关键变量:
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "yourname@example.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl-dev (>= 1.1), zlib1g-dev")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/debian/postinst")

我曾经在给一个内部工具打包时,忘了设置 CPACK_DEBIAN_PACKAGE_DEPENDS,结果用户安装后运行时提示缺少 libssl。嗯,从那以后我每次都会用 dpkg -I 检查生成的 .deb 包的依赖信息。

RPM 的配置思路类似,但变量名不同:

set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.1, zlib")
避坑指南: RPM 对版本号格式要求严格,不能有连字符。我曾经因为版本号写成 1.0-beta,结果 rpmbuild 直接报错。改成 1.0~beta 才通过。

平台安装向导生成器:NSIS、DMG

NSIS 是 Windows 下的安装程序生成器。它会生成一个 .exe 安装向导,用户双击后一路 Next 就能装好。DMG 是 macOS 下的磁盘映像格式,用户拖拽 App 到 Applications 文件夹即可完成安装。

我个人觉得 NSIS 的配置稍微有点繁琐,但效果很好:

set(CPACK_NSIS_DISPLAY_NAME "MyApp")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyApp.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '$INSTDIR\\\\bin\\\\register.dll'")

DMG 打包相对简单,但要注意背景图和卷标:

set(CPACK_DMG_VOLUME_NAME "MyApp-1.0.0")
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/dmg_background.png")
set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/DS_Store_template")
我的经验: DMG 的 DS_Store 文件很难手工制作。我一般先在 Finder 里手动排好图标位置,然后把 DS_Store 文件复制到源码目录作为模板。这样打包出来的 DMG 打开后图标位置就是整齐的。

如何选择打包格式?

你想想看,如果你的用户群体是 Linux 开发者,那 TGZ 和 DEB 都要提供。如果是 Windows 普通用户,NSIS 是必须的。macOS 用户则习惯 DMG。

我一般这样决策:

  • 跨平台命令行工具:ZIP + TGZ,用户自己解压放到 PATH 里
  • Linux 库文件:DEB + RPM,方便系统包管理
  • Windows 桌面应用:NSIS,带安装向导和开始菜单快捷方式
  • macOS 应用:DMG,符合 Mac 用户的使用习惯
  • 内部测试:STGZ,一行命令就装好了,不需要 root 权限

你可以同时指定多个生成器:

set(CPACK_GENERATOR "ZIP;TGZ;DEB;RPM")
if(WIN32)
  set(CPACK_GENERATOR "NSIS")
elseif(APPLE)
  set(CPACK_GENERATOR "DMG")
endif()

这样在 Linux 上执行 cpack 就会同时生成 .zip、.tar.gz、.deb、.rpm 四个包。在 Windows 上只生成 NSIS 安装包,macOS 上只生成 DMG。

小技巧:cpack -G "TGZ" 可以临时指定生成器,覆盖 CMakeLists.txt 里的默认设置。调试时我经常这么干。

知识体系结构图

CPack 生成器 通用归档类 Linux 包管理类 平台安装向导类 ZIP TGZ STGZ DEB RPM NSIS DMG 选择建议 命令行工具 → ZIP + TGZ Linux 库 → DEB + RPM Windows 桌面应用 → NSIS | macOS 应用 → DMG

最后说一句:不要试图一次性支持所有生成器。我刚开始做打包时,恨不得把十几种格式全列上,结果配置了三天还没调通。后来我学乖了——先搞定 TGZ,再逐个添加 DEB、RPM、NSIS。每加一个,就用对应的平台测试一遍。稳扎稳打,比什么都强。


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