20、CPack与打包:CPack配置、生成器(NSIS、DEB、RPM、DMG)、组件安装、安装脚本
说到软件交付,编译通过只是第一步。你想想看,代码写得再好,用户拿到的是一堆零散的二进制文件和头文件,这体验能好吗?CPack 就是 CMake 体系里专门解决这个问题的工具。它能把你的构建产物打包成各种平台下的安装包——Windows 上的 NSIS 安装程序、Linux 上的 DEB/RPM 包、macOS 上的 DMG 镜像,统统不在话下。
我个人习惯把 CPack 看作是 CMake 的「最后一公里」工具。前面折腾了那么多目标属性、生成器表达式,最终不就是为了让用户能顺利用上你的库或应用吗?
20.1 CPack 的基本配置
CPack 的配置其实很简单。你只需要在 CMakeLists.txt 里设置几个变量,然后调用 include(CPack) 就行了。但这里有个坑——变量的设置顺序很重要。
# 基础信息
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.2.3")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A fantastic application")
# 联系方式
set(CPACK_PACKAGE_CONTACT "support@mycompany.com")
# 必须包含这一行
include(CPack)
我曾经见过有人把 include(CPack) 写在了变量设置之前,结果打包出来的版本号全是空的。嗯,这里要注意:CPack 是在 include 的那一刻读取这些变量的,所以一定要先设置好。
20.2 选择打包生成器
CPack 支持多种打包格式,通过 CPACK_GENERATOR 变量来指定。你可以同时指定多个生成器,CPack 会依次生成对应的安装包。
| 生成器 | 平台 | 典型用途 |
|---|---|---|
| NSIS | Windows | 制作 .exe 安装程序 |
| DEB | Linux (Debian/Ubuntu) | 制作 .deb 包 |
| RPM | Linux (Red Hat/Fedora) | 制作 .rpm 包 |
| DMG | macOS | 制作 .dmg 磁盘映像 |
| ZIP/TGZ | 跨平台 | 通用压缩包 |
# 根据平台自动选择生成器
if(WIN32)
set(CPACK_GENERATOR "NSIS")
elseif(APPLE)
set(CPACK_GENERATOR "DMG")
elseif(UNIX)
# 检测包管理器
find_program(DPKG_CMD dpkg)
find_program(RPM_CMD rpm)
if(DPKG_CMD)
set(CPACK_GENERATOR "DEB")
elseif(RPM_CMD)
set(CPACK_GENERATOR "RPM")
else()
set(CPACK_GENERATOR "TGZ")
endif()
endif()
说白了,你不需要在代码里写死生成器。让 CMake 根据目标平台自动选择,这才是现代构建系统的做法。
20.3 组件安装
大型项目通常包含多个组件:核心库、开发头文件、文档、示例代码等。用户可能只需要安装运行时组件,而不需要开发文件。这时候就需要组件安装。
# 安装核心库
install(TARGETS mylib
LIBRARY DESTINATION lib
COMPONENT runtime
)
# 安装开发头文件
install(DIRECTORY include/
DESTINATION include
COMPONENT development
)
# 安装文档
install(FILES README.md LICENSE
DESTINATION share/doc/MyApp
COMPONENT documentation
)
# 启用组件打包
set(CPACK_COMPONENTS_ALL runtime development documentation)
set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "Runtime libraries and executables")
set(CPACK_COMPONENT_DEVELOPMENT_DESCRIPTION "Headers and development files")
我在项目中遇到过这样的情况:用户只需要运行程序,却被迫安装了整套开发环境。用了组件安装后,用户可以选择只安装 runtime 组件,安装包体积直接缩小了 80%。
COMPONENT 关键字。每个 install() 命令都可以指定所属组件,然后通过 CPACK_COMPONENTS_ALL 列出所有组件。
20.4 安装脚本
有些时候,光靠 CMake 的安装规则还不够。比如你需要在安装后创建快捷方式、注册文件关联、或者运行一些后处理脚本。这时候就需要安装脚本。
# 安装前执行的脚本
set(CPACK_INSTALL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pre_install.cmake")
# 安装后执行的脚本
set(CPACK_POST_INSTALL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/post_install.cmake")
# NSIS 特有的脚本
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyApp.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'
")
举个例子,我曾经需要打包一个数据库驱动的应用。安装后必须执行一个脚本来初始化数据库。用 CPACK_POST_INSTALL_SCRIPT 就完美解决了这个问题。
# post_install.cmake
message("Initializing database...")
execute_process(COMMAND ${CMAKE_INSTALL_PREFIX}/bin/init_db.sh)
message("Database initialized successfully!")
20.5 生成器特定配置
不同的打包格式有不同的特性。CPack 允许你为每个生成器设置专属变量。
# NSIS 配置
set(CPACK_NSIS_DISPLAY_NAME "MyApp ${CPACK_PACKAGE_VERSION}")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Uninstall.lnk' '$INSTDIR\\\\uninstall.exe'")
# DEB 配置
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "developer@mycompany.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl-dev (>= 1.1), libcurl4")
# RPM 配置
set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
# DMG 配置
set(CPACK_DMG_VOLUME_NAME "MyApp ${CPACK_PACKAGE_VERSION}")
set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/cmake/DS_Store")
你想想看,如果不做这些配置,生成的 DEB 包连依赖关系都没有,用户安装时根本不知道还需要 libssl。这包发出去,不是等着被吐槽吗?
20.6 知识体系总览
下面这张图总结了 CPack 打包的核心流程和关键配置点:
20.7 实战经验总结
最后分享几个我踩过的坑:
- 版本号一致性: 我曾经因为忘记更新
CPACK_PACKAGE_VERSION,导致发布的 DEB 包版本号还是上一个版本的。从那以后,我养成了在发布前用cpack --version检查配置的习惯。 - 依赖关系: 对于 DEB 和 RPM 包,一定要正确填写
CPACK_DEBIAN_PACKAGE_DEPENDS和CPACK_RPM_PACKAGE_REQUIRES。否则用户安装时会遇到「缺少依赖」的错误。 - 路径问题: NSIS 安装路径默认是
$PROGRAMFILES,但 64 位应用应该用$PROGRAMFILES64。这个细节我一开始没注意,导致用户安装到了错误的目录。 - 测试打包: 我建议在 CI 流程中加入打包测试。每次提交代码都自动生成安装包,确保打包过程不会因为代码变更而损坏。
CPack 其实不复杂,但细节很多。你只要把配置变量理清楚,组件划分好,脚本写对,就能生成专业级别的安装包。说白了,这就是一个「一次配置,到处打包」的过程。
公众号:蓝海资料掘金营,微信deep3321