RFC-0186:适用于 Fuchsia 的 Bazel

RFC-0186:适用于 Fuchsia 的 Bazel
状态已接受
区域
  • 构建
说明

建议将 Fuchsia SDK 和 Bazel 引入 fuchsia.git,从而将 Bazel 作为 Fuchsia 组件的首选主要构建系统。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-04-21
审核日期(年-月-日)2022-08-29

摘要

我们的计划是将 Fuchsia SDK 和 Bazel 引入 fuchsia.git,从而将 Bazel 作为 Fuchsia 组件的首选主要构建系统。

Fuchsia 团队致力于完成此迁移。具体而言,Fuchsia Build 团队、Fuchsia Bazel SDK 集成团队、Fuchsia 平台基础设施团队、Fuchsia 软件组装团队、Fuchsia TPM 团队和 Fuchsia DevRel 团队已承诺交付并支持此计划。

定义

  • “树内”是指存储在 fuchsia.git 中的所有内容。
  • “树外”是指以 Fuchsia 为目标平台并基于 Fuchsia SDK 构建的代码,这些代码不在平台源代码中,并且存储在 fuchsia.git 之外。例如,Intel WLAN 驱动程序构建示例使用 Fuchsia Bazel SDK 在单独的代码库中构建。
  • Bazel - 一款用于软件开发的开源构建和测试工具。

设计初衷

自 2022 年第 1 季度起,Fuchsia 的平台(即 fuchsia.git 中的代码)构建使用 GN/Ninja。2021 年,基于 SDK 的开发加速,以偿还技术债务并实现未来战略。Fuchsia 选择 Bazel 作为基于 SDK 的开发的理想途径,并将其应用于现有和新的源代码库。

我们发现,无论组件和产品开发者在哪个树/代码库中工作,构建和使用 Fuchsia 的许多用例都是相同的。例如:将源代码编译为组件,并将该组件及其资源打包到 Fuchsia 软件包中,在目标平台上运行该软件,组装产品,生成符号,流式传输日志等。我们希望通过统一的 Bazel 和 SDK 工作流以及实现来降低复杂性和成本。

为了让树外开发者产生共情,并创建和提供出色的 Bazel + SDK 体验,Fuchsia 团队本身必须每天使用 SDK 及其 Bazel 集成,并将其作为受支持的工作流的一部分。

利益相关方

教员:rlb

审核者

  • digit - Fuchsia 的 build 团队
  • chaselatta - Fuchsia 的 Bazel SDK 团队
  • mangini - Fuchsia 的 Bazel SDK 团队和 Fuchsia DevRel
  • nsylvain - Fuchsia 的 EngProd 团队
  • abarth - Fuchsia 架构

已咨询

  • aaronwood - 产品组装
  • awolter - 产品组装
  • dannyrosen - Eng Excellence
  • keir - Pigweed
  • tmandry - Rust for Fuchsia
  • brunodalbo - 连接
  • surajmalhotra - 驱动程序框架
  • amathes - Fuchsia PM Lead
  • cphoenix - 诊断
  • akbiggs - Fuchsia 上的 Flutter

共同化

此 RFC 的初始社交化提案已通过 Fuchsia Build 团队、Fuchsia SDK 团队、Fuchsia 的工具链团队、Fuchsia EngProd 团队、Fuchsia 的 Rust 团队以及 Fuchsia 的各种代表和主管的审核。

设计

此 RFC 旨在制定项目政策。以下是此政策的设计原则列表。

增量:我们将一次迁移一个组件,并在迁移过程中不断学习。

包容性:尽管 Fuchsia 团队已采用 Bazel,并将继续扩大其使用范围,并使用 Bazel 为用户创建清晰的路径,但使用 Bazel 构建 Fuchsia 软件并不是严格的要求。IDK 必须继续与 build 系统无关。

我们还遵循以下原则:构建系统的维度可以与源代码库的维度正交。将 build 目标迁移到 Bazel 和 SDK,并不一定需要将代码迁移到另一个代码库。

最终,我们希望以若干项目的形式组织 Fuchsia 开发,其中每个项目都将 Fuchsia SDK 作为输入,并生成若干可组装到 Fuchsia 系统映像中的软件包或其他二进制制品作为输出。这些项目可以由 Fuchsia、我们的合作伙伴或第三方开发者拥有。这些项目的大小也各不相同,从交付单个二进制文件的小项目到交付 Fuchsia 平台的重要部分(例如 fuchsia.googlesource.com/platform.git)的大项目都有。随着我们越来越接近这种情况,我们可以决定如何将代码组织到项目中,以最大限度地提高效率。

此 RFC 描述了朝着最终未来迈出的重要一步,即重构大部分 Fuchsia 开发,使其托管在 Fuchsia SDK 之上。重新托管到 Fuchsia SDK 后,我们将能够更灵活地将开发工作组织到项目中。

实现

现有采用计划

我们已开始评估和采用 Bazel 及 SDK,以实现以下目标:

  • 构建并测试一个简单但承载负载的驱动程序,该驱动程序已纳入工作站
  • 构建并测试一个简单但用户可访问的组件,该组件已纳入工作站
  • 构建并测试部分数量不断增加的驱动程序
  • 构建和测试示例及其他项目,帮助开发者开始为 Fuchsia 开发应用
  • 在 Fuchsia 嵌入器上构建和测试 Flutter
  • 构建和测试工作站体验代码
  • Google 产品的硬盘产品组装

其他采用提案

在继续下文之前,我们将收集证据,证明 Bazel SDK 具有最低可行性和足够的功能来支持最终会被一定数量的用户使用的软件和系统。上述简单驱动程序和简单组件将使用 SDK 构建并发布到全局集成,然后纳入当前的工作站产品组装流程。然后,Bazel SDK 中的代码及其在托管简单驱动程序和简单组件的代码库中的配置,以及我们将这些预构建组件发布以供工作站构建流程使用的机制,将由 Fuchsia 工程委员会和 Fuchsia 安全团队进行审核。只有在这些审核通过且用户收到配备简单驱动程序和简单组件的产品后,我们才会继续处理。

除了上述正在进行的“现有采用计划”之外,我们还将在 fuchsia.git 内创建 Bazel build 树,与 GN build 树并列。Bazel build 将能够构建和测试打包的组件,并组装产品。我们将与树外开发者分享 Fuchsia Bazel SDK 的逻辑(Bazel 引导加载程序、Starlark 规则)。

我们将首先使用 Bazel 和 SDK 在 fuchsia.git 中实现产品组装用例。我们将从fx build流程的末尾开始反向工作,这意味着我们可以提供线性 GN/Ninja -> Blaze 流程。

替代文本:从 Ninja 平台 build 到 Bazel 程序集的树内流程 - Bazel 中的程序集

然后,我们将确定一些候选组件,以便从 GN/Ninja 平台 build 逐步提取到 Bazel 平台 build,同时保留从同一制品组装有凝聚力的 Fuchsia 映像的能力,并保留或改进其他受支持的工作流。

替代文本:从 Ninja 平台 build 到 Bazel assembly 的 in-tree 流 - 将组件移至 Bazel

在实现对这些第一批候选组件的支持时,我们会关注 KPI(如下所述),并与早期采用者互动,以确保他们的工程生产力不会下降。我们会随时向 Fuchsia 团队报告状态和测量结果。

我们设想,fuchsia.git 贡献者将使用 fx 作为前端来驱动其 build,而 fx 将在后台管理 Bazel 和 GN/ninja 调用。对于过渡,保留 fx setfx build 等前端将有助于我们封装迁移的详细信息并保留工作流。

目前的计划是将调用 GN/Ninja 和 Bazel 的所有方面封装在 fint 中,并且我们注意到,以 fint 实现的任何 fx 构建工作流也会因上述封装而得以保留。

Fuchsia build 团队将负责保留编译器/模型训练功能,并与 GN build 合作,帮助保留语料库大小。

将 fuchsia.git 中的组件迁移到 Bazel 不应与在代码库之间移动组件混为一谈。迁移到 Bazel 的 fuchsia.git 中的组件可以保留在 fuchsia.git 中。此 RFC 不涉及代码库迁移。 不过,在将所有目标组件迁移到 Bazel 和 SDK 之前,我们计划重新审视构建架构,并与 FEC 合作,确定是否应将由 Bazel 和 SDK 构建的任何组件移出 fuchsia.git 树。

KPI

我们建议为此工作设定以下 KPI:

  • 开发者满意度(通过问卷调查衡量)
  • null 构建时间
  • 作为长杆预提交构建器的配置的完整构建时间
  • 相应配置的提交前测试时间

对于“空构建时间”(这是了解启动构建系统本身延迟的一种方式),我们将结合总体开发者满意度和效率来考虑。我们将努力避免空 build 时间出现 50% 的回归,除非我们确定空 build 时间是限制开发者满意度和工作效率的重要因素。

对于“配置的完整构建时间”和“预提交测试时间”,我们建议不允许 KPI 出现超过 10% 的暂时性回归,并期望最终能够改进所有 KPI。

人员配备

这项工作的员工配备和资金支持细节待定,不在本 RFC 的讨论范围之内。不过,我们注意到,制定人员配备和资金计划对于成功将软件发展为 SDK 和 Bazel 至关重要。Fuchsia 有两个团队(Bazel SDK 集成团队、Fuchsia build 团队)最初承诺投入资源,与组件团队合作,帮助 Fuchsia 工程师顺利完成此过程。我们预计 Bazel SDK 集成团队和 Fuchsia build 团队将推动首次迁移到 Bazel 和 SDK,然后根据这些经验,我们将为进一步迁移提供明智的人员配备/资金估计。

自动化工具

在将功能领域迁移到 Bazel 方面取得重大进展后,我们将探讨如何添加自动化检查,以确保这些领域未来的增长不会意外配置为使用 GN 构建。此阶段将在流程结束时或接近结束时进行,即在我们证明 KPI 目标已实现且未出现不理想的情况之后。

未来可能需要考虑的事项

如果前景明朗,我们未来可能会将 Fuchsia 的所有源代码迁移到 Bazel。在未来的潜在版本中,我们设想实现纯 Bazel 构建,并弃用用于管理构建的 fxfint 命令,而是完全使用 Bazel 命名规范来管理事物。

文档

根据 RFC-139,我们正在更新 fuchsia.dev 上的文档,以说明和指导如何将 SDK 与 Bazel 搭配使用。

我们将更新 fuchsia.dev 上面向 Fuchsia 贡献者的文档,说明何时以及如何在树内使用 Bazel。此文档将包含有关何时配置组件以使用 Bazel 或 GN 进行构建的指南,以及面向贡献者的有关如何运行 build 的指南。此文档也将公开。

缺点、替代方案和未知因素

我们注意到,RFC-0153 提议使用 Fuchsia 平台 build 所用的开源 Ninja 工具的临时自定义版本,目前仍在进行中,尚未被此 RFC 取代。Fuchsia 在一段时间内仍将依赖 Ninja,因此对 Ninja 的生活品质改进非常宝贵且受欢迎。

未知:如果从技术上讲,可以将 Fuchsia 的所有代码迁移到 Bazel,同时保持良好的用户体验并遵循惯用的 Bazel 用法。我们将与我们的构建合作伙伴一起探索这种可能性,并确定是否可以实现。我们注意到,这种未知情况不会阻碍我们打算将 Fuchsia 的组件和软件包迁移到 Bazel 的意图,也不会阻碍此 RFC 获得批准。

功能差异:Bazel 对同一 build 调用中的目标配置转换支持不佳。例如,如果用户想将所有可执行文件构建为 release,并将一个可执行文件构建为 asan,则需要两次调用 bazel build。我们认为这不会带来风险或造成严重问题,但它与 Fuchsia 基于 GN 的现有变体系统有所不同。

可能存在的风险:我们知道 Bazel 的内置 C/C++ 规则存在一些限制,可能无法灵活地构建低级 Fuchsia 库和二进制文件。根据 Bazel 团队对 Fuchsia 及其公开路线图的更新,将 Bazel C/C++ 移植到社区拥有和维护的 Starlark 规则是首要任务。该概念已得到充分证明,大部分风险已消除。

未知:Bazel 的文件系统沙盒化和正确性/密封性净结果权衡的效应。Bazel 使用文件系统沙盒和符号链接来提供增量正确性和密封性保证。已知此操作最多会使典型 build 工作器的干净 build 工作负载增加 10%,并会影响 build 时长。我们预计,当密闭性与远程 build 执行相结合,以实现更好的缓存利用率、提前截止和浅层 build 时,所带来的好处将远远超过这一差异。不过,如果我们发现密封性的成本高于预期,并且无法通过预期的性能优势来抵消,那么我们会考虑停用沙盒 (即 --spawn_strategy=local)。

未知:Fuchsia 团队将从 Bazel 的上游所有者那里获得何种程度的支持。我们没有理由认为我们不会获得支持,并且此 RFC 的一个目标是与 Bazel 的所有者建立更强大的合作伙伴关系。我们与 Bazel 团队的初步会议和互动非常富有成效,他们非常支持我们,并愿意详细了解我们的要求和观察结果。我们向 Bazel 团队提出的问题很快就能得到解答。我们相信,随着 Fuchsia 继续采用 Bazel 作为 SDK (RFC-139),并且随着 Fuchsia 证明自己是 Bazel 的优秀客户,我们将看到 Bazel 团队持续提供支持并积极参与。

未知:我们将如何使用 Bazel 和 Fuchsia SDK 编译 Rust 代码。 我们看到,目前正在积极开发对 Bazel 的 Rust 支持,但我们需要对此进行测试,并探索它是否能满足 Fuchsia 的需求。我们预计后续会有一份 RFC 介绍我们如何支持使用 Bazel 和 Fuchsia SDK 构建 Rust 代码。这项工作将与 Fuchsia 团队密切合作。

未知,待确定:由 GN/Ninja 构建的制品与在 fuchsia.git 中使用 Bazel 构建的打包组件之间的确切接口,以便将它们组装到产品映像中。我们预计会扩大产品组装范围来解决此问题,具体细节待定。

未知:Fuchsia 团队正在评估如何支持 Windows 作为开发者宿主环境,这将对 Fuchsia 如何支持基于 SDK 的开发(包括使用 Bazel)提出新的要求。我们尚未满足所有这些要求。不过,我们注意到 Bazel 支持将 Windows 作为开发者主机,并且目前没有理由认为 Bazel 在 Windows 上无法正常运行。我们会与 Bazel 团队保持密切联系,以防出现此类阻碍。

未知:基础设施中的测试可能或可能不会由 bazel test 驱动。这不会阻碍此 RFC,因为我们主要关注使用 Bazel 进行编译。

替代方案:继续使用 fuchsia.git 中组件的当前 GN/Ninja build。我们继续资助并行工作,无法从 Bazel 带来的工程生产力优势中受益,并且与开发者生态系统之间的共情差距继续存在。此外,平台 build 维护人员表示,继续依赖 GN/Ninja 会带来风险,因为这些系统无法保证密封的干净 build 或正确的增量 build。

替代方案:重新考虑 RFC-0139 中采用 Bazel 的决定,改为采用其他构建系统,然后围绕该构建系统统一树内和树外构建。由于没有理由重新考虑 RFC-0139,因此我们拒绝此替代方案。

替代方案:除了采用 Bazel 和 fuchsia.git 中的 SDK,我们还可以优先将组件代码从 fuchsia.git 移至由 Bazel 和 SDK 提供支持的不同代码库。这些代码库之间的集成只能通过预构建完成。最终结果是一样的:Fuchsia 团队已采用 Bazel 和 SDK 作为开发 Fuchsia 的主要方式,并且正在以与 Google 外部的 Fuchsia 开发者相同的方式开发 Fuchsia。我们曾考虑过此选项,但一些团队表示希望能够通过 SDK(例如接口)访问的代码及其组件代码作为同一提交的一部分进行更改。不过,我们注意到,有些团队已计划通过将代码移至 fuchsia.git 之外的代码库来采用 Bazel 和 SDK。

替代方案:(已考虑但被拒绝)我们不为 fuchsia.git 中的代码设置两个 build 系统,而是将组件移出树,以便使用 SDK 和 Bazel 构建这些组件。这样也能实现相同的长期目标(使用 SDK 和 Bazel 构建更多代码),但我们认为,这种方案需要更长的时间才能实现目标。此外,它还会引入多个变量(将代码的 build 迁移到 Bazel + SDK,将代码迁移到其他代码库),而我们希望一次只更改一个变量。我们倾向于先将组件的 build 更改为 Bazel 和 SDK,然后选择性地将组件的代码移至另一个代码库。

在先技术和参考资料

使用或迁移到 Bazel 的其他示例项目

Bazel 的用户社区正在不断壮大。Android 开源项目正在迁移到 Bazel,之前一直使用 Ninja 和 Make 的组合。Bazel 是 Google 多个成功开源项目(例如 Abseil 和 Tensorflow)的首选构建系统。

其他 build 系统迁移

Fuchsia 的 ZN->GN

不妨看看 Fuchsia 执行的另一项 build 系统迁移:ZN->GN 迁移,也称为“build 统一”。

在 build 统一之前,Fuchsia 有两个基于 GN/Ninja 的 build,它们按顺序调用。ZN/Ninja build 会先运行并构建一些制品,然后 GN/Ninja build 会运行并构建其他制品。您可以在 GN 中使用 ZN 输出,但不能反过来。

ZN 和 GN 之间的边界是围绕 ZBI 内容等制品绘制的。 这不是一个好的接口,因为它阻止了使用在 GN 中构建的制品(例如 FIDL、组件、软件包、Rust 支持)来生成在 ZN 中构建的制品(例如驱动程序、早期启动程序)。

曾提出将 GN 制品迁移到 ZN 的方案,但事实证明该方案不可行。而是将 ZN 制品移到了 GN 中。通过生成 build 结果的“摘要清单”,并确保没有单个迁移步骤会意外更改清单,我们对这种迁移制品的过程进行了逐步验证。当 ZN build 变为空(未贡献给上述清单)时,此工作结束,然后被移除。

经验教训:

  1. 在迁移期间保持重要工作流的连续性。
  2. 在分阶段迁移时,使用明确且有意的合约。Fuchsia ZN 与 Fuchsia GN 之间的合同并非有意为之(Fuchsia 不会仅生成 ZBI 以供外部使用或扩展)。此 RFC 建议使用软件包和产品组装作为合约,这体现了我们吸取的经验教训(请参阅 RFC-0072RFC-0095 和即将发布的 Fuchsia 平台路线图)。
  3. 在制定完成迁移的计划之前,不应开始迁移。

Chrome 的 GYP->GN

Fuchsia 团队成员参与的另一项 build 系统迁移是将 Chrome 从 GYP 迁移到 GN。

Chrome 之所以要从 GYP 迁移到 GN,是因为 GYP build 难以推理、难以解释,并且“gn gen”的等效操作大约需要一分钟。这严重影响了 Chrome 团队的工作效率,因此需要另一个构建系统。

迁移最终成功完成。我们估计,这项工作在约 3 年的实际时间内耗费了超过 8 个人年。

Chrome 团队尝试了增量方法,但在 9 个月后决定暂停这项工作。他们发现,build 配置中的阻抗不匹配是增量方法的主要阻碍。后来,该团队开始与 GYP build 并行进行“自下而上”的 GN build。首先是信息提示机器人,然后是真正的机器人。此迁移扩展到了所有目标平台(Linux、Windows、Mac、Android、iOS)。迁移之所以成功,一个关键原因是它有一位坚定的倡导者,致力于完成迁移。

我们从这次迁移中了解到:

  • 由于情况过于笼统,很难用简单易懂的说明来描述该怎么做。执行转换的人员必须几乎完全了解旧版和新版 build 系统。几乎没有人知道这一点。
  • 此类迁移需要付出大量努力,并且很难长时间保持贡献。
  • 我们可以清楚地说明迁移的先后顺序,并提供易于访问的跟踪和组织功能,从而让大家更轻松地协助完成迁移。
  • 对用户给予极大的关怀和同理心对于成功迁移至关重要。在此次迁移中,有大量用户功能和用户工作流程需要妥善迁移。