ROS 2开发者指南

这个页面定义了我们在开发ROS 2时采用的实践和策略。

一般原则

以下原则适用于所有ROS 2的开发:

  • 共享所有权:每个参与ROS 2开发的人都应该对系统的所有部分有所所有感。代码的原始作者没有特殊的权限或义务来控制或维护该代码块。每个人都可以自由地在任何地方提出更改、处理任何类型的问题票和审核任何拉取请求。

  • 愿意做任何工作:作为共享所有权的必然结果,每个人都应该愿意承担任何可用的任务,并为系统的任何方面做出贡献。

  • 寻求帮助:如果在某个任务上遇到困难,请适当地通过工单、评论或电子邮件向你的开发同事寻求帮助。

质量实践

根据它们所遵循的开发实践,软件包可以根据不同的质量水平进行分类,根据《REP 2004:软件包质量分类指南 <https://www.ros.org/reps/rep-2004.html>》中的准则进行分类。这些分类根据版本管理、测试、文档编写等方面的策略进行区分。

以下部分是我们遵循的特定开发规则,以确保核心软件包具有最高质量(“一级”)。我们建议所有ROS开发人员努力遵守以下政策,以确保ROS生态系统的质量。

版本管理

我们将使用`语义化版本指南 <http://semver.org/>`__(semver)进行版本管理。

我们还将遵守一些基于``semver``的ROS特定规则,这些规则是在``semver``完整意义上建立的。

  • 不应该在已发布的ROS发行版中进行主要版本增加(即破坏性变更)

    • 补丁(保持接口兼容)和次要(非破坏性)版本增加不会破坏兼容性,因此此类更改*允许*在发布中进行

    • 主要ROS版本是发布破坏性变更的最佳时机。如果核心包需要多个破坏性变更,则应将它们合并到集成分支(例如rolling)中,以便快速在CI中发现问题,但同时发布以减少ROS用户的主要发布数量

    • 尽管主要增量需要新的发行版,但新的发行版并不一定需要进行主要更新(如果开发和发布可以在不破坏API的情况下进行)

  • 对于编译代码,ABI(应用二进制接口)被视为公共接口的一部分。任何需要重新编译依赖代码的更改都被视为主要更改(破坏性的)

    • ABI破坏性更改可以在次要版本升级之前(加入滚动发布)进行

  • 尽管 Dashing 和 Eloquent 的主要版本组件为``0``,我们仍对核心包的API稳定性进行强制执行,尽管 SemVer 规范 中有关初始开发的规定

    • 随后,软件包应努力达到成熟状态并增加到版本``1.0.0``以符合``semver``的规范

注意事项

这些规则是*尽力而为*的。在极端情况下,可能需要在一个主要版本/发布中打破API的兼容性。对于非计划中的兼容性断裂,是将主版本号还是次版本号增加将根据具体情况进行评估。

例如,考虑一个涉及发布的 X-turtle 和对应的主版本号为``1.0.0``的 Y-turtle 的情况。

如果在 X-turtle 中发现必须打破API兼容性的问题,显然不能选择升级到``2.0.0``,因为``2.0.0``已经存在。

在这种情况下处理X-turtle版本的解决方案,都是非理想的,包括:

  1. 增加X-turtle的次要版本:非理想,因为它违反了语义化版本规范(SemVer)中破坏性变更必须增加主要版本的原则。

  2. 将X-turtle的主要版本升级到超过Y-turtle(3.0.0):非理想,因为旧发行版的版本将会高于已有的新发行版版本,这将使得特定版本条件代码失效/破坏。

开发人员将需要决定使用哪种解决方案,或者更重要的是,他们愿意违背哪个原则。我们不能建议选用其中一种,但无论哪种情况,我们都要求采取明确的措施手动向用户传达中断情况及其解释(不仅仅是版本号的增加)。

如果没有 Y-turtle,即使修复只是一个补丁,X-turtle 也必须升级到 2.0.0。这种情况符合 SemVer 规则,但违反了我们自己的规定,即在发布的分发版中不应引入主要增量。

这就是为什么我们将版本规则视为“尽力而为”。尽管上述例子不太可能出现,但准确定义我们的版本系统非常重要。

公共 API 声明

根据 semver,每个软件包必须明确声明一个公共 API。我们将使用软件包的质量声明中的“公共 API 声明”部分来声明哪些符号是公共 API 的一部分。

对于大多数 C 和 C++ 包,声明是其安装的任何头文件。然而,定义一组被视为私有的符号也是可以接受的。在头文件中避免私有符号可以帮助实现 ABI 的稳定性,但并非必需。

对于其他语言如 Python,必须明确定义公共 API,以便清楚地知道哪些符号可以依赖于版本指南。公共 API 还可以扩展到构建工件,如配置变量、CMake 配置文件等,以及可执行文件、命令行选项和输出。包的文档应清楚地说明公共 API 的任何元素。如果你使用的内容在包的文档中没有明确列出作为公共 API 的一部分,那么你不能指望它在次要或修补版本之间不发生变化。

废弃策略

在可能的情况下,我们还将使用主要版本增量的滴答滴答废弃和迁移策略。新的废弃将在新的发行版中提出,伴随着编译器警告,表示该功能正在被废弃。在下一个发布中,该功能将被完全移除(不再有警告)。

示例函数 foo 已弃用,并被函数 bar 替代:

版本

API

X-turtle

void foo();

Y-龟

[[已弃用("使用bar()")]] void foo(); <br> void bar();

Z-龟

void bar();

在发行版发布之后,我们不能添加废弃项。然而,废弃项不一定需要引发主要版本的升级。如果在发行版发布之前进行次要版本的升级,就可以引入废弃项(类似于破坏ABI的更改)。

例如,如果X-turtle作为``2.0.0``开始开发,可以在X-turtle发布之前的``2.1.0``中添加废弃项。

我们将尽可能在不同发行版之间保持兼容性。然而,与语义化版本控制(SemVer)相关的注意事项一样,特定情况下可能无法完全遵守打勾或废弃项。

变更控制流程

  • 所有更改都必须通过拉取请求进行。

  • 我们将在ROSCore仓库的拉取请求中执行`开发者证书 (DCO) <https://developercertificate.org/>`_ 的强制要求。

    • 它要求所有提交消息都包含与提交作者相匹配的``Signed-off-by``行及其电子邮件地址。

    • 您可以在``git commit``命令中添加``-s`` / --signoff``参数或手动编写所需的消息(例如:``Signed-off-by: Your Name Developer <your.name@example.com>)。

    • 只涉及空格删除、拼写错误更正和其他 微小更改 的拉取请求,不需要DCO。

  • 对于每个拉取请求,始终在所有 一级支持平台 上运行CI作业,并在拉取请求中包含作业链接。(如果您无权访问Jenkins作业,将有人为您触发作业。)

  • 需要至少来自未参与拉取请求撰写的其他开发人员的1个批准才能被视为已批准。在合并之前需要批准。

    • 软件包可以选择增加此数字。

  • 在合并相关更改之前,必须先提出对文档(API文档、功能文档、发布说明等)所需的任何更改。

关于回溯拉取请求的准则

在更改较旧版本的ROS时:

  • 确保在向较旧版本回溯更改的拉取请求之前,这些功能或修复已被接受并合并到滚动分支中。

  • 当将代码回溯到较旧的版本时,还要考虑回溯到任何其他 仍受支持的版本,甚至非LTS版本。

  • 如果您要完整地回溯单个PR,请将回溯PR的标题命名为"[Distro] <原始PR的名称>"。如果从一个或多个PR回溯部分更改,则标题应为"[Distro] <更改的描述>"。

  • 在回溯PR的描述中链接到您回溯更改的所有PR。在将Foxy更改回溯到Dashing时,您不需要链接到同一更改的Eloquent回溯。

文档

所有的软件包都应该在其README中包含或从其README中链接到以下文档元素:

  • 描述和目的

  • 公共API的定义和描述

  • 示例

  • 如何构建和安装(应该引用外部工具/工作流程)

  • 如何构建和运行测试

  • 如何构建文档

  • 如何进行开发(对于描述诸如``python setup.py develop``之类的内容很有用)

  • 许可证和版权声明

每个源文件必须有一个许可证和版权声明,通过自动化检查工具进行检查。

每个软件包必须有一个LICENSE文件,通常是Apache 2.0许可证,除非该软件包有现有的宽松许可证(例如,rviz使用三条款的BSD许可证)。

每个软件包应该对自身和其目的进行描述,尽量假设读者没有关于ROS或其他相关项目的先前知识而意外发现该软件包。

每个软件包应该定义和描述其公共API,以便用户可以合理地预期语义版本控制政策所覆盖的内容。即使在C和C++中,公共API可以通过API和ABI检查来强制执行,这也是一个很好的机会来描述代码的结构和每个部分的功能。

理应很容易通过任何软件包的文档了解如何构建、运行、构建并运行测试,并构建文档。显然,我们应该避免在常见工作流程中重复自己,比如在工作空间中构建软件包,但基本工作流程应该被描述或引用。

最后,它应该包含开发者的任何文档。这可能包括使用类似 python setup.py develop 的方法测试代码的工作流程,或者可能意味着描述如何利用软件包提供的扩展点。

示例:

(API文档尚未自动生成)

测试

所有的软件包都应该具备一定程度的 系统测试、集成测试和/或单元测试。

**单元测试**应该始终位于被测试的软件包中,并且应该使用诸如``Mock``之类的工具,尝试在构建的场景中测试代码库的狭窄部分。单元测试不应引入非测试工具的测试依赖,例如 gtest、nosestest、pytest、mock 等...

**集成测试**可以测试代码的各个部分之间或代码与系统之间的交互。它们通常以我们期望用户使用它们的方式测试软件接口。与单元测试类似,集成测试应该位于被测试的软件包中,除非绝对必要,否则不应引入非工具测试依赖,即所有非工具依赖都应在极端审查下才被允许,因此应尽量避免使用。

**系统测试**旨在测试软件包之间的端到端情况,并应位于它们自己的软件包中,以避免软件包膨胀或耦合以及避免循环依赖。

通常情况下,应尽量减少外部或跨包的测试依赖,以避免循环依赖和紧密耦合的测试包。

所有的包都应该有一些单元测试和可能的集成测试,但其数量取决于包的质量分类。以下小节适用于“Level 1”包:

代码覆盖率

我们将提供行覆盖率,并保证行覆盖率达到95%以上。如果有合理降低百分比目标的情况,必须明确记录。我们可以提供分支覆盖率,或者将某些代码排除在覆盖率之外(测试代码、调试代码等)。在合并更改之前,我们要求覆盖率要增加或保持不变,但在适当的理由下,可能会接受降低代码覆盖率的更改(例如,删除以前覆盖的代码可能会导致百分比下降)。

性能

我们强烈建议进行性能测试,但我们也认识到对于某些软件包来说,这并没有意义。如果有性能测试,我们将选择在每次更改或发布之前进行检查,或者两者都要进行。我们还需要对降低性能的更改或发布进行合并的理由。

代码检查器和静态分析

我们将使用:doc:ROS代码风格,并使用来自`ament_lint_common<https://github.com/ament/ament_lint/tree/humble/ament_lint_common/doc/index.rst>`_的代码检查器来强制执行。必须使用`ament_lint_common`的所有代码检查器和静态分析工具。

ament_lint_auto 文档提供了关于运行 ament_lint_common 的信息。

一般实践

一些实践适用于所有 ROS 2 开发。

这些实践不会影响软件包质量等级(如 REP 2004 中所述),但在开发过程中仍然强烈推荐。

问题

在提交问题时,请确保:

  • 提供足够的信息让其他人能够理解问题。在ROS 2中,以下几点对于缩小问题原因范围非常有帮助。在每个类别中尽可能尝试多种替代方案将特别有帮助。

    • **操作系统及版本。**原因:ROS 2支持多个平台,某些错误可能只出现在特定版本的操作系统/编译器上。

    • **安装方法。**理由:某些问题只在使用“fat archives”或Debians安装ROS 2时才显现出来。这可以帮助我们确定问题是否与打包过程有关。

    • **ROS 2的具体版本。**理由:某些错误可能存在于特定的ROS 2版本中,后来被修复。了解您的安装是否包含这些修复非常重要。

    • **正在使用的DDS/RMW实现**(请参阅[此页面](../../Concepts/Intermediate/About-Different-Middleware-Vendors)以确定使用哪个实现)。原因:通信问题可能特定于正在使用的ROS中间件。

    • **正在使用的ROS 2客户端库。**理由:这可以帮助我们缩小问题可能出现的堆栈层级范围。

  • 包含一系列重现问题的步骤。

  • 如果遇到错误,请考虑提供一个`简短、自包含、正确(可编译)的示例 <http://sscce.org/>`__。如果其他人能够轻松地重现错误,问题更有可能被解决。

  • 提及已尝试过的故障排除步骤,包括:

    • 升级到最新版本的代码,该版本可能包含尚未发布的错误修复。参见`此部分 <building-from-source>`,并按照说明获取"rolling"分支。

    • 尝试使用不同的RMW实现。请参考`此页面 <../../How-To-Guides/Working-with-multiple-RMW-implementations>`了解如何操作。

分支

注解

这只是一些指南。选择分支名称以匹配其自身工作流程是包维护者的责任。

在一个包的源代码库中,为其所针对的每个ROS发行版创建**单独的分支**是一种良好的做法。这些分支通常以它们所针对的发行版命名。例如,针对Humble发行版的开发可以创建一个名为``humble``的分支。

从这些分支中也可以发布版本,以适应相应的发行版。针对特定ROS发行版的开发可以在相应的分支上进行。例如:针对``foxy``的开发提交将被放到``foxy``分支,并且针对``foxy``的软件包发布将从同一分支进行。

注解

这需要软件包维护者根据需要执行回溯或向前移植,以保持所有分支与功能的同步。维护者还必须对所有仍然进行软件包发布的分支进行常规维护(修复错误等)。

例如,如果某个功能已合并到特定于Rolling的分支(例如``rolling``或``main``),并且该功能也适用于Humble发行版(不会破坏API等),那么将该功能回溯到Humble特定分支是一种良好的做法。

如果存在新功能或错误修复,则维护者可以为这些旧的发行版进行发布。

关于 main rolling 呢?

main 通常目标为 Rolling <../../Releases/Release-Rolling-Ridley>`(即下一个尚未发布的ROS版本),但维护者可能决定从 ``rolling` 分支进行开发和发布。

拉取请求

  • 一个拉取请求应该只关注一个更改。不同的更改应该放入不同的拉取请求中。参见 GitHub完美拉取请求指南

  • 补丁应该尽量保持最小的大小,并避免任何不必要的更改。

  • 拉取请求必须包含最少数量的有意义的提交。

    • 在拉取请求正在审核中时,您可以创建新的提交。

  • 在合并拉取请求之前,所有的更改应该被压缩成少量的语义提交,以保持历史清晰。

    • 在审核期间避免压缩提交记录。您的审阅人员可能不会注意到您所做的更改,从而引入混淆的可能性。而且,无论如何在合并之前都要进行压缩;提前压缩没有任何好处。

  • 任何开发者都可以审核并批准拉取请求(请参阅 General Principles)。

  • 当您正在处理尚未准备好审核或合并的更改时,请使用草稿拉取请求。当这些更改准备好进行审核时,将拉取请求从草稿状态移出。请注意,如果您想从草稿拉取请求中获得特定人员的早期反馈意见,可以在拉取请求的描述或评论中提及他们。

  • 如果您的拉取请求依赖于其他拉取请求,请通过在拉取请求的描述顶部添加 - Depends on <link> 来链接到每个所依赖的拉取请求。这样做有助于审阅人员了解拉取请求的上下文。

  • 当您开始审查拉取请求时,请在拉取请求上发表评论,以便其他开发人员知道您正在进行审查。

  • 拉取请求审查不是只读的,审查人员会发表评论,然后等待作者处理。作为审查人员,可以自由地进行一些小的改进(拼写错误、样式问题等)。作为拉取请求的发起人,如果您在一个分支上工作,请勾选`允许来自上游贡献者的编辑 <https://github.com/blog/2247-improving-collaboration-with-forks>`__ 选项,这将有助于前述情况。作为审查人员,也可以自由地进行一些较大的改进,但考虑将它们放在一个单独的分支中(在评论中提到新的分支,或者从新分支向原始分支发起另一个拉取请求)。

  • 任何开发人员(作者、审查人员或其他人)都可以合并任何已批准的拉取请求。

库版本管理

我们将同时对一个包中的所有库进行版本控制。这意味着库的版本继承自该包。这样可以防止库和包的版本分歧,并与共享同一个代码仓库的包发布策略保持一致。如果您需要库具有不同的版本,请考虑将它们拆分为不同的包。

开发过程

  • 默认分支(大多数情况下是滚动分支)必须始终构建,通过所有测试并且没有警告信息。如果出现任何回归问题,最重要的是至少恢复到先前的状态。

  • 始终使用启用了测试的构建。

  • 在对更改进行推送之前,始终在本地运行测试,并确保它们在拉取请求中正常工作。除了使用自动化测试外,还要手动运行修改后的代码路径,以确保补丁按预期工作。

  • 对于每个拉取请求,始终运行适用于所有平台的CI作业,并在拉取请求中包含作业链接。

有关推荐的软件开发工作流程的更多详细信息,请参阅“软件开发生命周期”_部分。

RMW API的更改

当更新 RMW API 时,需要同时更新Tier 1中间件库的RMW实现。例如,RMW API 中引入了新函数 rmw_foo(),必须在以下软件包中实现(截至ROS Galactic版本):

如果可行的话,也应考虑更新非一级中间件库(例如,根据更改的大小)。有关中间件库及其等级的列表,请参阅 REP-2000

跟踪任务

为了帮助组织ROS 2的工作,核心ROS 2开发团队使用看板式 GitHub项目看板

然而,并不是所有问题和拉取请求都在项目看板上跟踪。一个看板通常表示一个即将发布的版本或特定的项目。可以通过浏览 ROS 2存储库的个别问题页面 来浏览票证。

任何给定的ROS 2项目看板中的列名称和用途可能会有所不同,但通常遵循相同的一般结构:

  • 待办事项:与项目相关的问题,可以分配处理

  • 进行中:正在进行工作的活动拉取请求

  • 审核中:工作已完成且准备好进行审核的拉取请求,以及目前正在进行主动审核的请求

  • 完成:合并/关闭拉取请求和相关问题(仅供参考)

如果要请求更改权限,请在您感兴趣的工单上发表评论即可。根据复杂程度,描述您打算如何解决问题可能会很有用。我们将更新状态(如果您没有权限),然后您可以开始处理拉取请求。如果您经常做出贡献,我们可能会直接授予您管理标签等权限。

编程约定

  • 防御式编程:确保尽早持有假设。例如,检查每个返回代码,并确保至少抛出异常,直到处理情况更加优雅为止。

  • 所有错误消息必须指向 stderr

  • 在尽可能狭窄的范围内声明变量。

  • 保持一组项目(依赖项、导入项、包含项等)按字母顺序排列。

特定于 C++。

  • 避免直接将流 (<<) 输出到 stdout/stderr,以防止多个线程之间的交错。

  • 避免使用引用来操作 std::shared_ptr,因为这会破坏引用计数。如果原始实例超出作用域且引用正在使用中,它将访问已释放的内存。

文件系统布局

软件包和存储库的文件系统布局应遵循相同的约定,以便为浏览我们的源代码的用户提供一致的体验。

包布局

  • src: 包含所有的 C 和 C++ 代码

    • 同时包含未安装的 C/C++ 头文件

  • include: 包含所有已安装的 C 和 C++ 头文件

    • <package name>:对于所有已安装的 C 和 C++ 标头,它们应该由包名进行命名空间分隔

  • <package_name>:包含所有 Python 代码

  • test:包含所有自动化测试和测试数据

  • config:包含配置文件,例如 YAML 参数文件和 RViz 配置文件

  • doc:包含所有的文档

  • launch:包含所有的启动文件

  • package.xml:根据`REP-0140 <https://www.ros.org/reps/rep-0140.html>`_定义的(可能会更新用于原型设计)

  • CMakeLists.txt:仅限使用 CMake 的 ROS 软件包

  • setup.py:仅适用于仅使用Python代码的ROS软件包

  • README:可以作为项目的首页在GitHub上显示

    • 这可以是简短或详细的,但至少应该链接到项目文档

    • 考虑在此README中添加CI或代码覆盖率标签

    • 它也可以是 .rst 或任何 GitHub 支持的格式

  • CONTRIBUTING:描述贡献准则

    • 这可能涉及许可证的含义,例如使用 Apache 2 许可证时

  • LICENSE:此软件包的许可证副本或许可证

  • CHANGELOG.rstREP-0132 兼容的变更日志

存储库布局

每个软件包应该位于与软件包同名的子文件夹中。如果一个存储库只包含一个软件包,它可以选择位于存储库的根目录中。

上游软件包

Debian和Ubuntu上游的软件包

感谢Jochen Sprickerhof和Leopold Palomo-Avellaneda的辛勤努力,一些`ROS 2软件包现在可以从Debian和Ubuntu的主要存储库中获取<https://wiki.debian.org/DebianScience/Robotics/ROS2/Packages>`_。`这里有Jochen在ROSCon 2015上的简要概述<https://vimeo.com/142151399#t=29m15s>`_。原始的ROS软件包已经根据Debian的准则进行了修改,包括将软件包拆分为多个部分,在某些情况下更改名称,根据FHS准则安装到/usr,并在共享库上使用soversions。

此外,一些引导依赖项,如命令行工具如``vcstool``和``colcon``以及一些库如``osrf-pycommon``和``ament``也被上游打包。

与来自http://packages.ros.org的OSRF提供的ROS软件包不同,上游存储库中的软件包没有附加到特定的:doc:ROS分发版。相反,它们代表了时间上的快照,将定期在Debian不稳定版本中更新,然后在不同的Debian和Ubuntu发行版中的各个时间点被锁定。

不要混合使用

我们强烈建议不要在同一系统上混合使用来自上游Debian/Ubuntu和来自http://packages.ros.org的ROS软件包。在某些情况下,这样的混合系统可能会正常工作,但两组软件包之间可能会产生负面互动。我们正在与Jochen和他的朋友合作,通过文档和软件包冲突规范来最小化问题的可能性,但我们预计一些风险仍然存在,包括一些相当微妙的问题。

因此,我们建议您选择要么从上游安装软件包,要么从http://packages.ros.org安装,但不要同时两者兼而有之。不仅不应该同时安装来自两者的软件包,而且如果您打算使用上游软件包,那么您甚至不应该在apt源中具有http://packages.ros.org条目(即在``/etc/apt/sources*``中的任何文件中)。同时启用两者可能会导致两个来源之间名称重叠的软件包混合,例如``python3-rospkg``。

已知的差异

与packages.ros.org的ROS软件包相比,上游ROS软件包存在一些差异,人们应该注意:

  • 软件包集合不完整。

  • 软件包可能具有不同的名称并以不同的方式进行分区。

开发者工作流程

我们使用 GitHub项目面板 来跟踪与即将发布的版本和较大项目相关的未解决问题和活动中的PR。

通常的工作流程是:

  • 讨论设计(在适当的存储库上的GitHub票证,并在需要时在 https://github.com/ros2/design 上创建设计PR)

  • 在一个分支上的特性分支上编写实现

  • 编写测试

  • 启用并运行代码检查工具

  • 在本地运行测试,使用 ``colcon test``(请参阅 colcon 教程

  • 一旦本地构建没有警告并且所有测试都通过,请在你的功能分支上运行CI:

    • 访问 ci.ros2.org

    • 登录(右上角)

    • 点击 ci_launcher 作业

    • 点击"使用参数构建"(左列)

    • 在第一个框中输入"CI_BRANCH_TO_TEST",输入您的特性分支名称

    • 点击 build 按钮

    (如果您不是ROS 2的贡献者,您无法访问CI工作区。在这种情况下,请联系您的PR审查者为您运行CI)

  • 如果您的使用场景需要运行代码覆盖率:

    • 访问 ci.ros2.org

    • 登录(右上角)

    • 点击``ci_linux_coverage``作业

    • 点击"使用参数构建"(左列)

    • 确保将"CI_BUILD_ARGS"和"CI_TEST_ARGS"保留为默认值

    • 点击 build 按钮

    • 在文档的末尾,有关于如何 :ref:`解读报告结果 <read-coverage-report>`和 :ref:`计算覆盖率 <calculate-coverage-rate>`的说明

  • 如果 CI 作业在没有警告、错误和测试失败的情况下构建成功,请在您的 PR 或汇总所有 PR 的高级票证上发布您作业的链接(参见示例 here)

    • 请注意,这些徽章的 Markdown 代码位于“ci_launcher”作业的控制台输出中

  • 当 PR 被批准后:

    • 提交 PR 的人使用“Squash and Merge”选项将其合并,以便保持干净的历史记录

      • 如果这些提交应该保持分开:将所有的 nitpick/linters/typo 提交合并在一起,然后合并剩余的提交集合

        • 注意:每个 PR 应该针对一个具体的功能,因此大部分情况下 Squash and Merge 是有意义的

  • 合并后删除该分支

架构开发实践

本节描述了在对ROS 2进行重大架构更改时应采用的理想生命周期。

软件开发生命周期

本节逐步描述了如何规划、设计和实现一个新功能:

  1. 任务创建

  2. 创建设计文档

  3. 设计评审

  4. 实现

  5. 代码评审

任务创建

需要对ROS 2关键部分进行更改的任务应在发布周期的早期阶段进行设计审查。如果设计审查在后期阶段进行,则更改将成为未来发布的一部分。

  • 在适当的 ros2 代码库 中创建一个问题,清楚地描述正在进行的任务。

    • 它应具有明确的成功标准,并突出显示预期的具体改进。

    • 如果该功能针对ROS版本,请确保在ROS版本票据中进行跟踪(示例)。

编写设计文档

设计文档不得包含机密信息。是否需要为您的更改编写设计文档取决于任务的大小。

  1. 您正在进行小的更改或修复错误:

  • 不需要设计文档,但应在适当的存储库中打开一个问题来跟踪工作并避免重复努力。

  1. 您正在实施新功能或希望为OSRF拥有的基础架构(如Jenkins CI)做贡献:

在拉取请求或提交消息中提及相关的ros2问题(例如,任务ros2/ros2#<问题编号>的设计文档)。详细说明可在`ROS 2 Contribute <https://design.ros2.org/contribute.html>`__页面找到。设计评论将直接在拉取请求上进行。

如果任务计划在特定版本的ROS中发布,应在拉取请求中包含这些信息。

设计文档审查

一旦设计准备好进行审查,应打开拉取请求并指定适当的审阅者。建议将项目所有者(即所有受影响软件包的维护者,根据``package.xml``维护者字段定义,参见`REP-140 <https://www.ros.org/reps/rep-0140.html#maintainer-multiple-but-at-least-one>`__)作为审阅者。

  • 如果设计文档很复杂或者审阅人员的日程安排有冲突,可以设置一个可选的设计审查会议。在这种情况下,

    会议之前

    • 至少提前一周发送会议邀请

    • 建议会议持续一个小时

    • 会议邀请应列出审查期间需要做出的所有决策(需要包维护者批准的决策)

    • 会议要求出席人员:设计拉取请求的审查人员

      会议可选出席人员:所有OSRF工程师,如果适用的话

    在会议期间

    • 任务负责人主导会议,提出他们的想法,并管理讨论,以确保在规定时间内达成一致意见

    会议之后

    • 任务负责人应该向所有与会者发送会议纪要

    • 如果有关设计的小问题被提出:

      • 任务所有者应根据反馈意见更新设计文档的拉取请求

      • 不需要额外的审查

    • 如果有关设计方面的重大问题被提出:

      • 可以删除没有明确一致意见的部分,这是可以接受的

      • 设计中有争议的部分可以在未来作为一个独立任务重新提交

      • 如果无法移除有争议的部分,直接与包的所有者合作以达成一致

  • 一旦达成共识:

    • 确保已合并`ros2/design <https://github.com/ros2/design/>`__的拉取请求(如果适用)

    • 更新并关闭与此设计任务相关的 GitHub 问题

实现

开始之前,请参阅 Pull requests 部分以了解最佳实践。

  • 对于每个需要修改的存储库:

    • 修改代码,完成后或定期备份工作,然后进入下一步。

    • 使用``git add -i``进行自我审查,并将更改添加到暂存区。

    • 使用``git commit -s``创建一个新的带有签名的提交。

      • 一个拉取请求应该包含最少的语义化的有意义的提交(例如,大量的一行提交是不可接受的)。在反馈过程中创建新的修正提交,或者根据需要使用``git commit --amend``修正现有的提交,如果你不想每次都创建新的提交。

      • 每个提交必须有适当编写的、有意义的提交消息。更多说明请查看 这里

      • 将文件移动操作放在单独的提交中进行,否则 Git 可能无法准确跟踪文件的历史记录。

      • 无论是拉取请求描述还是提交信息,都必须包含与相关 ros2 问题的引用,这样在合并拉取请求时会自动关闭它。有关更多详细信息,请参阅此 文档

      • 推送新的提交。

代码审查。

一旦修改准备好进行代码审查:

  • 为每个修改后的仓库打开一个拉取请求。

    • 记住遵循 拉取请求 的最佳实践。

    • GitHub 可用于从命令行创建拉取请求。

    • 如果任务计划在特定版本的ROS中发布,则应在每个拉取请求中包含此信息。

  • 应在拉取请求中提及已审查设计文档的包所有者。

  • 代码审查SLO:尽管审查拉取请求是尽力而为的,但有助于评审人员在一周内对拉取请求发表评论,代码作者在一周内回复评论,以确保不会失去上下文。

  • 按照通常的做法,根据反馈进行迭代,根据需要修改和更新开发分支。

  • 一旦PR被批准,软件包维护人员将合并这些更改。

构建农场介绍

构建农场位于 ci.ros2.org

每晚我们运行夜间任务,对各种场景和平台进行构建和运行所有测试。此外,在合并之前,我们还会将所有拉取请求与这些平台进行测试。

这是当前的目标平台和架构设置,尽管它会随时间变化而发展:

  • Ubuntu 22.04 Jammy

    • amd64

    • aarch64

  • Windows 10

    • amd64

构建农场上有几类工作:

  • 手动工作(由开发人员手动触发):

    • ci_linux:在Ubuntu Xenial上构建+测试代码

    • ci_linux-aarch64:在ARM 64位机器(aarch64)上的Ubuntu Xenial上构建+测试代码

    • ci_linux_coverage:构建 + 测试 + 生成测试覆盖率

    • ci_windows:在 Windows 10 上构建 + 测试代码

    • ci_launcher:触发上述所有作业

  • 每晚运行的夜间任务:

    • 调试:使用CMAKE_BUILD_TYPE=Debug构建和测试代码

      • 每夜构建(Linux平台,调试模式)

      • 每夜构建(Linux平台,aarch64架构,调试模式)

      • 每夜构建(Windows平台,调试模式)

    • 发布:使用CMAKE_BUILD_TYPE=Release构建和测试代码

      • 每夜构建:Linux平台 Release 版本

      • 每夜构建:Linux(aarch64架构)平台 Release 版本

      • 每夜构建:Windows平台 Release 版本

    • 重复执行:构建,然后运行每个测试最多20次或直到失败(也称为稳定性测试)

      • 每夜Linux重复执行

      • 每夜Linux-aarch64重复执行

      • 每夜Windows重复执行

    • 覆盖率:

      • nightly_linux_coverage:构建 + 测试代码 + 分析 C/C++ 和 Python 的覆盖率

        • 结果以 Cobertura 报告的形式导出

  • 打包(每晚运行;结果打包成一个归档文件):

通过提供源码和二进制包的构建、持续集成、测试和分析,另外两个构建农场支持 ROS / ROS 2 生态系统。

有关详细信息、常见问题和故障排除,请参阅:构建农场

覆盖率运行注意事项

ROS 2软件包的组织方式是,给定软件包的测试代码不仅包含在该软件包内,还可以存在于其他软件包中。换句话说,在测试阶段,软件包可以执行属于其他软件包的代码。

为了达到ROS 2核心软件包中所有可用代码的覆盖率,建议使用一组固定的建议存储库运行构建。该集合在Jenkins中的覆盖率作业的默认参数中定义。

如何从构建工厂报告中读取覆盖率

查看给定包的覆盖率报告:

  • 当``ci_linux_coverage``构建完成后,点击``Coverage Report``

  • 向下滚动到``Coverage Breakdown by Package``表格

  • 在表格中,查看名为"Name"的第一列

构建工厂中的覆盖率报告包括在ROS工作空间中使用的所有软件包。覆盖率报告包括与同一软件包对应的不同路径:

  • 以以下形式的名称条目: src.*.<repository_name>.<package_name>.*。这些对应于针对软件包自身源代码的单元测试运行

  • 以以下形式的名称条目: build.<repository_name>.<package_name>.*。这些对应于针对构建或配置时生成的文件的软件包的单元测试运行

  • 以以下形式的名称条目: install.<package_name>.*。这些对应于来自其他软件包的测试运行的系统/集成测试

如何从构建农场报告中计算覆盖率

使用自动脚本获取组合单元覆盖率:

  • 从ci_linux_coverage Jenkins构建中复制构建的URL

  • 下载 get_coverage_ros2_pkg 脚本

  • 执行脚本:``./get_coverage_ros2_pkg.py <jenkins_build_url> <ros2_package_name>``(README

  • 从脚本输出的"Combined unit testing"最后一行中获取结果

替代方法:从覆盖率报告中获取组合单元覆盖率(需要手动计算):

  • 当ci_linux_coverage构建完成后,点击``Cobertura Coverage Report``

  • 向下滚动到``Coverage Breakdown by Package``表格

  • 在表格中,查找第一列“Name”下的内容(其中<package_name>是您正在测试的软件包):

    • 在模式“src.*.<repository_name>.<package_name>.*”下的所有目录中,获取列“Lines”中的两个绝对值。

    • 在模式“build/.<repository_name>.*”下的所有目录中,获取列“Lines”中的两个绝对值。

  • 使用上述选择:对于每个单元格,第一个值是测试的行数,第二个值是代码总行数。汇总所有行以获得测试的行数总和和正在测试的代码行数总和。进行除法运算以获取覆盖率。

如何使用 lcov(Ubuntu)在本地测量覆盖率

要在自己的机器上测量覆盖率,请安装``lcov``。

sudo apt install -y lcov

本节的其余部分假设您正在使用自己的 colcon 工作空间。使用覆盖率标志进行调试编译。可以使用 colcon 标志来针对特定的软件包。

colcon build --cmake-args -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} --coverage" -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} --coverage"

lcov 需要一个初始基准,您可以使用以下命令生成。根据您的需要更新输出文件位置。

lcov --no-external --capture --initial --directory . --output-file ~/ros2_base.info

运行测试以获得与覆盖率测量相关的软件包。例如,如果同时测量``rclcpp``和``test_rclcpp``

colcon test --packages-select rclcpp test_rclcpp

使用类似的命令捕获 lcov 结果,但这次不使用``--initial``标志。

lcov --no-external --capture --directory . --output-file ~/ros2.info

合并追踪的 .info 文件:

lcov --add-tracefile ~/ros2_base.info --add-tracefile ~/ros2.info --output-file ~/ros2_coverage.info

生成 HTML,以便轻松可视化和标注覆盖的代码行。

mkdir -p coverage
genhtml ~/ros2_coverage.info --output-directory coverage