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)

  1. 给 syntax/ 加一个从安装目录扫全部 .vsh 的装载器,并让 :set syntax cpp 从数据目录找规则文件;
  2. 在 .deb 依赖里补 libncurses-dev(如果我们的二进制是动态链 ncurses),验证 ldd 列表与依赖一致;
  3. 产出 .zip 格式并加 CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF,让解压直接得 vicarus/ 前缀;
  4. 挑战:配置 CPACK_RESOURCE_FILE_LICENSE,把 MIT 放进包里,并发布一个带签名校验的 release。

下一节 34-GitHub与CI自动化GitHubCI.md:让构建、测试、打包三件事,在每次 push 时自动做完。

posted @ 2026-09-08 13:51  IcarusLee  阅读(12)  评论(0)    收藏  举报