asherikov/ccws

GitHub: asherikov/ccws

一个基于 Make 和 Shell 的 ROS 开发环境,集成交叉编译、测试、静态检查、文档生成与 Debian 二进制包部署功能。

Stars: 18 | Forks: 2

- [简介](#introduction) - [功能](#features) - [构建 profiles](#build-profiles) - [执行 profiles](#execution-profiles) - [依赖](#dependencies) - [用法](#usage) - [初始设置](#initial-setup) - [编译](#compilation) - [运行](#running) - [测试](#testing) - [文档](#documentation) - [Debian 包生成](#debian-package-generation) - [交叉编译](#cross-compilation) - [高级用法](#advanced-usage) - [`CCWS` docker 镜像](#ccws-docker-image) - [CI 中的 `CCWS`](#ccws-in-ci) - [扩展 `CCWS`](#extending-ccws) - [编程代理](#coding-agents) - [已知问题](#known-issues) - [相关软件](#related-software) - [TODO](#todo) - [书签(不再提供 支持)](#bookmarks-not-going-to-be-supported) # 简介
CI status Build Status
`CCWS` 是一个用于 ROS 的开发环境,它集成了传统的 `catkin` 工作区和 CI pipeline 的功能,以便于(交叉)编译、测试、linting、文档生成和二进制包生成。它既可用作 CI/CD 的骨干,也可用作开发人员的工作环境。请注意,`CCWS` 并不打算成为一个完整的解决方案,而是作为开发特定供应商工作流的基础。`CCWS` 与 ROS 版本无关,在大多数情况下应该同时适用于 ROS1 和 ROS2。 ## 功能 - 构建 profiles —— 一组用于构建过程的配置,例如 cmake 工具链、colcon 配置、环境变量等。各个 profiles 之间互不冲突,可以并行使用,而无需克隆单独的工作区和包。 - 执行 profiles —— 简单的 shell mixins,旨在修改运行时环境,例如在 `valgrind` 中执行节点、改变节点崩溃处理方式等。 - 通过构建 profiles 实现的许多功能: - 针对几个常见平台的交叉编译。 - 使用 `doxygen` 为整个工作区或选定的包生成文档,类似于 。 - 使用 `clang-tidy` 和 `scan_build` 进行 Linting。 - 各种静态检查,如 所示,特别是: - `cppcheck` - `catkin_lint` - `yamllint` - `shellcheck` - 二进制 Debian 包生成。 - 演示如何使用其中部分功能的包模板。 - 可以根据可用的 RAM 而不是 CPU 核心数来选择并行作业的数量,因为 RAM 很可能是限制因素。 - 完全基于 `make` 和 shell 脚本。所有脚本和配置都保存在工作区中,便于根据特定需求进行调整。 - 与 AI 编程代理(`qwen`)集成。 ## 构建 profiles Profile 配置位于 `ccws/profiles/build` 中,`common` 子目录包含默认参数,这些参数可以被特定的 profiles 覆盖: - \[默认\] `reldebug` —— 默认编译器,cmake 构建类型为 `RelWithDebInfo`。 - `release` —— 默认编译器,cmake 构建类型为 `Release`,测试被禁用。 - `scan_build` —— 使用 `scan_build` 和 `clang-tidy` 通过 `clang` 进行编译,以进行静态检查。`clang-tidy` 参数在 cmake 工具链中定义,必须如包模板 `CMakeLists` 中所示在包中启用。 - `clang_tidy` —— 没有 `clang` 和 `scan_build` 的 `scan_build` 简化版本。 - `thread_sanitizer` —— 使用线程 sanitizer 进行编译。 - `addr_undef_sanitizers` —— 使用地址和未定义行为 sanitizer 进行编译。 - `static_checks` —— 静态检查器及其配置。 - `doxygen` —— doxygen 及其配置。 - `cross_raspberry_pi` —— 针对 Raspberry Pi 的交叉编译。 - `cross_arm64` —— 针对 arm64 的交叉编译(使用 docker 容器)。 - `clangd` —— 从另一个 profile 收集编译命令,并在工作区根目录生成 clangd 配置文件。 - `deb` —— Debian 包生成(见下文)。 旧版: - `cross_jetson_xavier` —— 针对 Jetson Xavier 的交叉编译。 - `cross_jetson_nano` —— 针对 Jetson Nano 的交叉编译。 ## 执行 profiles 执行 profiles 设置可在启动脚本中使用的环境变量,以更改运行时行为,如 `ccws/pkg_template/catkin/launch/bringup.launch` 所示,当前可用的 profiles 有: - `common` —— 一组通用的 ROS 参数,例如 `ROS_HOME`,它会自动包含在二进制包中。 - `test` —— 设置 `CCWS_NODE_CRASH_ACTION` 变量,使遵循该变量的节点变为 `required`(必需的),即此类节点的终止将导致测试脚本崩溃,从而可以很容易地被检测到。 - `valgrind` —— 将 `CCWS_NODE_LAUNCH_PREFIX` 设置为 `valgrind`,并设置一些控制 `valgrind` 行为的变量。 - `core_pattern` —— 设置 core pattern 以将 core 文件保存在 artifacts 目录中。 - `address_sanitizer` —— `addr_undef_sanitizers` profile 的辅助工具。 执行 profiles 对构建过程没有影响,仅在 `*test*` 目标或 Debian 包中生效。在测试中始终使用 `test` 执行 profile,并且可以通过 `EXEC_PROFILE=" "` 提供额外的 profiles。这些目标使用位于 `CCWS` 根文件夹中的 `setup.bash` 脚本加载 profiles,该脚本也可手动使用,例如 `source setup.bash [ [ ...]]`。 请注意,设置脚本始终包含 `common` profile,并且如果未指定其他执行 profiles,则使用 `test` 执行 profile。 ## 依赖 可以使用 `make bp_install_build BUILD_PROFILE=[,...]` 安装依赖,这将安装以下工具和特定于 profile 的依赖: - `colcon` - `yq` —— 依赖 - `cmake` - `ccache` —— 可以在 cmake 工具链中禁用 - `wget` # 用法 有关命令使用提示,请参见 `.ccws/test_main.mk`。 ## 初始设置 - 通过将开发者和供应商特定的参数添加到 `make/config.mk` 来覆盖它们,可用参数可以在 `Makefile` 的顶部找到。 - 使用 `make bp_install_build BUILD_PROFILE=` 目标安装 profile 依赖,交叉编译 profiles 需要一些额外的步骤,如下所述。在某些极简环境中,您可能需要在使用 `bp_install_build` 目标之前运行 `./ccws/tools/bin/bootstrap.sh` 来安装 `make` 和其他实用工具。 - 将包克隆到 `src` 子目录中,或使用 `make new PKG=` 创建新包。 - 使用 `make dep_install` 安装包依赖。可以通过使用 `PKG` 参数提供包名和/或使用 `CCWS_DEP_TYPE` (build\|exec\|test) 提供依赖类型来选择性地安装依赖。 ## 编译 - `make build PKG=""`,其中 `` 是一个或多个以空格分隔的包名。 - `make ` —— `make build` 的快捷方式,但 `` 可以是包名的子字符串。所有匹配该子字符串的包都将被构建。 - 可以使用 `JOBS=X` 参数覆盖作业数。 - `make build PKG= BUILD_PROFILE=scan_build` 覆盖默认 profile。 ## 运行 - 执行 `source setup.bash ` 以便能够使用包。也可以直接使用由 `colcon` 生成的设置脚本,例如 `install//local_setup.sh`,但在这种情况下,某些 `CCWS` 功能将不可用。 ## 测试 - `make test PKG=` 使用 `colcon` 进行测试,或使用 `make wstest` 测试所有包。 - `make ctest PKG=` 绕过 `colcon` 并直接运行 `ctest`,或使用 `make wsctest` 测试所有包。 ## 文档 - `make BUILD_PROFILE=doxygen`, `firefox artifacts/doxygen/index.html` - 查看示例: ## Debian 包生成 ### 概述 `CCWS` 采用了一种不同寻常的二进制包生成方法,这是传统 ROS(1 个包 = 1 个 deb)和 docker 容器之间的折中方案:在工作区中构建的所有包都被打包在一起,形成一个单一的 Debian“超级包”。与 `bloom` 不同,`CCWS` 直接生成二进制包,而不是先生成源包。 二进制包生成实现为构建 profile mixin,可以覆盖在任意构建 profile 之上: `make BUILD_PROFILE=deb,reldebug`。 `CCWS` 的方法具有许多优点: - 与传统的 ROS 方法相比,二进制兼容性问题被最小化: - 无需担心多个独立二进制包之间的兼容性并执行 ABI 检查; - 如果包含了基础的 ROS 包,还可以避免同一 ROS 版本的不同同步版本之间的二进制不兼容性(实际上这种情况确实会发生)。 - 在处理 tags、版本、git submodules 等方面,与 ROS 相比,包仓库的管理可以更加宽松,例如,不需要为所有包维护发布仓库。 - Debian“超级包”比独立包和 docker 容器都更容易处理,例如,开发人员可以从他们的工作分支生成它们,并轻松地复制和安装到目标机器上。 - Debian 包通常比 docker 容器具有一些优势: - 执行期间零开销。 - 直接访问硬件。 - 轻松安装系统服务、udev 规则、配置等。 - 如果使用不同的 `VERSION` 参数构建,可以同时安装多种变体的二进制“超级包”。请注意,这会更改安装路径,因此某些在 `cmake` 中表现得过于“聪明”的工作区包,如果之前已经构建过,可能需要清理构建目录。 ### 构建包 通常,为了在 `catkin` `cmake` 文件中获取所有正确的路径并正确安装系统文件,在编译期间需要将包安装到文件系统根目录。`CCWS` 使用 `proot` 来避免这一点,类似于交叉编译 profiles。 ## 交叉编译 这里 `` 代表 `cross_raspberry_pi`、`cross_arm64`。 交叉编译的 make 目标可以在 `ccws/make/cross.mk` 和 `ccws/profiles//targets.mk` 中找到。 关于旧版 `cross_jetson_xavier` 和 `cross_jetson_nano` 的注意事项:这些 profiles 需要 Ubuntu 18.04 / ROS melodic 并安装 `nvcc`,你可能需要在容器中执行此操作。 下面记录了常规的工作流程,有关更多技术细节,请参见 `ccws/doc/cross-compilation.md` 以及 `.ccws/test_cross.mk` (ROS1) 和 `.ccws/test_cross_ros2.mk` (ROS2) 中的 `CCWS` CI 测试: 1. 使用 `make bp_install_build BUILD_PROFILE=` 安装 profile 依赖 2. 获取系统镜像: - `cross_raspberry_pi` —— `bp_install_build` 目标会自动下载标准镜像; - `cross_arm64` —— 使用 `docker_mountpoint` `make` 目标,该目标从 docker 镜像中提取根文件系统; - `cross_jetson_xavier`、`cross_jetson_nano` —— `CCWS` 不会自动获取这些镜像,您必须手动将系统分区镜像复制到 `ccws/profiles/cross_jetson_xavier/system.img`。 3. 初始化源码仓库: - `make wsinit REPOS="https://github.com/asherikov/staticoma.git"` - \[构建所有 ROS1 包时(不适用于 ROS2)\] 将所有包的 ROS1 依赖项添加到工作区 `make dep_to_repolist ROS_DISTRO=melodic`,或特定包 `make dep_to_repolist PKG= ROS_DISTRO=melodic`; - 获取所有包 `make wsupdate`。 4. 将工作区中包的系统依赖项安装到系统镜像中: `make cross_install PKG=staticoma BUILD_PROFILE= ROS_DISTRO=` 5. 编译包: - 使用 `make cross_mount BUILD_PROFILE=` 挂载 sysroot - 构建包,例如 `make staticoma BUILD_PROFILE=` 或构建并生成 deb 包 `make PKG=staticoma BUILD_PROFILE=deb,` - 完成后使用 `make cross_umount BUILD_PROFILE=` 卸载 sysroot # 高级用法 ## `CCWS` docker 镜像 预装了 `CCWS` 及依赖的 docker 镜像用于测试,但建议使用 `ccws/examples/Dockerfile` 作为示例构建定制化的镜像。 该镜像可以通过以下方式使用: - `docker pull asherikov/ccws:noble` (或 `asherikov/ccws:jammy`) - `mkdir tmp_ws` \# sources, build, install, cache 将会放在这里 - `docker run --rm -ti -v ./tmp_ws:/ccws/workspace asherikov/ccws:noble bash` - `make wsinit REPOS="https://github.com/asherikov/qpmad.git"` - `...` ## CI 中的 `CCWS` 有关示例,请参见 `ccws/examples/Jenkinsfile.example`、`.github/workflows/reusable_*` 以及 。 ## 扩展 `CCWS` 可以通过多种方式扩展 `CCWS` 功能: - 通过添加新的构建 profiles,例如 `make bp_new BUILD_PROFILE=vendor_static_checks,static_checks`,所有以 `vendor` 前缀开头的 profiles 都会被 git 忽略; - 通过添加执行 profiles; - 可以通过创建 `ccws/profiles/build/vendor/.mk` 文件来添加 `make` 目标; - 可以将通用的 `cmake` 工具链后缀添加到 `ccws/profiles/build/vendor/toolchain_suffix.cmake`。 ## 编程代理 提供了与 的基本集成,可以通过两种方式使用: - `make qwen`:使用源码空间作为代理工作区,运行原始的 `ghcr.io/qwenlm/qwen-code` docker 容器。 - `make qwen_ccws`:运行自定义构建容器,参见 `ccws/examples/Dockerfile.qwen`(docker hub 上的 `asherikov/ccws_qwen:noble`),它同时包含 `CCWS` 和 `qwen-code`,允许代理在执行命令时使用 `CCWS`。源码、构建、安装和其他目录都作为卷挂载。 在这两种情况下,`qwen` 配置都存储在源码空间的 `.ccws/qwen` 目录中。更多细节请参见 `ccws/make/ai.mk`。 # 已知问题 - 在 docker 容器内部进行交叉编译或生成 Debian 包期间发生段错误(两者都需要 `proot`):这可能是由于 Linux 的 `seccomp` 特性导致的,可以使用 `--security-opt seccomp:unconfined` docker 参数将其禁用。对于 `proot` 使用 `PROOT_NO_SECCOMP=1` 禁用 `seccomp` 似乎是不必要的。 - 在 Ubuntu 22 的 arm64 架构上进行构建时(例如构建 Debian 包时),`proot` 发生段错误。必须使用更新版本的 `proot`,参见 。 - 使用 sanitizers(`addr_undef_sanitizers` 或 `thread_sanitizer` 构建 profiles)编译的程序在执行时输出 `2: AddressSanitizer:DEADLYSIGNAL` 或 `FATAL: ThreadSanitizer: unexpected memory mapping`:原因在于现代 Linux 内核通过 ASLR(地址空间布局随机化)加强了内存安全性,参见 。可以通过设置 `sudo sysctl vm.mmap_rnd_bits=28` 解决该问题。 - 由于 cmake 的误用,部分 ROS2 核心包无法使用 `CCWS` 构建,例如参见 。 - 工作区前缀是故意从调试信息中的路径裁剪掉的,您必须在 gdb 中设置路径替换才能正确解析它们,即 `set substitute-path / `。 - 交叉编译可能需要在构建主机上安装工作区依赖。 # 相关软件 - —— ROS 特定的 CI 脚本,非交互式、“一次性”设计,无 sanitizers,模拟交叉编译。 - —— 针对 ROS 和 ROS2 的模拟交叉编译。 - —— 以不同方式完成的针对 Raspberry Pi 的交叉编译。 - —— 覆盖了部分 `CCWS` 功能的 `github` action。 - —— ROS 打包基础设施的核心。较为复杂,专门用于处理单个包而不是工作区,不适合快速的现场重新部署。 - 提供了类似于“超级包”的功能,允许将安装空间打包成单个归档文件。很自然地,它并不提供所有包功能,如依赖、安装脚本等。此外,它不依赖类 chroot 环境来确保路径正确。 - —— 基于 Python 的环境,也旨在实现工作区处理自动化。与主要作为构建/部署环境的 `CCWS` 不同,它具有更多 ROS 特有的功能,如设置 bringup 包。但是,它缺少 `CCWS` 所具有的一些高级构建功能。 - —— 特定于 VSCode 的工作区环境,提供了各种构建和静态分析选项,但不如 `CCWS` 的分析功能多。 # TODO - 测试 - 模糊测试 。 - 添加代码覆盖率 profile。 - cmake 3.21:`--output-junit = Output test results to JUnit XML file.` - 静态检查 - - `scan_build` 的潜在替代品 ,具有额外的检查和缓存。 - 动态检查 - 带 的执行 profile,以便在收到信号时自动启动调试器。 - 作为 `valgrind` 执行 profile 的替代方案 —— 尽管在一般情况下有些大材小用。 - 构建性能 - 用 替换 `ccache`。 - 可用于缓存 `clang-tidy` 运行。 - 使用 缓存 cmake 检查, 可能也有用。 - 使用 clang 进行构建时间分析 和 / 或 。 - 使用 支持分布式编译可能会很有用。 - 构建 - 可重现构建 。 - 打包 - 探索生成调试和开发包,特别是剥离静态库和头文件。 - —— 通用二进制包生成器,`dpkg-deb` 的潜在替代品。 - 容器和镜像 - 作为 OCI artifacts 的系统镜像 。 - 以分层的、类似容器的方式生成系统镜像 。 - 其他 - 高级依赖图生成 。 - 现代文档生成器 。 - 更好地与 GitHub 集成,例如 。 ## 书签(不再提供支持) - Shell 格式化工具 。 - —— C++ 静态分析工具。 - —— catkin 包的 linter。 - —— 感知 ROS 的静态分析,在非 `catkin_make` 构建环境中可能会有问题。 - 控制符号可见性并使用 进行验证。 - 集成 - 源代码拼写检查 。 - 不会使用 ,但可以集成其中的一些 linter。 - 添加 `CodeQL` profile ()。 - 使用 代替 loop 设备,以避免使用 sudo。不过,在 Ubuntu 中存在一些问题,错误 759725,参见 。`guestfs` 太慢了,不实用。 - 似乎不是很有用。 - 多用途 linter 包装器。 - 可能对检测不必要的头文件有用,但看起来已经停滞了。 - 似乎已被废弃,linter 并不是很有帮助 —— 主要是格式化的东西。这同样适用于
标签:Bash脚本, Cutter, Debian打包, LNA, ROS, 交叉编译, 合规性检查, 开发环境, 请求拦截