企业级CPack打包方案原理与实践 - 第1节
CPack入门:它到底是什么?
说实话,我第一次接触CPack的时候,心里想的是:「CMake不是已经能编译项目了吗?怎么又冒出个打包工具?」
后来我才明白,编译成功和交付给用户,中间还差着十万八千里。你想想看,你辛辛苦苦写了个C++库,同事要拿去用,总不能让人家自己从头编译吧?这时候就需要一个打包工具,把你的二进制文件、头文件、配置文件、依赖库,一股脑儿塞进一个规范的安装包里。
CPack,就是CMake官方提供的打包工具。它本身不干活,它是个「包工头」——负责调度各种后端打包程序,比如:
- Archive:生成 .tar.gz、.zip 这类压缩包
- RPM:给Red Hat系Linux用的
- DEB:给Debian/Ubuntu用的
- NSIS:Windows下的安装程序
- DragNDrop:macOS的.dmg安装包
说白了,你只需要写一份CPack配置,它就能帮你生成各种平台下的安装包。我在项目中遇到过最头疼的事,就是给同一个库打RPM和DEB包,手动维护两套spec文件简直要命。后来换成CPack,一份CMakeLists.txt搞定所有。
核心要点:CPack不是打包引擎,而是打包编排器。它把CMake的安装规则翻译成各种打包格式的配置。
CMake与CPack的关系
很多人搞不清这两者的关系。我打个比方:
- CMake是建筑师,负责设计房子结构、画图纸、指挥施工队把房子盖起来
- CPack是搬家公司的调度员,负责把盖好的房子里的家具(二进制文件、库、头文件)打包好,运到客户手里
它们之间通过install()命令来衔接。你在CMakeLists.txt里用install()声明了哪些文件要安装、安装到哪个目录,CPack就照着这个清单去打包。
来看一个最简单的例子:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyLib VERSION 1.0.0)
add_library(mylib SHARED src/mylib.cpp)
# 告诉CMake:编译好的库要安装到 lib/ 目录
install(TARGETS mylib
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
# 告诉CPack:我要打一个DEB包
set(CPACK_GENERATOR "DEB")
set(CPACK_PACKAGE_NAME "mylib")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "me@example.com")
include(CPack)
然后你只需要:
mkdir build && cd build
cmake ..
cpack
一个 .deb 包就生成了。嗯,就这么简单。
个人习惯:我一般会在CMakeLists.txt底部单独放一个# Packaging区块,把所有CPack相关的配置集中管理。这样找起来方便,也避免跟构建逻辑混在一起。
CPack在持续集成中的角色
说到CI/CD,CPack简直就是为自动化而生的。你想想看,每次代码提交后,CI服务器自动编译、自动跑测试、自动打包——这一套流程里,CPack就是那个「自动打包」环节的关键角色。
我曾在某大厂维护过一个C++基础库,每天有几十次提交。如果没有CPack,每次发版都要手动登录服务器,敲一堆命令打RPM包。后来我写了个Jenkins Pipeline,把CPack集成进去:
# Jenkinsfile 片段
stage('Build') {
steps {
sh 'cmake -B build -DCMAKE_BUILD_TYPE=Release'
sh 'cmake --build build --parallel'
}
}
stage('Test') {
steps {
sh 'cd build && ctest --output-on-failure'
}
}
stage('Package') {
steps {
sh 'cd build && cpack -G RPM'
sh 'cd build && cpack -G DEB'
sh 'cd build && cpack -G TGZ'
}
}
stage('Publish') {
steps {
// 把生成的包上传到制品仓库
sh 'nexus-upload build/*.rpm build/*.deb build/*.tar.gz'
}
}
你看,一行cpack -G RPM就搞定了RPM包的生成。而且CPack支持同时生成多种格式,一次构建,到处分发。
我曾经踩过的坑:在CI里用CPack时,一定要确保install()命令的DESTINATION路径是相对路径,不要写死成/usr/local这种绝对路径。否则打包出来的安装包,用户一装就把文件散到系统目录里了,卸载的时候哭都来不及。
CPack的工作流程
为了让你更直观地理解,我画了个流程图:
流程其实很清晰:
- CMake配置阶段,解析
install()命令,生成安装清单 - CMake构建阶段,编译出二进制文件
- CPack读取安装清单,调用后端打包程序(如dpkg-deb、rpmbuild)
- 最终生成目标格式的安装包
这里有个细节要注意:CPack并不重新编译代码。它只是把已经编译好的产物,按照install()的规则重新组织一下,然后打包。所以如果你改了代码,记得先重新编译,再跑CPack。
避坑指南:我曾经犯过一个低级错误——在CI里只跑了cpack,忘了前面的cmake --build。结果打包出来的还是旧版本的二进制文件。后来我在Pipeline里加了个判断:如果构建缓存过期,强制重新编译。嗯,血的教训。
CPack支持的打包格式一览
我整理了个表格,方便你快速了解CPack支持哪些格式:
| 生成器名称 | 目标平台 | 输出格式 | 常用场景 |
|---|---|---|---|
| Archive | 跨平台 | .tar.gz, .zip, .7z | 通用分发,源码包 |
| RPM | Linux (Red Hat系) | .rpm | 企业级Linux发行版 |
| DEB | Linux (Debian系) | .deb | Ubuntu/Debian系统 |
| NSIS | Windows | .exe 安装程序 | Windows桌面应用 |
| DragNDrop | macOS | .dmg | macOS应用分发 |
| IFW | 跨平台 | Qt Installer Framework | Qt应用安装包 |
| NuGet | .NET / C++ | .nupkg | C++库的NuGet分发 |
我个人最常用的是Archive + RPM + DEB三件套。Archive给开发人员用,RPM和DEB给生产环境用。Windows下偶尔用NSIS,但说实话,Windows的打包坑比较多,后面章节我会专门讲。
小结
这一章我们聊了:
- CPack是什么——CMake生态里的打包编排器
- CMake和CPack的关系——建筑师和搬家调度员
- CPack在CI里的角色——自动化打包的关键一环
- CPack的工作流程——从install()到安装包
- 支持的打包格式——一表打尽
下一章,我会带你手把手配置第一个CPack项目。到时候咱们直接上代码,把今天讲的理论落到实处。你准备好了吗?