4. CPack生成器详解(上):ZIP生成器、TGZ生成器、STGZ生成器(自解压压缩包)

好,咱们今天来聊聊CPack生成器里最常用的三个家伙:ZIP、TGZ和STGZ。说实话,这三个生成器是我在实际项目中打交道最多的。为什么?因为跨平台分发软件,压缩包是最朴素、最通用的方式。你想想看,不管用户用的是Windows、macOS还是Linux,总得有个办法把编译好的二进制文件、库文件、配置文件一股脑儿塞给人家吧?

我个人习惯把这三个生成器归为一类——「压缩包派」。它们本质上都是把安装产物打包成一个文件,区别只在于压缩算法和自解压能力。嗯,咱们一个一个来看。

4.1 ZIP生成器:跨平台的「万金油」

ZIP生成器,说白了就是把你整个安装目录压缩成一个.zip文件。这东西的好处不用我多说——几乎所有的操作系统都原生支持解压ZIP。Windows资源管理器直接双击打开,macOS自带归档工具,Linux也有unzip命令。

我在项目中遇到过最典型的场景:给Windows用户分发一个绿色版工具。用户下载后解压就能用,不需要安装,不需要管理员权限。省心。

配置起来也很简单,在CMakeLists.txt里加上这么几行:

set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}")

这里有个小细节要注意——CPACK_PACKAGE_FILE_NAME决定了最终压缩包的文件名。我建议你把操作系统名称加进去,不然用户拿到一个myapp-1.0.0.zip,根本不知道是给哪个平台用的。

小技巧: 如果你在Windows上打包,ZIP生成器会自动处理文件权限。但如果你在Linux上生成ZIP包,记得检查一下可执行文件的权限位是否保留。我曾经踩过这个坑——在Linux上打的ZIP包,Windows用户解压后所有文件都变成了只读。

4.2 TGZ生成器:Linux世界的「老大哥」

TGZ生成器,其实就是tar.gz。在Linux和macOS生态里,这玩意儿比ZIP更常见。为什么?因为tar格式能完美保留文件权限、符号链接、所有权信息。你想想看,一个Linux守护进程,安装后需要设置setuid位,或者需要创建软链接指向共享库——这些用ZIP就搞不定。

配置方式和ZIP几乎一样:

set(CPACK_GENERATOR "TGZ")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}")

但这里有个坑——TGZ生成器默认使用gzip压缩。如果你的项目特别大(比如几百兆的二进制文件),压缩时间会很长。我个人建议,如果对压缩率要求不高,可以换成STGZ或者干脆用ZIP。TGZ的压缩率虽然高,但打包速度确实慢一些。

我记得有一次给客户交付一个嵌入式Linux的SDK,整个包有1.2GB。用TGZ打包花了将近5分钟,压缩后只有400MB。客户那边解压又花了3分钟。后来我换成了ZIP,打包时间缩短到40秒,压缩后500MB。客户反馈说解压也快多了。嗯,有时候压缩率不是唯一指标。

注意: TGZ生成器在Windows上可能不太好使。虽然CPack支持在Windows上生成TGZ包,但你需要安装额外的工具(比如7-Zip或者Cygwin的tar)。我一般只在Linux和macOS上使用TGZ。

4.3 STGZ生成器:自解压的「黑科技」

STGZ,全称是Self-Extracting TGZ。说白了,就是把一个shell脚本和一个TGZ压缩包粘在一起。用户拿到这个文件后,直接chmod +x然后运行,就能自动解压并执行安装脚本。

这东西在Linux世界里特别有用。你想想看,有些用户可能连tar -xzf都懒得敲,或者干脆不知道这个命令。给一个.run文件,双击就能装,多省事。

配置STGZ生成器:

set(CPACK_GENERATOR "STGZ")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-linux-x64")
set(CPACK_STGZ_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/stgz_install.sh")

这里CPACK_STGZ_SCRIPT指向一个自定义的安装脚本。这个脚本会在解压完成后执行,你可以用它来做一些额外的事情,比如创建用户、设置环境变量、注册服务等等。

我曾经给一个内部工具做过STGZ包。那个工具需要把二进制文件放到/opt/myapp,还要在/etc/profile.d里加一个环境变量脚本。用STGZ自解压+自定义脚本,一条命令搞定所有事情。运维同事反馈说「这玩意儿真香」。

核心区别总结:
  • ZIP:跨平台通用,Windows友好,不保留Unix权限
  • TGZ:Linux/macOS原生,保留权限和符号链接,压缩率高
  • STGZ:自解压,用户体验好,适合给非技术用户分发

4.4 如何选择?我的经验法则

说了这么多,到底该用哪个?我根据自己的项目经验,整理了一个简单的选择矩阵:

场景 推荐生成器 理由
Windows桌面应用分发 ZIP 用户双击就能解压,不需要额外工具
Linux服务器软件包 TGZ 保留权限,适合root用户安装
给非技术用户的分发 STGZ 一条命令安装,体验最好
跨平台SDK ZIP + TGZ 同时提供两种格式,覆盖所有用户
大型项目(>500MB) ZIP 打包和解压速度更快

你可能会问:「能不能同时生成多个格式?」当然可以。CPack支持一次生成多种格式的包:

set(CPACK_GENERATOR "ZIP;TGZ;STGZ")

这样执行cpack命令后,你会得到三个包:.zip、.tar.gz和.sh。用户喜欢哪个就用哪个。

避坑指南: 我曾经同时生成ZIP和TGZ,结果发现两个包的文件名一模一样(除了后缀)。用户下载时经常搞混。后来我强制在文件名里加上了生成器标识:${PROJECT_NAME}-${PROJECT_VERSION}-${CPACK_GENERATOR}。这样文件名就变成了myapp-1.0.0-ZIP.zip和myapp-1.0.0-TGZ.tar.gz,一目了然。

4.5 一个完整的例子

最后,给你看一个我实际项目中用过的CMakeLists.txt片段。这个项目是一个跨平台的命令行工具,需要同时提供ZIP和TGZ包:

# 设置版本号
set(PROJECT_VERSION_MAJOR 2)
set(PROJECT_VERSION_MINOR 1)
set(PROJECT_VERSION_PATCH 0)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")

# 安装目标
install(TARGETS mytool DESTINATION bin)
install(FILES README.md LICENSE DESTINATION share/doc/mytool)

# CPack配置
set(CPACK_PACKAGE_NAME "mytool")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A handy command-line tool")

# 生成器选择
if(WIN32)
  set(CPACK_GENERATOR "ZIP")
  set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-win64")
elseif(APPLE)
  set(CPACK_GENERATOR "TGZ")
  set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-macos")
else()
  set(CPACK_GENERATOR "TGZ;STGZ")
  set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-linux-x64")
  set(CPACK_STGZ_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/post_install.sh")
endif()

include(CPack)

你看,这里根据不同的操作系统选择了不同的生成器。Windows用户拿到ZIP包,macOS用户拿到TGZ包,Linux用户则同时有TGZ和STGZ两种选择。嗯,这就是我所谓的「因地制宜」。

好了,关于ZIP、TGZ和STGZ生成器,咱们就聊到这里。下一章我会继续讲剩下的几个生成器——NSIS、DEB和RPM。到时候咱们再聊聊安装包和系统包管理器的那些事儿。