33-CPack打包交付Packaging
33 · 打包与交付 CPack Packaging(Installable Distributions)
本节目标(Goals):把 Vicarus 交给"不会看源码的人"——
install规则 + CPack 生成.tar.gz与.deb,数据目录(插件/语法文件)随包走。
前置知识(Prerequisites):教程 02-03(CMake/目标)、31(路径与数据目录习惯)。
1. 从"可编译"到"可安装"的三步
1) install 规则 → 声明"哪些文件装哪里"(bin/share/…)
2) cmake --install → 本地试装到一个 --prefix
3) cpack -G <格式> → 产出可分发的归档/包
前两步是"我能跑",第三步是"别人能装"。每一步的验收标准不同,别混。
2. install 规则:二进制 + 数据目录
在顶层 CMakeLists.txt 末尾:
# —— 交付物 ——
install(TARGETS vicarus
RUNTIME DESTINATION bin)
# 数据资产:插件与语法规则跟程序一起走
install(DIRECTORY plugins/ DESTINATION share/vicarus/plugins FILES_MATCHING
PATTERN "*.py" EXCLUDE)
install(DIRECTORY syntax/ DESTINATION share/vicarus/syntax)
# 编译期把"安装后的数据目录"注入进去(资源定位,见下)
set(VICARUS_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/vicarus")
target_compile_definitions(vicarus PRIVATE
VICARUS_DATA_DIR="${VICARUS_DATA_DIR}")
不带
FILES_MATCHING的话会连.git/README 一起拷;上面给plugins加了.py/.md的白名单过滤示范(精确过滤按你分发的充实程度调整)。
3. 运行时定位"数据目录"
二进制不假设自己在哪。三层回退:
// include/vicarus/infra/platform.hpp 加一个
// 资源目录:可被 VICARUS_DATA_DIR 覆盖(安装场景),否则看运行目录
std::string resource_dir() {
#ifdef VICARUS_DATA_DIR
return VICARUS_DATA_DIR; // 安装包场景
#else
return "share/vicarus"; // 开发构建
#endif
}
Editor 拼路径完全复用(教程 26/31 的底座):
// Editor::init 里
const std::string plugins_root = resource_dir() + "/plugins";
const std::string syntax_root = resource_dir() + "/syntax";
plugin_manager_.scan_and_load(plugins_root);
syntax_loader_.scan(syntax_root); // 若有语法文件装载器
于是开发构建与安装包共用一套定位逻辑;安装包里 share/vicarus/plugins 被编译器写死的路径命中。
4. 本地试装
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build --prefix /tmp/vicarus-stage
检查布局:
/tmp/vicarus-stage
├── bin/vicarus
└── share/vicarus/
├── plugins/…
└── syntax/…
试运行(用我们的资源目录做环境变量回退验证):
/tmp/vicarus-stage/bin/vicarus # 若数据路径错,报"plugin root not found"
这条"装到
--prefix再运行"的手工冒烟路径,就是教程 34 CI 里装包后的验收步骤。
5. CPack:产出归档与 .deb
# 顶层 CMakeLists.txt 末尾(install 规则之后)
set(CPACK_PACKAGE_NAME "vicarus")
set(CPACK_PACKAGE_VENDOR "Vicarus Project")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A Vim-like TUI editor in C++20")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_GENERATOR "TGZ;DEB") # 先两个最通用的格式
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"libncurses6, python3" ) # .deb 的运行时依赖声明
include(CPack) # 必须在所有变量设完后
打包:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cpack --config build/CPackConfig.cmake -G DEB -B dist
cpack --config build/CPackConfig.cmake -G TGZ -B dist
ls dist/
# vicarus-0.1.0-Linux.tar.gz vicarus-<ver>-Linux.deb
.deb 里看得到依赖与安装清单:
dpkg -c dist/vicarus-*.deb # 列出将装入的文件
dpkg -I dist/vicarus-*.deb # 查看依赖等元信息
6. 版本与校验
- 版本:
project(Vicarus VERSION 0.1.0)一处定义,CPACK_PACKAGE_VERSION直接引用——别在两处手写(教程 02 已建立这个习惯); - 验证脚本(可进 CI):装完
vicarus --version打印Vicarus 0.1.0且退出码 0; - 未来加
.zip(Windows/ZIP)与.dmg(macOS/外部依赖)不过是CPACK_GENERATOR的加法。
本节小结(Summary)
- install 规则 = bin + share 数据;
VICARUS_DATA_DIR编译期注入让运行时零猜测; cmake --install --prefix本地试装,是打包前的"手工冒烟";- CPack 一行配置产出 TGZ + DEB;依赖声明让 .deb 可被软件仓库消费者直接拉取;
- 版本单一来源 + 安装后版本冒烟,保证交付物可核对。
动手练习(Exercise)
- 给
syntax/加一个从安装目录扫全部.vsh的装载器,并让:set syntax cpp从数据目录找规则文件; - 在
.deb依赖里补libncurses-dev(如果我们的二进制是动态链 ncurses),验证ldd列表与依赖一致; - 产出
.zip格式并加CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF,让解压直接得vicarus/前缀; - 挑战:配置
CPACK_RESOURCE_FILE_LICENSE,把 MIT 放进包里,并发布一个带签名校验的 release。
下一节
34-GitHub与CI自动化GitHubCI.md:让构建、测试、打包三件事,在每次 push 时自动做完。

浙公网安备 33010602011771号