1、CPack入门:CPack是什么、CPack在CMake生态中的位置、CPack与CMake的关系、CPack的工作流程
1.1 CPack到底是什么?
CPack,说白了就是CMake自带的打包工具。
你写完了代码,用CMake构建好了项目,生成了可执行文件、库文件、配置文件……然后呢?怎么把这些东西发给用户?
手动压缩?写脚本?维护多个平台的安装包格式?
嗯,CPack就是来解决这个问题的。
我个人习惯把CPack理解为「CMake的打包插件」。它不负责编译,不负责链接,只负责一件事:把你构建好的产物,打包成用户能直接安装的格式。
比如:
- Windows上的.exe安装包(NSIS、WiX)
- macOS上的.dmg
- Linux上的.deb、.rpm、.tar.gz
我在项目中遇到过最尴尬的情况:代码写得挺好,CMakeLists.txt也写得挺规范,结果交付的时候,运维说「你这包怎么装不上?」。后来才发现,是CPack的依赖声明漏了。从那以后,我对CPack的每个字段都不敢马虎。
1.2 CPack在CMake生态中的位置
CMake生态圈里,有几个核心工具,我画个图帮你理清关系:
你看,CMake是核心,它负责生成构建系统。CTest负责测试,CDash负责展示测试结果,而CPack负责打包。这四个工具组成了CMake的完整工具链。
你想想看,如果没有CPack,你要怎么交付?写Shell脚本?用Python的打包库?维护一套自己的打包逻辑?
CPack的好处就在于:它和CMake共享同一套变量和配置。你在CMakeLists.txt里定义的项目信息、安装规则、依赖关系,CPack可以直接拿来用。
核心观点:CPack不是独立工具,它是CMake生态的「最后一公里」。没有它,你的项目就停留在「能编译」的阶段,到不了「能安装」的阶段。
1.3 CPack与CMake的关系
很多人刚接触CPack时会问:CPack是CMake的一部分吗?还是独立的?
答案是:CPack是CMake的模块,不是独立工具。
具体来说:
- CPack的配置写在CMakeLists.txt里,或者单独的CPackConfig.cmake文件里
- CPack使用CMake的
install()命令定义的安装规则 - CPack读取CMake的变量,比如
CMAKE_INSTALL_PREFIX、PROJECT_NAME、PROJECT_VERSION
我举个例子你就明白了:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyApp VERSION 1.2.3)
# 安装可执行文件
install(TARGETS myapp DESTINATION bin)
# 安装配置文件
install(FILES config.ini DESTINATION etc)
# 启用CPack
include(CPack)
# 设置打包参数
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_GENERATOR "DEB;RPM;TGZ")
看到了吗?include(CPack) 这一行,就把CPack模块加载进来了。之后你运行 cpack 命令,它就会读取这些配置,生成对应的安装包。
小技巧:我个人习惯把CPack的配置单独放到 CPackConfig.cmake 文件里,然后在CMakeLists.txt中通过 include(CPackConfig.cmake) 引入。这样主构建文件不会太臃肿,打包相关的配置也容易维护。
1.4 CPack的工作流程
CPack的工作流程,其实就三步。我画个流程图帮你理解:
流程其实很简单:
- 先构建:把代码编译成可执行文件和库文件
- 再安装:把构建产物安装到一个临时目录(staging directory)
- 最后打包:CPack读取安装目录里的文件结构,按照你指定的格式打包
这里有个关键点,我曾经踩过坑:CPack打包时,依赖的查找路径和运行时不一样。
举个例子,你的程序依赖libfoo.so,编译时能找到,但CPack打包时,它不会自动把libfoo.so打进包里。你需要显式地用install()命令把依赖的库也安装到临时目录。
避坑指南:我曾经有一个项目,在开发机上跑得好好的,打包发给客户后,客户说「缺少libssl.so.1.1」。查了半天,发现是CPack没有把OpenSSL的运行时库打包进去。从那以后,我养成了一个习惯:每次打包前,先用 ldd 检查一下可执行文件的依赖,确保所有运行时库都被 install() 覆盖到了。
1.5 一个完整的CPack入门示例
光说不练假把式。我们来看一个完整的例子:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(HelloCPack VERSION 1.0.0 LANGUAGES CXX)
# 添加可执行文件
add_executable(hello main.cpp)
# 安装规则
install(TARGETS hello DESTINATION bin)
install(FILES README.md DESTINATION share/doc/HelloCPack)
# ---------- CPack 配置 ----------
set(CPACK_PACKAGE_NAME "HelloCPack")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A simple CPack demo")
set(CPACK_PACKAGE_VENDOR "My Company")
set(CPACK_PACKAGE_CONTACT "support@mycompany.com")
# 指定打包格式
set(CPACK_GENERATOR "TGZ;DEB")
# 如果是DEB包,需要额外配置
if(CPACK_GENERATOR MATCHES "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "developer@mycompany.com")
set(CPACK_DEBIAN_PACKAGE_SECTION "devel")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
endif()
# 加载CPack模块(必须放在所有配置之后)
include(CPack)
构建和打包命令:
# 构建
mkdir build && cd build
cmake ..
cmake --build .
# 打包
cpack -G TGZ # 生成 .tar.gz
cpack -G DEB # 生成 .deb
运行后,你会在build目录下看到:
HelloCPack-1.0.0-Linux.tar.gzHelloCPack-1.0.0-Linux.deb
提示:如果你在Windows上,可以试试 cpack -G NSIS 生成exe安装包。NSIS是CPack默认支持的Windows安装包生成器,配置起来也很简单。
1.6 小结
这一章我们聊了:
- CPack是CMake的打包模块,不是独立工具
- 它在CMake生态中负责「最后一公里」的交付
- 它和CMake共享配置,通过
include(CPack)启用 - 工作流程:构建 → 安装到临时目录 → 打包
下一章,我们会深入CPack的配置体系,聊聊那些让你头疼的CPACK_XXX变量到底该怎么配。嗯,到时候我会分享一些我踩过的坑,保证让你少走弯路。