第二章:CPack核心概念——生成器、包类型、组件与安装规则

好,咱们进入正题。CPack 这东西,说白了就是 CMake 的打包小弟。你负责编译,它负责把编译好的东西收拾整齐,装进一个包里。但想用好它,你得先搞懂四个核心概念:生成器、包类型、组件、安装规则。

这四个概念,我当年刚接触时也绕了一阵子。尤其是生成器和包类型,名字长得像,但干的活完全不同。咱们一个一个来拆。

2.1 生成器(Generator)——打包的“后端引擎”

生成器,就是 CPack 用来生成安装包的那个“后端”。它决定了最终产出什么格式的文件。比如 .tar.gz、.deb、.rpm、.dmg,这些都是不同生成器干的事。

我个人习惯把生成器分成两类:

  • 平台原生生成器:比如 DEB(Debian/Ubuntu)、RPM(Red Hat/Fedora)、NSIS(Windows)、DragNDrop(macOS)。它们生成的包可以直接在对应系统上安装。
  • 跨平台生成器:比如 TGZ、STGZ、ZIP。这些格式通用性强,但需要用户手动解压或运行脚本。

你可以在 CMakeLists.txt 里这样指定生成器:

set(CPACK_GENERATOR "TGZ;DEB")

嗯,这里要注意:生成器可以同时指定多个。CPack 会依次调用每个生成器,生成对应的包。我在项目中遇到过,同时生成 TGZ 和 DEB 包,方便不同用户使用。

小技巧: 如果你不确定当前平台支持哪些生成器,可以运行 cpack --help 查看列表。我经常这么干。

2.2 包类型(Package Type)——你最终拿到的是什么

包类型,其实就是生成器产出的“文件类型”。它和生成器是一一对应的。比如:

生成器 包类型 典型后缀
TGZ tar.gz 压缩包 .tar.gz
DEB Debian 软件包 .deb
RPM Red Hat 软件包 .rpm
NSIS Windows 安装程序 .exe
DragNDrop macOS 磁盘映像 .dmg

说白了,生成器是“怎么造”,包类型是“造出啥”。你想想看,如果你只指定了 TGZ 生成器,那最终用户拿到的就是一个 .tar.gz 文件。如果你指定了 DEB,那用户就能用 apt install 直接装。

我建议,在跨平台项目里,至少保留一个通用包类型(比如 TGZ),再根据目标平台添加原生包类型。这样兼容性最好。

2.3 组件(Component)——把安装内容分门别类

组件,是 CPack 里一个非常实用的概念。它允许你把安装内容分成多个逻辑单元。比如:

  • Runtime:运行时库和可执行文件
  • Development:头文件、静态库、CMake 配置文件
  • Documentation:文档和示例
  • Tests:测试用例和数据

为什么要分组件?因为用户可能只需要运行时,不需要开发文件。或者你只想发布一个轻量级的包给最终用户。

在 CMake 里,你可以用 COMPONENT 关键字来标记安装目标:

install(TARGETS mylib
  LIBRARY DESTINATION lib
  COMPONENT Runtime
)

install(FILES mylib.h
  DESTINATION include
  COMPONENT Development
)

然后,在 CPack 配置里,你可以指定打包哪些组件:

set(CPACK_COMPONENTS_ALL Runtime Development)

我曾经踩过一个坑:忘记设置 CPACK_COMPONENTS_ALL,结果 CPack 把所有组件都打包进去了,包体积大了三倍。嗯,从那以后我每次都会检查这个变量。

注意: 如果你使用了组件,但没设置 CPACK_COMPONENTS_ALL,CPack 会默认打包所有组件。这可能导致包体积失控。

2.4 安装规则(Install Rules)——打包的“地基”

安装规则,是 CPack 打包的基础。没有安装规则,CPack 根本不知道要打包什么。说白了,install() 命令就是告诉 CMake:哪些文件、装到哪个目录、属于哪个组件。

常见的安装规则有:

  • 安装目标:install(TARGETS ...)
  • 安装文件:install(FILES ...)
  • 安装目录:install(DIRECTORY ...)
  • 安装脚本:install(SCRIPT ...)

举个例子,一个典型的安装规则长这样:

install(TARGETS myapp
  RUNTIME DESTINATION bin
  COMPONENT Runtime
)

install(FILES README.md LICENSE
  DESTINATION share/doc/myapp
  COMPONENT Documentation
)

install(DIRECTORY config/
  DESTINATION etc/myapp
  COMPONENT Runtime
)

这里要注意 DESTINATION 的路径。它通常是相对于安装前缀的。默认安装前缀是 /usr/local,但你可以通过 CMAKE_INSTALL_PREFIX 修改。

我个人习惯在项目根目录的 CMakeLists.txt 里统一管理安装规则,而不是分散在各个子目录。这样维护起来方便,也容易发现遗漏。

核心要点: 安装规则决定了“包的内容”,生成器决定了“包的格式”,组件决定了“内容的组织方式”。三者缺一不可。

2.5 它们如何协同工作?

咱们用一张图来理清关系:

安装规则 install() 命令 组件 Runtime / Development 归类 生成器 TGZ / DEB / RPM 选择打包内容 包类型 产出

流程很清楚:

  1. 你通过 install() 定义安装规则,指定文件装到哪里、属于哪个组件。
  2. CPack 根据组件配置,筛选出要打包的内容。
  3. 生成器读取这些内容,按照指定格式生成安装包。
  4. 最终产出对应包类型的文件。

举个例子,假设你有一个库项目,你想同时发布 .tar.gz 和 .deb 包。你只需要:

  • 定义好安装规则,把库文件、头文件、文档分别归入 Runtime、Development、Documentation 组件。
  • 设置 CPACK_GENERATOR 为 TGZ;DEB。
  • 设置 CPACK_COMPONENTS_ALL 为 Runtime;Development(文档单独打包)。

运行 cpack 后,你就会得到两个包:一个 .tar.gz,一个 .deb。每个包里都只包含 Runtime 和 Development 组件的内容。

避坑指南: 我曾经在生成 DEB 包时,忘记设置 CPACK_DEBIAN_PACKAGE_MAINTAINER,结果打包失败。这些元信息变量,一定要提前配好。

好了,这四个核心概念就讲到这里。下一章咱们会深入生成器的具体配置,包括如何为不同平台定制打包参数。到时候我会分享一些实际项目里的配置模板,保证你能直接拿来用。