3、CPack生成器详解(上):Archive生成器(TGZ/TBZ2/TXZ/ZIP)、STGZ生成器(自解压)、External生成器
好,咱们接着往下聊。上一章我们把 CPack 的配置骨架搭好了,CMakeLists.txt 里该写的也都写了。但说实话,配置只是第一步,真正决定你的安装包长什么样的,是 生成器(Generator)。
CPack 的生成器,说白了就是「打包引擎」。你告诉它「我要一个压缩包」,它就去调 tar 或 zip;你告诉它「我要一个自解压安装包」,它就生成一个带解压逻辑的 Shell 脚本。我刚开始接触 CPack 时,以为生成器就是换个后缀名那么简单,结果在 Linux 上打包给 Windows 用户用,闹了笑话。嗯,这里面的门道,咱们今天一次性说清楚。
3.1 Archive 生成器:最通用的打包方式
Archive 生成器,顾名思义,就是生成各种压缩格式的归档文件。它支持 TGZ、TBZ2、TXZ、ZIP 这四种主流格式。我个人习惯把它叫做「万能生成器」,因为几乎在所有平台上都能用。
3.1.1 支持的格式与底层工具
先看一张表,把四种格式的底层依赖和适用场景说清楚:
| 生成器名称 | 文件后缀 | 底层工具 | 压缩率 | 跨平台性 |
|---|---|---|---|---|
| TGZ | .tar.gz | tar + gzip | 中等 | Linux/macOS 原生,Windows 需额外工具 |
| TBZ2 | .tar.bz2 | tar + bzip2 | 较高 | 同上,但 bzip2 在 Windows 上更少见 |
| TXZ | .tar.xz | tar + xz | 最高 | Linux 主流,macOS 需安装 xz-utils |
| ZIP | .zip | zip/unzip | 中等 | Windows 原生支持,所有平台通用 |
你可能会问:「那我该选哪个?」我的建议是:面向 Linux 用户,优先用 TGZ;面向 Windows 用户,必须用 ZIP。为什么?因为 Windows 用户拿到一个 .tar.gz 文件,大概率会愣住——「这玩意儿怎么打开?」我在项目中就遇到过,给客户发了一个 TGZ 包,对方回邮件问「是不是发错了,我要的是安装包」。从那以后,只要目标平台有 Windows,我一定加一个 ZIP 生成。
3.1.2 配置示例与避坑
配置 Archive 生成器非常简单。在 CMakeLists.txt 里加上一行:
set(CPACK_GENERATOR "TGZ" "ZIP")
然后运行 cpack,你就会在构建目录下看到两个文件:MyProject-1.0.0-Linux.tar.gz 和 MyProject-1.0.0-Linux.zip。
但这里有个坑:文件名中的架构信息。默认情况下,CPack 会把当前机器的架构(比如 x86_64)加到文件名里。如果你在 CI 上打包,想生成一个「无架构」的通用包,怎么办?
我曾经被这个坑过——在 Jenkins 上打出来的包叫 MyProject-1.0.0-Linux-x86_64.tar.gz,结果测试人员拿到 ARM 机器上,一看文件名就以为包不对。其实内容是一样的,但名字误导人。
解决办法是手动覆盖文件名模板:
set(CPACK_PACKAGE_FILE_NAME "MyProject-${CPACK_PACKAGE_VERSION}")
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
这样生成的文件名就变成了 MyProject-1.0.0.tar.gz,干净利落。
CPACK_GENERATOR 设置多个值。但要注意,ZIP 格式在 Linux 上需要安装 zip 工具,否则会报错。我一般会在 CI 脚本里先检查一下:which zip || apt-get install -y zip。
3.2 STGZ 生成器:自解压的 Shell 脚本
STGZ 全称是 Self-Extracting Tar GZip,说白了就是一个 Shell 脚本 + 压缩包 的合体。用户拿到一个 .sh 文件,直接 sh MyProject.sh 就能解压安装,不需要任何额外工具。
你想想看,这玩意儿在什么场景下最有用?给 Linux 新手用户分发软件。你不需要教他们怎么解压 tar.gz,也不需要让他们装 zip,一个脚本搞定一切。我记得有一次给一个嵌入式团队做工具链分发,对方工程师对 Linux 不熟,我直接扔了一个 STGZ 包过去,他们双击运行就装好了,省了我不少售后时间。
3.2.1 配置与定制
启用 STGZ 生成器同样简单:
set(CPACK_GENERATOR "STGZ")
生成的文件名默认是 MyProject-1.0.0-Linux.sh。运行 cpack 后,你会得到一个可执行的 Shell 脚本。
但这里有个关键点:STGZ 生成器允许你定制安装过程中的提示信息。比如你想在解压前显示一段欢迎语,或者让用户选择安装路径,都可以通过 CPack 变量实现:
set(CPACK_STGZ_INSTALL_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/stgz_install.sh")
set(CPACK_STGZ_PROMPT "欢迎安装 MyProject!请输入安装路径 [默认: /usr/local]: ")
其中 stgz_install.sh 是你自己写的脚本,可以包含任何 Shell 命令。我一般会在里面加一个检查依赖的逻辑:
#!/bin/sh
# 检查是否安装了 libfoo
if ! ldconfig -p | grep libfoo > /dev/null 2>&1; then
echo "错误:未检测到 libfoo,请先安装。"
exit 1
fi
cat 或 less 查看内容。所以不要在脚本里放任何敏感信息(比如密码、密钥)。我曾经见过有人把数据库密码硬编码在 STGZ 脚本里,结果被用户直接看到了……嗯,那场面挺尴尬的。
3.2.2 STGZ 的局限性
STGZ 虽然方便,但也不是万能的。它有几个硬伤:
- 只支持 Linux/macOS:Windows 上没法直接运行 .sh 脚本。
- 解压速度慢:因为脚本里内嵌了 base64 编码的压缩包,解压时需要先解码再解压,比直接解压 tar.gz 慢不少。
- 文件体积大:base64 编码会让体积增加约 33%。如果你的安装包本身就有几百 MB,那 STGZ 就不太合适了。
所以我的建议是:STGZ 适合小工具、脚本库、或者给非技术用户的分发。如果是大型软件,还是老老实实用 Archive 生成器吧。
3.3 External 生成器:接入第三方打包工具
External 生成器,说白了就是一个「桥接器」。它允许你调用外部的打包工具,比如 NSIS(Windows 安装包)、PackageMaker(macOS .pkg)、或者你自己写的打包脚本。
你可能会问:「CPack 自己不是能生成安装包吗?为什么还要用 External?」原因很简单:有些场景 CPack 搞不定。比如你想生成一个带图形界面的 Windows 安装向导,或者你想生成一个 macOS 的 .dmg 文件,CPack 原生不支持这些格式。这时候,External 生成器就派上用场了。
3.3.1 配置方式
External 生成器的配置稍微复杂一点,因为它需要你提供一个外部的打包脚本。基本配置如下:
set(CPACK_GENERATOR "External")
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/external_package.cmake")
然后在 external_package.cmake 里,你可以调用任何外部命令。比如调用 NSIS 生成 Windows 安装包:
# external_package.cmake
set(NSIS_SCRIPT "${CMAKE_SOURCE_DIR}/installer.nsi")
execute_process(
COMMAND makensis ${NSIS_SCRIPT}
WORKING_DIRECTORY ${CPACK_TEMPORARY_DIRECTORY}
RESULT_VARIABLE NSIS_RESULT
)
if(NOT NSIS_RESULT EQUAL 0)
message(FATAL_ERROR "NSIS 打包失败!")
endif()
我在项目中用过 External 生成器来对接 Inno Setup(Windows 安装工具)。当时客户要求安装包必须带一个「选择组件」的界面,CPack 原生做不到,我就写了一个 Inno Setup 脚本,通过 External 生成器调用它。效果还不错,就是调试起来有点麻烦——你得先确保外部工具在 PATH 里。
3.3.2 适用场景总结
External 生成器适合以下场景:
- 需要生成特定平台的安装包(如 .exe、.msi、.dmg)
- 需要复杂的安装逻辑(如多语言、自定义组件、注册表操作)
- 需要集成已有的打包工具链(比如公司内部自研的打包工具)
但要注意,External 生成器有一个明显的缺点:可移植性差。你的 CMakeLists.txt 里如果用了 External 生成器,那其他开发者构建时,必须安装对应的外部工具。否则 CPack 会报错。所以我的建议是:只在 CI 或特定平台上启用 External 生成器,不要把它作为默认配置。
3.4 三种生成器的对比与选择
最后,咱们用一张表总结一下这三种生成器的特点:
| 生成器 | 输出格式 | 跨平台性 | 定制能力 | 推荐场景 |
|---|---|---|---|---|
| Archive | .tar.gz / .zip 等 | 高(ZIP 全平台通用) | 低(仅控制文件名和压缩级别) | 通用分发、CI 产物、库文件 |
| STGZ | .sh(自解压脚本) | 中(仅 Linux/macOS) | 中(可定制安装提示和前置脚本) | 小工具分发、非技术用户 |
| External | 任意(由外部工具决定) | 低(依赖外部工具) | 高(完全自定义) | 平台专属安装包、复杂安装逻辑 |
我个人习惯是:默认用 Archive 生成 TGZ 和 ZIP,覆盖 90% 的场景。如果目标用户是 Linux 新手,再加一个 STGZ。只有遇到特殊需求(比如 Windows 安装向导),才动用 External 生成器。你想想看,这样既保证了通用性,又不会把配置搞得太复杂。
嗯,这一章的内容就到这里。下一章咱们继续聊剩下的生成器——包括 NSIS、PackageMaker、以及 RPM/DEB 这些包管理器的打包方式。到时候你会发现,CPack 的生态比你想的要丰富得多。