15、CPack入门:CPack是什么?CPack的工作流程、支持的打包格式(ZIP/TGZ/DEB/RPM/NSIS)

说实话,很多用CMake的人,都只停留在「编译通过就行」的阶段。

项目写完了,怎么发给别人?

扔一堆源码让人家自己编译?还是手动压缩一下?

嗯,我早年就是这么干的。直到有一次,我给客户发了个zip包,里面少了个动态库,对方直接打电话过来骂了半小时……从那以后,我老老实实学起了CPack。

CPack是什么?

CPack是CMake官方自带的打包工具。它不是一个独立的软件,而是CMake的一部分。你装好CMake,CPack就有了。

它的核心作用就一句话:把编译好的产物,打包成各种格式的分发包

说白了,你写了一个库或者一个工具,编译完了有一堆二进制文件、头文件、配置文件。CPack帮你把这些东西整理好,塞进一个zip、一个tar.gz、一个deb包,甚至一个Windows安装程序里。

核心理解:CMake负责「怎么编译」,CPack负责「怎么分发」。两者配合,一条命令从源码到安装包全部搞定。

CPack的工作流程

CPack的工作流程其实不复杂。我拆成三步来讲,你跟着走一遍就明白了。

第一步:在CMakeLists.txt里配置打包信息

你需要在项目的CMakeLists.txt里告诉CPack:项目叫什么、版本号多少、维护者是谁、许可证是什么。这些信息通过set()命令设置,变量名都有固定前缀CPACK_

# 最基本的CPack配置
set(CPACK_PACKAGE_NAME "MyTool")
set(CPACK_PACKAGE_VERSION "1.2.3")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A handy tool for doing things")
set(CPACK_PACKAGE_CONTACT "support@mycompany.com")

# 必须包含这个模块
include(CPack)

注意最后一行include(CPack)。没有这一行,CPack根本不会生效。我见过有人配了一大堆变量,忘了写这一句,然后跑来问我为什么打不了包……

第二步:指定要安装哪些文件

CPack打包的内容,来源于CMake的install()命令。你之前用install()指定了安装规则,CPack就照着这个规则去收集文件。

# 安装可执行文件
install(TARGETS mytool DESTINATION bin)

# 安装头文件
install(FILES mytool.h DESTINATION include)

# 安装配置文件
install(FILES mytool.conf DESTINATION etc)

这里有个坑:CPack只打包install()指定的内容。如果你忘了写install(),CPack会给你一个空包。我曾经在给一个开源库打deb包时,漏掉了动态库的install规则,结果打出来的deb只有几百字节……

第三步:运行CPack命令

配置好之后,在构建目录里运行:

# 先构建项目
cmake --build .

# 然后打包,默认生成当前平台最常用的格式
cpack

# 或者指定格式
cpack -G ZIP
cpack -G TGZ
cpack -G DEB
cpack -G RPM
cpack -G NSIS

就这么简单。一条命令,所有文件自动打包好。

我的习惯:在CI脚本里,我会同时生成ZIP和TGZ两种格式。ZIP给Windows用户,TGZ给Linux用户。这样不管对方用什么系统,都能直接解压使用。

CPack支持的打包格式

CPack支持的格式很多。我把最常用的几个列出来,你根据目标用户选就行。

格式名称 CPack生成器 适用平台 典型场景
ZIP ZIP 跨平台 通用压缩包,Windows用户最爱
TGZ TGZ 跨平台 通用压缩包,Linux/macOS用户常用
DEB DEB Debian/Ubuntu Linux发行版软件包,支持依赖管理
RPM RPM Red Hat/Fedora/CentOS Linux发行版软件包,支持依赖管理
NSIS NSIS Windows Windows安装程序,带图形界面向导
DMG DragNDrop macOS macOS磁盘映像,拖拽安装

ZIP和TGZ:最通用的格式

这两种格式不需要任何额外工具。CPack直接用zlib库就能生成。你想想看,用户拿到压缩包,解压就能用,多方便。

我个人建议:如果你不确定用户用什么系统,优先打ZIP包。Windows和Linux都能解压ZIP,兼容性最好。

DEB和RPM:Linux发行版的标准格式

这两种格式需要系统上安装对应的打包工具。DEB需要dpkg-deb,RPM需要rpmbuild

为什么要用DEB/RPM?因为它们支持依赖管理。你的程序可能依赖libssl、libcurl之类的系统库。用DEB包的话,用户安装时系统会自动检查并安装这些依赖。这一点,ZIP和TGZ做不到。

注意:打DEB包时,记得设置CPACK_DEBIAN_PACKAGE_DEPENDS变量。我曾经漏掉了一个依赖,结果用户装完包运行时报错说找不到动态库……那场面,挺尴尬的。

# DEB包依赖配置示例
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl-dev (>= 1.1), libcurl4 (>= 7.58)")

NSIS:Windows安装程序

NSIS(Nullsoft Scriptable Install System)是Windows上最流行的安装程序制作工具之一。CPack可以自动生成NSIS脚本并调用NSIS编译器生成.exe安装包。

NSIS包的好处是:用户双击安装,有图形界面,可以选择安装路径、创建桌面快捷方式、添加环境变量。对于企业级软件分发,这是标配。

# NSIS配置示例
set(CPACK_NSIS_DISPLAY_NAME "MyTool 1.2.3")
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\MyTool.lnk' '$INSTDIR\\\\bin\\\\mytool.exe'")

嗯,这里要注意:NSIS本身需要单独安装。CPack只是帮你生成脚本,真正的编译工作还是交给NSIS工具链完成的。

一张图看懂CPack

下面这张图展示了CPack在整个构建流程中的位置,以及它如何把不同格式的安装包输出给最终用户。

CPack工作流程与打包格式 CMakeLists.txt 配置 + install() 规则 cmake --build . 编译生成二进制文件 cpack 自动收集文件并打包 ZIP 通用压缩包 TGZ 通用压缩包 DEB Debian/Ubuntu RPM Red Hat/Fedora NSIS Windows安装程序 最终用户 解压即用 / 双击安装 / apt install

如何选择打包格式?

这个问题没有标准答案。我根据经验给你几个建议:

  • 给开发者用的工具库:打ZIP或TGZ包。开发者不介意手动解压,他们更关心能不能快速拿到头文件和库文件。
  • 给普通用户用的桌面软件:Windows上打NSIS包,macOS上打DMG包。用户习惯双击安装,别让他们碰命令行。
  • 给服务器部署用的服务:打DEB或RPM包。运维人员需要依赖管理和版本控制,apt/yum安装是标准流程。
  • 跨平台分发:同时打ZIP和TGZ。我在GitHub Release里经常这么干,一个项目挂两个包,用户自己选。

一个小技巧:你可以用cpack -G "ZIP;TGZ;DEB"一次生成多种格式。CPack会依次调用各个生成器,省得你一条一条敲命令。

避坑指南

最后分享几个我踩过的坑:

  • 忘了include(CPack):这是最基础的错误。没有这一行,你配再多变量都没用。
  • install()路径写错:CPack打包时,文件路径是相对于CMAKE_INSTALL_PREFIX的。如果你写死了绝对路径,换台机器就废了。
  • DEB包版本号格式:Debian的版本号有严格规范,不能有连字符以外的特殊符号。我试过在版本号里加了下划线,结果dpkg直接报错。
  • NSIS需要单独安装:CPack不会帮你装NSIS。如果你在CI环境里打NSIS包,记得先装好NSIS工具链。

CPack入门其实不难。你只要记住:先配install(),再配CPACK变量,最后跑cpack。这三步走完,你的项目就有了专业的分发包。


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