21、CPack打包:set(CPACK_XXX)变量,cpack命令,生成安装包(ZIP、TGZ、DEB、RPM、NSIS)

说到CMake的终极技能,CPack绝对算一个。

你想想看,代码写完了,编译通过了,测试也绿了。然后呢?你怎么把成果交给用户?总不能让人家也装个CMake,再敲一遍cmake --build吧?

CPack就是干这个的。它能把你的构建产物打包成各种格式的安装包。ZIP、TGZ给跨平台用户,DEB给Debian/Ubuntu,RPM给Red Hat/Fedora,NSIS给Windows。一套CMakeLists.txt,通吃所有平台。

我个人习惯是,项目一开始就把CPack配置写好。哪怕现在只支持Linux,也把Windows的NSIS配置占个位。省得到时候临时抱佛脚。

核心思路:CPack读取你设置的CPACK_XXX变量,然后调用对应的打包工具(如zip、tar、dpkg-deb、rpmbuild、makensis)生成安装包。你只需要告诉CPack「包名叫什么、版本号多少、要装哪些文件」,剩下的它自己搞定。

21.1 最基本的CPack配置

先看一个最小配置。假设你有一个可执行文件叫myapp,想把它打包成ZIP和TGZ。

# CMakeLists.txt 末尾加上
include(CPack)

set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A useful tool")

# 指定打包格式
set(CPACK_GENERATOR "ZIP;TGZ")

# 指定安装路径
install(TARGETS myapp DESTINATION bin)

然后执行:

mkdir build && cd build
cmake ..
cpack

你就会在当前目录下看到MyApp-1.0.0-Linux.zipMyApp-1.0.0-Linux.tar.gz

嗯,这里要注意:install()命令必须写。CPack打包的依据就是install()指定的安装规则。你忘了写install,CPack就给你打个空包。

小技巧:可以用cpack -G ZIP只生成ZIP包,不用每次都改CMakeLists.txt。我经常在调试打包配置时这么干,省时间。

21.2 常用CPack变量一览

CPack的变量很多,但常用的就那么几个。我列个表,你一看就明白。

变量名 作用 示例值
CPACK_PACKAGE_NAME 包名 MyApp
CPACK_PACKAGE_VERSION 版本号 1.0.0
CPACK_PACKAGE_VENDOR 供应商/作者 My Company
CPACK_PACKAGE_DESCRIPTION_SUMMARY 简短描述 A useful tool
CPACK_GENERATOR 打包格式列表 ZIP;TGZ;DEB;RPM;NSIS
CPACK_PACKAGE_FILE_NAME 输出文件名模板 MyApp-1.0.0-${CMAKE_SYSTEM_NAME}
CPACK_PACKAGE_INSTALL_DIRECTORY 安装目录(NSIS用) MyApp
CPACK_RESOURCE_FILE_LICENSE 许可证文件路径 ${CMAKE_SOURCE_DIR}/LICENSE
CPACK_PACKAGE_ICON 图标文件路径(NSIS用) ${CMAKE_SOURCE_DIR}/icon.ico

为什么文件名模板里要用${CMAKE_SYSTEM_NAME}?因为同一个项目在Linux和Windows上打包,文件名得区分开。不然用户下载了都不知道该用哪个。

21.3 生成DEB包(Debian/Ubuntu)

DEB包是Linux发行版最常用的格式。CPack生成DEB包需要额外设置一些变量。

set(CPACK_GENERATOR "DEB")

set(CPACK_DEBIAN_PACKAGE_MAINTAINER "me@example.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.31), libssl1.1")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)

CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON这个变量很实用。它会自动扫描你的可执行文件依赖了哪些共享库,然后生成对应的Depends字段。我曾经手动写依赖写漏了一个,结果用户装上去跑不起来。从那以后我就老老实实开这个选项。

注意:生成DEB包需要系统安装dpkg-deb工具。Ubuntu/Debian默认就有,但其他发行版可能需要自己装。可以用sudo apt install dpkg搞定。

21.4 生成RPM包(Red Hat/Fedora)

RPM包的配置和DEB类似,但变量名前缀不同。

set(CPACK_GENERATOR "RPM")

set(CPACK_RPM_PACKAGE_RELEASE "1")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Utilities")
set(CPACK_RPM_PACKAGE_REQUIRES "libc >= 2.31, openssl-libs")
set(CPACK_RPM_PACKAGE_AUTOREQ ON)

CPACK_RPM_PACKAGE_AUTOREQ ON和DEB的SHLIBDEPS是一个道理,自动检测依赖。我建议你两个都开着,省心。

生成RPM包需要rpmbuild工具。CentOS/RHEL/Fedora用户可以用sudo yum install rpm-build安装。

21.5 生成NSIS安装包(Windows)

NSIS是Windows上最流行的安装包制作工具。CPack对NSIS的支持相当完善。

set(CPACK_GENERATOR "NSIS")

set(CPACK_NSIS_DISPLAY_NAME "MyApp")
set(CPACK_NSIS_HELP_LINK "https://example.com/help")
set(CPACK_NSIS_URL_INFO_ABOUT "https://example.com")
set(CPACK_NSIS_CONTACT "support@example.com")
set(CPACK_NSIS_CREATE_ICONS_EXTRA
    "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyApp.lnk' '$INSTDIR\\\\bin\\\\myapp.exe'")

这里有个坑:NSIS的路径分隔符要用双反斜杠\\\\。因为CMake本身会转义一次,NSIS又会转义一次。我第一次写的时候用了单反斜杠,结果生成的安装包快捷方式全是坏的。折腾了半天才发现是转义问题。

避坑指南:如果你在Windows上交叉编译Linux程序,CPack依然可以生成Linux的DEB/RPM包。只要你的系统装了对应的打包工具就行。我经常在Windows上用WSL做这件事。

21.6 打包流程全景图

下面这张图展示了CPack的完整工作流程。从CMakeLists.txt配置,到install()规则,再到cpack命令调用,最后生成各种格式的安装包。

CPack打包流程 CMakeLists.txt 配置 CPACK_XXX 变量 install() 指定安装规则 cmake .. → cpack ZIP / TGZ 跨平台通用 DEB Debian/Ubuntu RPM Red Hat/Fedora NSIS Windows CPack 根据 CPACK_GENERATOR 自动调用对应的打包工具

21.7 实战:一个完整的CPack配置

最后,我给你看一个我实际项目中用过的配置。它同时支持ZIP、TGZ、DEB、RPM、NSIS五种格式。

# 基础信息
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A useful tool")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}")

# 许可证文件
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")

# 打包格式
set(CPACK_GENERATOR "ZIP;TGZ;DEB;RPM;NSIS")

# DEB 专用
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "me@example.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)

# RPM 专用
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Utilities")
set(CPACK_RPM_PACKAGE_AUTOREQ ON)

# NSIS 专用
set(CPACK_NSIS_DISPLAY_NAME "MyApp")
set(CPACK_NSIS_HELP_LINK "https://example.com/help")
set(CPACK_NSIS_URL_INFO_ABOUT "https://example.com")
set(CPACK_NSIS_CONTACT "support@example.com")

# 最后引入CPack
include(CPack)

把这个配置加到你的CMakeLists.txt末尾,然后执行cpack。你会在build目录下看到一堆安装包。每个平台的用户都能找到适合自己的格式。

总结一下:CPack的核心就三件事——设置变量、写install规则、执行cpack命令。变量控制元信息,install规则决定装什么文件,cpack命令负责生成最终产物。搞懂了这三步,你就能给任何平台打包。

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