本页聚焦 roboto-motors 的 Debian 二进制包构建体系与自动化发布管线。内容覆盖构建脚本的阶段化设计、CMake 安装规则、Debian 元数据与维护脚本,以及基于 GitHub Actions 的 CI/CD 发布流程。理解这些机制有助于在部署环境中完成离线安装,也为需要二次打包或定制发布流程的开发者提供可直接参考的工程范式。
构建架构总览
整个 Debian 包的构建遵循 Staged Install + dpkg-deb 手工打包 的轻量级模式,而非 Debian 官方源码包(source package)规范。核心思想是:先通过 CMake 将产物安装到一个临时根目录(DESTDIR),再将该目录结构连同 DEBIAN 控制信息一起封装为 .deb。这一模式兼顾了跨平台 CMake 工程与 Debian 包管理的兼容性,同时支持可选的 ROS 2 ament 构建路径。
flowchart TD
A[源码树] --> B[CMake Configure]
B --> C[make -j$(nproc)]
C --> D[DESTDIR Staged Install<br/>build/destdir/opt/roboparty]
D --> E[组装 DEBIAN 目录<br/>control / postinst / postrm]
E --> F[dpkg-deb --build]
F --> G[roboto-motors_2.1.0_arm64.deb]
H[GitHub Actions<br/>ubuntu-22.04-arm] -->|tag v*| B
H -->|workflow_dispatch| B
G --> I[Upload Artifact]
G -->|startsWith(refs/tags/)| J[GitHub Release]
上述流程中的关键设计决策包括:安装前缀固定为 /opt/roboparty,以隔离第三方 SDK 与系统路径;静态库为主,避免运行时库版本冲突;Python 绑定与 C++ 头文件、CMake Config 同包交付,保证下游 CMake 与 Python 项目均可一键消费。
Sources: build_deb.sh, CMakeLists.txt
构建脚本解析
build_deb.sh 是构建流程的中央协调器,执行编译、分段安装、控制文件生成和最终打包四个阶段。
阶段一:环境选择与编译
脚本通过环境变量 WITH_ROS 控制是否进入 ROS 2 构建模式。默认值为 0(部署模式),此时不 source ROS 环境,仅生成常规 Debian 包;若设为 1,则自动探测 jazzy → iron → humble → rolling 的 ROS 2 发行版,并 source 首个找到的 setup.bash,使 CMake 中的 ament_cmake 探测成功,从而在包内额外生成 ROS 2 的 ament 索引与导出文件。
flowchart LR
A[WITH_ROS=0<br/>默认] --> B[不加载ROS]
C[WITH_ROS=1] --> D[探测/opt/ros/]
D --> E{找到setup.bash?}
E -->|是| F[source并编译]
E -->|否| G[退出报错]
编译阶段使用标准 out-of-source 构建,显式指定 -DCMAKE_INSTALL_PREFIX=/opt/roboparty 与 -DCMAKE_BUILD_TYPE=Release,随后通过 DESTDIR="${SCRIPT_DIR}/build/destdir" cmake --install . 将产物复制到暂存目录,而非直接写入系统根目录。
Sources: build_deb.sh
阶段二:Debian 包结构组装
脚本在暂存目录基础上新建 ${PACKAGE}_${VERSION}_${ARCH} 目录,并执行以下操作:
| 步骤 | 操作内容 | 目的 |
|---|---|---|
| 1 | 拷贝 build/destdir${PREFIX} 下的全部产物 |
包含头文件、静态库、Python .so、CMake Config |
| 2 | 复制 debian/postinst、debian/postrm 到 DEBIAN/ |
安装/卸载维护脚本 |
| 3 | 可选复制 debian/conffiles |
标记配置文件(当前未使用) |
| 4 | sed 替换 debian/control 中的 ARCH_PLACEHOLDER 与 VERSION_PLACEHOLDER |
动态生成与当前硬件和代码版本匹配的控制文件 |
| 5 | dpkg-deb --root-owner-group --build |
以 root 身份所有权打包,避免权限漂移 |
最终产物形如 roboto-motors_2.1.0_arm64.deb,命名遵循 Debian 标准 <Package>_<Version>_<Architecture>.deb。
Sources: build_deb.sh
包元数据与维护脚本
control 模板
debian/control 采用模板化设计,由构建脚本在打包时注入架构与版本号。其字段语义如下:
| 字段 | 值/占位符 | 说明 |
|---|---|---|
| Package | roboto-motors |
包名,与 ROS 2 包名一致,但 Debian 规范要求小写与连字符 |
| Version | VERSION_PLACEHOLDER |
运行时替换为 2.1.0,与 package.xml 中的版本号保持同步 |
| Architecture | ARCH_PLACEHOLDER |
运行时替换为 dpkg --print-architecture 的输出,通常为 arm64 |
| Depends | roboto-base (>= 1.0.0) |
运行时硬依赖,确保底层 CAN/通信基础设施已就绪 |
| Section | libs |
分类为库包 |
| Priority | optional |
优先级 |
值得注意的是,当前 Depends 仅声明了 roboto-base,而未列出 libspdlog-dev、libfmt-dev、libboost-system-dev、libeigen3-dev、pybind11-dev 等构建依赖。这是因为产物以静态库形式交付,编译期依赖已被链接进 .a 文件中;但下游若通过 CMake Config 消费本包,仍需这些库的头文件与 CMake 模块,因此实际部署时通常需要确保它们已安装。
Sources: debian/control
维护脚本
postinst 与 postrm 均仅包含一行 ldconfig,用于在包安装或卸载后刷新动态链接器缓存。虽然本包主要交付静态库,但 Python 绑定模块 motors_py.so 为共享对象,必须保证动态链接器能正确解析其符号依赖,因此 ldconfig 的调用是必要的防御性措施。
Sources: debian/postinst, debian/postrm
CMake 安装规则
顶层 CMakeLists.txt 定义了所有会被打包到 .deb 中的产物。安装规则可分为三类:
1. 头文件与静态库
install(DIRECTORY include/ DESTINATION include):将公共 API 头文件(如motor_driver.hpp)安装到${PREFIX}/include。install(TARGETS motors dm_motors evo_motors lro_motors motors_can motors_canfd ...):将顶层库及所有子模块静态库安装到${PREFIX}/lib。
子模块(dm_motors、evo_motors、lro_motors、motors_can、motors_canfd)自身并未显式编写 install(TARGETS ...),而是由顶层 CMakeLists.txt 统一收集并安装。这种集中式安装策略降低了子目录 CMake 的重复代码,但也意味着新增库时必须同步修改顶层的 install 指令。
Sources: CMakeLists.txt
2. Python 绑定
通过 pybind11_add_module 生成的 motors_py 模块,被安装到 ${PREFIX}/lib/python${MAJOR}.${MINOR}/site-packages。该路径与 Debian/Ubuntu 系统 Python 的 sys.path 并不完全对齐,因此下游 Python 项目通常需要显式设置 PYTHONPATH=/opt/roboparty/lib/python3.x/site-packages,或通过虚拟环境符号链接引入。
Sources: CMakeLists.txt
3. CMake Config 与下游消费
cmake/roboto_motorsConfig.cmake 被安装到 ${PREFIX}/lib/cmake/roboto_motors。该文件定义了 roboto_motors::roboto_motors 这一 INTERFACE IMPORTED 目标,自动查找 fmt、spdlog、Eigen3 依赖,并收集所有组件静态库的绝对路径。下游工程通过以下方式即可消费:
find_package(roboto_motors REQUIRED)
target_link_libraries(my_app PRIVATE roboto_motors::roboto_motors)
Config 文件使用 get_filename_component 从当前文件位置向上回溯三级计算安装前缀,确保无论包被安装到 /opt/roboparty 还是其他路径,相对关系均保持正确。
Sources: cmake/roboto_motorsConfig.cmake, CMakeLists.txt
CI/CD 发布管线
.github/workflows/build-deb.yml 实现了完整的自动化构建与发布:
| 阶段 | 说明 |
|---|---|
| 触发条件 | 推送以 v 开头的 tag(如 v2.1.0),或手动 workflow_dispatch |
| 运行环境 | ubuntu-22.04-arm,即 GitHub 提供的 ARM64 原生 runner,无需 QEMU 模拟,保证 CAN/SocketCAN 相关代码在目标架构上的编译可靠性 |
| 依赖安装 | build-essential、cmake、ccache、dpkg-dev,以及 libspdlog-dev、libfmt-dev、libboost-system-dev、libeigen3-dev、pybind11-dev、python3-dev |
| 构建 | 直接调用 ./build_deb.sh,默认 WITH_ROS=0,产出部署模式 deb |
| 产物留存 | 通过 actions/upload-artifact@v4 上传 .deb,供每次工作流运行后下载 |
| 自动发布 | 若触发源为 tag,则使用 softprops/action-gh-release@v2 创建 GitHub Release,并将 .deb 作为 Release Asset 附加 |
该流程的隐含约定是:版本号的单一事实来源目前为 build_deb.sh 中的 VERSION 变量,package.xml 也记录了 2.1.0,但 GitHub Release 的 tag 名称(如 v2.1.0)与脚本内版本必须人工保持一致。建议在发布前通过自动化脚本或 CI 校验二者是否同步。
Sources: .github/workflows/build-deb.yml
本地手动构建指南
若需在本地(如 NVIDIA Jetson、Raspberry Pi 或 ARM64 服务器)构建 Debian 包,执行以下命令即可:
# 1. 安装构建依赖
sudo apt-get update
sudo apt-get install -y build-essential cmake ccache dpkg-dev \
libspdlog-dev libfmt-dev libboost-system-dev \
libeigen3-dev pybind11-dev python3-dev
# 2. 执行构建脚本
chmod +x build_deb.sh
./build_deb.sh
# 3. 查看产物
ls -lh roboto-motors_2.1.0_*.deb
# 4. 安装测试
sudo dpkg -i roboto-motors_2.1.0_arm64.deb
若目标环境已安装 ROS 2,且希望 deb 包同时包含 ament 索引(用于 ros2 run 或 colcon 识别),可前置环境变量:
WITH_ROS=1 ./build_deb.sh
构建完成后,可通过 dpkg-deb -I 查看包元数据,dpkg-deb -c 查看包内文件清单,以验证安装结构是否符合预期。
Sources: build_deb.sh
与其他构建模式的关系
本项目的构建系统具有双重身份:既是纯 CMake 工程,也是可选的 ROS 2 ament 包。CMakeLists.txt 中通过 find_package(ament_cmake QUIET) 实现无侵入式探测——当 ament_cmake 不可用时,整个构建退化为标准 CMake 工程,所有安装规则依然有效;当存在时,则追加 ament_export_libraries、ament_export_include_directories 与 ament_package(),使该包可被 colcon 识别并放入 ROS 2 工作空间的 install 目录。
Debian 包构建脚本默认走非 ROS 路径(WITH_ROS=0),这是因为部署场景通常不需要完整的 ROS 2 运行时,仅需 C++ 库与 Python 绑定。若需将本包作为 ROS 2 依赖分发,应在 CI 中新增一个 WITH_ROS=1 的矩阵任务,并相应调整包名或版本后缀以区分两种变体。
Sources: CMakeLists.txt, CMakeLists.txt
延伸阅读与下一步
- 若需了解 CMake 的依赖探测逻辑、子模块组织与静态库链接顺序,请参阅 CMake构建系统与依赖管理。
- 若关注 ROS 2 ament 的集成细节与
package.xml语义,请参阅 ROS 2 ament集成。 - 若需查看各驱动子模块的具体实现与协议抽象,请参阅 架构总览与模块划分。