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.zip和MyApp-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命令调用,最后生成各种格式的安装包。
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