RFC-0186:适用于 Fuchsia 的 Bazel

RFC-0186:适用于 Fuchsia 的 Bazel
状态已接受
领域
  • Build
说明

他们提议通过将 Fuchsia SDK 和 Bazel 引入 fuchsia.git,来整合 Bazel 并将其作为 Fuchsia 组件选择的主要构建系统。

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

总结

我们计划通过将 Fuchsia SDK 和 Bazel 引入 fuchsia.git,来整合 Bazel,并将其作为为 Fuchsia 组件选择的主要构建系统。

Fuchsia 团队致力于完成此次迁移。具体而言,Fuchsia 构建团队、Fuchsia Bazel SDK 集成团队、Fuchsia 平台基础架构、Fuchsia Software Assembly、Fucsia TPM 团队和 Fuchsia DevRel 都表示承诺会实施和支持该计划。

定义

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

设计初衷

自 2022 年第 1 季度起,Fuchsia 的平台(即 fuchsia.git 中的代码)build 使用 GN/Ninja。2021 年,加速了基于 SDK 的开发,以偿还技术债务并实施未来的策略。Fuchsia 选择了 Bazel 作为基于 SDK 的开发渠道,并正在将 Bazel 用于现有和新的源代码库。

我们发现,对于所有组件和产品开发者,构建和使用 Fuchsia 的许多用例都是相同的,无论他们在什么树/代码库中进行工作。例如:将源代码编译到组件中,然后将该组件及其资源打包到 Fuchsia 软件包、在目标上运行该软件、组装产品、生成符号、流式传输日志等。我们致力于通过整合一组统一的 Bazel 和 SDK 工作流及实现来降低复杂性和成本。

为了对我们的外部开发者产生同理心,打造并提供出色的 Bazel + SDK 体验,Fuchsia 团队本身必须在日常工作中使用 SDK 及其 Bazel 集成,并将其作为受到良好支持的工作流的一部分。

利益相关方

教员:rlb

审核者

  • 位数字 - Fuchsia 的构建团队
  • chaselatta - Fuchsia 的 Bazel SDK 团队
  • Mangini - Fuchsia 的 Bazel SDK 团队和 Fuchsia DevRel
  • nsylvain - Fuchsia 的 EngProd 团队
  • Abarth - Fuchsia 建筑

咨询人员

  • aaronwood - 产品组装
  • awolter - 产品装配
  • dannyrisen - 卓越工程
  • keir - 鼠尾草
  • tmandry - 适用于 Fuchsia 的 Rust
  • Brunodalbo - 连接
  • surajmalhotra - 驱动程序框架
  • amathes - Fuchsia 项目经理主管
  • cphoenix - 诊断
  • akbiggs - Fuchsia 上的 Flutter

社交

此 RFC 的初始社交方案已通过了 Fuchsia Build 团队、Fuchsia SDK 团队、Fucchsia 的工具链团队、Fucchsia EngProd 团队、Fuchsia 的 Rust 团队以及 Fuchsia 各个代表和潜在客户的审核。

设计

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

增量迁移:我们一次迁移一个组件,边学习边学习。

包容性:虽然 Fuchsia 团队采用了并将继续扩展 Bazel 的使用,并使用 Bazel 为用户创建光线充足的路径,但使用 Bazel 构建 Fuchsia 软件并非硬性要求。IDK 必须继续与构建系统无关。

我们还遵循以下原则:构建系统的维度可以与源代码库的维度成正交。将 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 构建并发布到全球集成中,然后纳入当前的工作站产品组装流程中。然后,Fuchsia 工程委员会和 Fuchsia 安全团队将审核 Bazel SDK 中的代码及其在托管简单驱动程序和简单组件的代码库中的配置,以及我们发布这些预构建组件以供工作站构建流程使用的机制。只有在这些审核通过,并且用户通过简单的驱动程序和简单的组件收到产品后,我们才会进行处理。

随着上述“现有采用计划”的推进,我们将在 fuchsia.git 内和 GN build 树旁边创建一个 Bazel build 树。Bazel 构建能够构建和测试打包的组件并组装产品。对于树外开发者,我们将与 Fuchsia Bazel SDK 共享逻辑(Bazel 引导、Starlark 规则)。

首先,使用 Bazel 和 SDK 在 fuchsia.git 中实现 product assembly 用例。我们将从 fx build 流程结束开始反向设计,这意味着我们可以提供线性 GN/Ninja -> Blaze 流程。

替代文本:
从 ninja 平台 build 流入树内 bazel 组装 - 在 bazel 中组装

然后,我们将确定一些候选组件,这些组件可从 GN/Ninja 平台 build 逐步提取到 Bazel 平台 build,同时保留从同一工件组合出统一的 Fuchsia 映像的功能,并保留或改进其他受支持的工作流。

替代文本:从 ninja 平台 build 流入 bazel 组件的树内 - 将组件移动到 bazel

在我们实现对第一批候选组件的支持时,我们将观察 KPI(下文会提到),并将与尝鲜者互动,以确保他们的工程效率不会下降。在此过程中,我们会向 Fuchsia 团队报告状态和测量结果。

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

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

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

将 fuchsia.git 中的组件迁移到 Bazel 不会与在代码库之间移动组件相混淆。fuchsia.git 中已迁移到 Bazel 的组件可以保留在 fuchsia.git 中。代码库迁移不在本 RFC 的范围内。不过,在完成将所有目标组件迁移到 Bazel 和 SDK 之前,我们计划重新审视构建架构并与 FEC 合作,以确定是否有必要将 Bazel 和 SDK 构建的任何组件移出 fuchsia.git 树。

KPI

为此,我们提出了以下 KPI:

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

对于“null 构建时间”(这是了解构建系统本身的延迟时间的一种方式),我们将结合开发者的整体满意度和产品来审视这一问题。我们的目标是不允许 null 构建时间回归 50%,除非我们确定 null 构建时间是限制开发者满意度和工作效率的一个重要因素。

对于“配置的完整构建时间”和“提交前测试时间”,我们提议某个 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 构建组件的指南,以及面向贡献者的指南,以说明如何运行构建。本文档也将是公开的。

缺点、替代方案和未知情况

我们注意到,RFC-0153(提议使用 Fuchsia 平台 build 所使用的开源 Ninja 工具的临时自定义版本)仍在进行中,并且未被此 RFC 所取代。Fuchsia 会在一段时间内继续依赖 Ninja,因此为 Ninja 改进生活质量是非常有价值的,欢迎使用。

未知:从技术层面来讲,是否能够将 Fuchsia 的所有代码迁移到 Bazel,同时保持良好的用户体验并保持惯用的 Bazel 用法。我们将与构建合作伙伴一起探索这种潜力,并确定是否存在视线。我们注意到,这一未知情况并不妨碍我们将 Fuchsia 的组件和软件包迁移到 Bazel,也无意阻止此 RFC 获得批准。

功能差异:Bazel 无法很好地支持同一构建调用中的目标配置转换。例如,如果用户希望将所有可执行文件构建为 release,将一个可执行文件构建为 asan,那么就是两次 bazel build 调用。我们认为这并不是一种风险或问题,但它与 Fuchsia 现有的基于 GN 的变体系统有所不同。

可能的风险:我们知道 Bazel 的内置 C/C++ 规则存在一些限制,导致它们在构建低层级 Fuchsia 库和二进制文件时可能不够灵活。 根据 Bazel 团队对 Fuchsia 的更新及其公开路线图,将 Bazel C/C++ 移植到社区拥有和维护的 Starlark 规则是当务之急。这一概念已被证实,大部分风险都得到了消除。

未知:Bazel 的文件系统沙盒影响以及正确性/封闭性的最终结果权衡。Bazel 使用文件系统沙盒和符号链接来实现增量正确性和封闭性保证。众所周知,在典型的构建工作器上,这样的总构建工作负载会增加 10% 的开销,并且会影响构建时长。我们预计,与远程构建执行结合使用可以提高缓存利用率、提前终止构建和浅层构建,不仅能利用封闭性的优势来弥补差异。但是,如果我们发现封闭成本高于预期,并且预期的性能优势未被抵消,那么我们将考虑停用沙盒(即 --spawn_strategy=local)。

未知:Fuchsia 团队将获得的 Bazel 上游所有者提供的支持级别。我们没有理由相信我们不会获得支持,此 RFC 的一个目标是与 Bazel 的所有者建立更牢固的合作伙伴关系。我们与 Bazel 团队的初次会面和互动非常有成效,他们非常乐于助人,并愿意详细了解我们的要求和观察结果。我们向 Bazel 团队提出的问题 可以快速解答我们相信,随着 Fuchsia 继续为 SDK (RFC-139) 采用 Bazel,并且 Fuchsia 证明了他们是 Bazel 的优秀客户,所以 Bazel 团队将继续为您提供支持和参与。

未知:我们将如何使用 Bazel 和 Fuchsia SDK 编译 Rust 代码。 我们发现目前正在积极开发针对 Bazel 的 Rust 支持,不过我们需要对此进行测试,并了解它是否符合 Fuchsia 的需求。我们预计会有后续的 RFC 说明我们支持使用 Bazel 和 Fuchsia SDK 构建 Rust 代码的方法。我们将与 Rust on Fuchsia 团队密切合作。

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

未知:Fucsia 团队正在评估如何支持 Windows 作为开发者托管环境,这将提出一些新要求,以说明 Fuchsia 如何支持基于 SDK 的开发(包括使用 Bazel)。我们尚未满足所有这些要求。不过,我们注意到 Bazel 支持以 Windows 作为开发者主机,因此目前我们没有理由相信 Bazel 无法在 Windows 上运行。如果遇到此类阻碍,我们将与 Bazel 团队保持密切联系。

未知:基础架构中的测试可能通过何种方式由 bazel test 驱动。这不会对此 RFC 造成阻碍,因为我们主要侧重于使用 Bazel 进行编译。

替代方案:继续将当前的 GN/Ninja build 用于 fuchsia.git 中的组件。我们将继续为并行工作提供资金,无法受益于 Bazel 带来的工程生产力,并且对开发者生态系统持续存在同理心差距。此外,平台 build 维护人员指出,继续依赖 GN/Ninja 会产生法律责任,因为这些系统无法保证封闭的整洁 build 或正确的增量 build。

替代方案:重新考虑 RFC-0139 中记录的决定:采用 Bazel,而改为采用不同的构建系统,然后围绕同一构建系统内树内和树外区域保持一致。由于没有理由重新考虑 RFC-0139,我们拒绝此替代方案。

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

替代方案:(已考虑但被拒绝),我们将组件移出树状结构,以使用 SDK 和 Bazel 构建这些组件,而不是为 fuchsia.git 内的代码使用两个构建系统。这会实现相同的长期目标(使用 SDK 和 Bazel 构建更多代码),但我们认为,这种情况将在更长的时间范围内实现。它还会引入多个变量(将代码的 build 迁移到 Bazel + SDK,将代码迁移到其他代码库),我们希望一次只更改一个变量。最好先将组件的 build 更改为 Bazel 和 SDK,然后视需要将该组件的代码移至其他代码库。

早期技术和参考资料

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

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

其他构建系统迁移

紫红 ZN->GN

了解 Fuchsia 执行的另一个构建系统迁移将具有启发性:ZN -> GN 迁移,也称为“构建统一”。

在构建统一之前,Fuchsia 有两个基于 GN/Ninja 的 build,这两个 build 是按顺序调用的。ZN/Ninja build 会先构建一些工件,然后 GN/Ninja build 再构建其他工件。您可以在 GN 中使用 ZN 输出,反之则不行。

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

有人提出了将 GN 工件迁移到 ZN 的计划,但事实证明,此方案不可行。而是将 ZN 伪影移到 GN 中。我们对移动工件的过程进行了逐步验证,方法是生成构建结果的“摘要清单”,并确保没有任何一个迁移步骤会意外更改清单。当 ZN build 变为空白(对上述清单没有贡献)时,这项工作结束,然后被移除。

经验教训:

  1. 在迁移过程中保持重要工作流的连续性。
  2. 使用明确的、有意的协定来分阶段迁移。与 Fuchsia GN 签订的 Fuchsia ZN 合同并非有意签订的合同(Fuchsia 并不只是生成供外部使用或扩展的 ZBI)。此 RFC 提议将软件包和产品组件用作合同,这演示了所学的课程(请参阅 RFC-0072RFC-0095,即将发布的 Fuchsia 平台路线图)。
  3. 在我们制定如何完成迁移的计划之前,不应开始迁移。

Chrome 的 GYP->GN

Fuchsia 团队成员参与的另一个构建系统迁移是 Chrome 从 GYP 迁移到 GN。

Chrome 决定从 GYP 迁移到 GN,因为 GYP build 难以推断和解释,而等效的“gn gen”大约需要一分钟时间。这极大地降低了 Chrome 团队的工作效率,因此需要使用其他构建系统。

迁移最终成功。据我们估计,这项工作在大约 3 年的实际时间里花费了超过 8 人年。

Chrome 团队尝试了一种增量方法,但在 9 个月后决定暂停这项工作。他们发现,构建配置中的阻抗不匹配是增量方法阻碍的主要因素。后来,该团队启动了与 GYP build 并行的“自下而上”GN build。先使用机器人供参考,然后再使用真人机器人。此迁移扩展到了多个目标平台(Linux、Windows、Mac、Android、iOS)。迁移成功的一个关键原因是它有一个坚定的拥护者,他们致力于使迁移成功。

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

  • 由于用例过于宽泛,很难用易于遵循的说明来描述要采取的操作。进行转换的用户必须几乎完全了解新旧构建系统。几乎没有人知道这些知识。
  • 这样的迁移需要大量的工作和工作,并且很难长期维持对工作的贡献。
  • 我们可以通过清晰地传达事件需要发生的顺序,并提供易于访问的跟踪和组织方式,让人们更轻松地帮助完成迁移。
  • 对用户的周到和感同身受是迁移成功的关键。对于此迁移,需要妥善迁移一长串的用户功能和用户工作流。