18、CPack生成器详解(上):ZIP与TGZ生成器配置、跨平台压缩包生成、文件权限与符号链接处理

各位好,欢迎来到第18讲。今天咱们聊聊CPack里最常用的两个生成器——ZIP和TGZ。

说实话,我刚开始用CPack时,觉得生成器不就是个压缩格式嘛,选哪个都一样。后来在给一个跨平台项目做交付时,被Windows和Linux上的权限问题折腾得够呛,才意识到这里面的门道有多深。

这一讲,我们就来拆解ZIP和TGZ生成器的配置细节,看看怎么生成跨平台的压缩包,以及如何处理文件权限和符号链接这些“坑”。

ZIP与TGZ生成器:基础配置

先看一个最简单的例子。假设你有一个项目,想生成一个ZIP包和一个TGZ包,CMakeLists.txt里这样写:

include(CPack)

set(CPACK_GENERATOR "ZIP;TGZ")
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")

然后运行:

cpack -G ZIP
cpack -G TGZ

嗯,就这么简单。但实际项目中,你往往需要更精细的控制。

我个人习惯把生成器配置单独放在一个CPackConfig.cmake文件里,这样主CMakeLists.txt不会太臃肿。比如:

# CPackConfig.cmake
set(CPACK_GENERATOR "ZIP;TGZ")
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A cross-platform application")

然后在主文件里include它:

include(CPackConfig.cmake)
include(CPack)

这样做的好处是,你可以为不同平台准备不同的配置片段,灵活切换。

跨平台压缩包生成:一个真实案例

我在项目中遇到过这样一个场景:项目需要在Windows、Linux和macOS上都能构建,并且生成的压缩包要能在任意平台上解压使用。

听起来简单?其实不然。Windows上的ZIP包拿到Linux上解压,文件权限全丢了;Linux上的TGZ包拿到Windows上,很多解压工具不识别符号链接。

那怎么办?我的做法是:

  • Windows平台:默认生成ZIP包,因为Windows原生支持ZIP。
  • Linux/macOS平台:默认生成TGZ包,因为tar.gz能保留文件权限和符号链接。
  • 如果需要跨平台分发:同时生成两种格式,让用户自己选。

代码实现起来也不复杂:

if(WIN32)
  set(CPACK_GENERATOR "ZIP")
elseif(APPLE)
  set(CPACK_GENERATOR "TGZ;ZIP")
else()
  set(CPACK_GENERATOR "TGZ")
endif()

你想想看,这样是不是既照顾了各平台的习惯,又保证了兼容性?

文件权限处理:TGZ的天然优势

说到文件权限,TGZ生成器有个天然优势——它原生支持Unix文件权限。你只需要在安装规则里设置好权限,CPack会自动保留。

举个例子:

install(TARGETS myapp DESTINATION bin
        PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                    GROUP_READ GROUP_EXECUTE
                    WORLD_READ WORLD_EXECUTE)

install(FILES config.ini DESTINATION etc
        PERMISSIONS OWNER_READ OWNER_WRITE
                    GROUP_READ
                    WORLD_READ)

这样生成的TGZ包里,myapp是可执行的,config.ini是只读的。解压后权限自动恢复。

但ZIP生成器呢?它不保留Unix权限。这是个硬伤。

注意:ZIP格式本身不支持Unix文件权限。如果你用ZIP包在Linux上分发,解压后所有文件都是默认权限(通常是644或755),可执行文件会丢失执行位。

那有没有办法补救?有的。你可以在安装后脚本里手动设置权限,但说实话,这很麻烦。我个人建议:如果目标平台包含Linux,优先用TGZ。

符号链接处理:小心这个“隐形炸弹”

符号链接是另一个容易出问题的地方。我记得有一次,项目里用到了动态库的符号链接(比如libfoo.so -> libfoo.so.1),结果生成的ZIP包在Windows上解压后,符号链接变成了普通文件,内容还是链接目标的内容……

为什么会这样?因为ZIP格式对符号链接的支持很有限。CPack的ZIP生成器默认会尝试保留符号链接,但跨平台时经常出问题。

TGZ就好很多。tar格式原生支持符号链接,只要你在安装时正确设置了符号链接,CPack会原样保留。

看个例子:

install(CODE [[
  execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
                  libfoo.so.1 libfoo.so
                  WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/lib)
]])

或者用更CMake的方式:

install(FILES ${CMAKE_SOURCE_DIR}/lib/libfoo.so.1 DESTINATION lib)
install(CODE [[
  file(CREATE_LINK libfoo.so.1 ${CMAKE_INSTALL_PREFIX}/lib/libfoo.so SYMBOLIC)
]])

这样生成的TGZ包里,符号链接是完整的。解压后,libfoo.so会正确指向libfoo.so.1

小技巧:如果你必须用ZIP包,又想保留符号链接,可以考虑在安装后脚本里把符号链接替换成硬链接或复制文件。但这样会增大包体积,而且失去了符号链接的灵活性。

知识体系一览

为了让你更直观地理解ZIP和TGZ生成器的核心逻辑,我画了一张图:

CPack生成器:ZIP vs TGZ 核心逻辑 CMake项目 选择生成器 ZIP生成器 特性 • Windows原生支持 • 不保留Unix权限 • 符号链接支持有限 TGZ生成器 特性 • Linux/macOS原生支持 • 保留Unix权限 • 符号链接完整保留

从图上可以看得很清楚:选择哪个生成器,取决于你的目标平台和需求。没有绝对的好坏,只有合不合适。

实战建议:我的经验总结

讲了这么多,最后给你几条实战建议:

  1. 优先用TGZ:除非你的用户全是Windows,否则TGZ是更安全的选择。权限和符号链接都能保留。
  2. ZIP只用于Windows分发:如果用户明确只需要Windows版本,ZIP没问题。但别忘了在文档里注明“解压后请手动设置可执行权限”。
  3. 同时生成两种格式:如果你做的是开源项目,建议同时提供ZIP和TGZ。用户习惯不同,多一个选择总是好的。
  4. 测试解压流程:我曾经吃过亏——生成的TGZ包在macOS上解压正常,但在某个Linux发行版上符号链接断了。后来发现是tar版本差异导致的。所以,一定要在目标平台上测试解压。

核心要点

  • ZIP生成器适合Windows平台,但不保留Unix权限和符号链接
  • TGZ生成器适合Linux/macOS平台,完整保留权限和符号链接
  • 跨平台分发时,建议同时生成两种格式
  • 符号链接处理是ZIP生成器的短板,TGZ则无此问题

好了,这一讲的内容就到这里。ZIP和TGZ是最基础的生成器,下一讲我们会继续深入其他生成器,比如DEB、RPM和NSIS,到时候再聊。


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