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 中的所有内容。
  • “外部代码”(OOT)是指以 Fuchsia 为目标平台、基于 Fuchsia SDK 构建且不包含平台源代码的代码,并且存储在 fuchsia.git 之外。例如,Intel wlan 驱动程序示例使用 Fuchsia Bazel SDK 在单独的代码库中构建。
  • Bazel - 用于软件开发的开源构建和测试工具。

设计初衷

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

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

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

利益相关方

教员:rlb

Reviewers:

  • 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 - 适用于 Fuchsia 的 Rust
  • brunodalbo - 连接性
  • surajmalhotra - 驱动程序框架
  • amathes - Fuchsia PM Lead
  • cphoenix - 诊断
  • akbiggs - Flutter on Fuchsia

社交

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

设计

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

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

包容性:虽然 Fuchsia 团队已采用 Bazel 并将继续扩大其使用范围,并为用户使用 Bazel 创建了良好的路径,但并非严格要求使用 Bazel 构建 Fuchsia 软件。IDK 必须继续与构建系统无关。

我们还遵循以下原则:构建系统维度可以与源代码库维度相互独立。将构建目标迁移到 Bazel 和 SDK 并不一定需要将代码迁移到其他代码库。

最终,我们希望按多个项目来组织 Fuchsia 开发,其中每个项目都将 Fuchsia SDK 作为输入,并生成一些可组装成 Fuchsia 系统映像的软件包或其他二进制工件作为输出。这些项目可以由 Fuchsia、我们的合作伙伴或第三方开发者拥有。这些项目的大小也可能不尽相同,从仅提供单个二进制文件的小型项目到可能提供 Fuchsia 平台的大部分内容的大型项目(例如 fuchsia.googlesource.com/platform.git)。当我们接近这种情况时,就可以决定如何将代码整理到项目中,以最大限度地提高效率。

本文档介绍了迈向最终目标的重要一步,即将大部分 Fuchsia 开发重构为托管在 Fuchsia SDK 之上。重新托管到 Fuchsia SDK 后,我们将能够更灵活地将开发内容整理到项目中。

实现

现有的采用计划

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

  • 构建和测试一个简单但承载负载的驱动程序,并将其纳入 Workstation
  • 构建和测试一个简单但可供用户访问的组件,并将其纳入 Workstation
  • 构建和测试精选的越来越多的驱动程序
  • 构建和测试示例和其他项目,帮助开发者开始为 Fuchsia 开发
  • 在 Fuchsia 嵌入器上构建和测试 Flutter
  • 构建和测试 Workstation Experiences 代码
  • 推动 Google 产品的组装

其他采用提案

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

除了上述正在实施的“现有采用计划”之外,我们还将在 fuchsia.git 中创建一个 Bazel 构建树,并将其与 GN 构建树放在一起。Bazel build 将能够构建和测试打包的组件以及组装产品。我们将与 Fuchsia Bazel SDK 共享逻辑(Bazel 引导加载程序、Starlark 规则),以便外部开发者使用。

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

替代文本:从 ninja 平台 build 流入 bazel 汇编的 in-tree - 汇编在 bazel 中

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

替代文本:从 ninja 平台 build 将 in-tree 流入 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 与在代码库之间移动组件混为一谈。迁移到 Bazel 的 fuchsia.git 中的组件可以保留在 fuchsia.git 中。此 RFC 不涵盖代码库迁移。不过,在将所有目标组件迁移到 Bazel 和 SDK 之前,我们计划重新审视构建架构,并与 FEC 合作,确定是否有必要将由 Bazel 和 SDK 构建的任何组件移出 fuchsia.git 树。

KPI

我们为此提出了以下 KPI:

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

对于“null build time”(无效构建时间),这是了解启动构建系统本身的延迟时间的一种方法,我们将结合开发者的整体满意度和工作效率来考量这一指标。我们的目标是,除非我们确定 null 构建时间是限制开发者满意度和工作效率的重要因素,否则不允许 null 构建时间出现 50% 的回归。

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

人员配置

关于此计划的人员配置和资金的详细信息尚未确定,不在本 RFC 的讨论范围之内。不过,我们注意到,制定人员配置和资金计划对于将软件成功演变为 SDK 和 Bazel 至关重要。Fuchsia 有两个团队(Bazel SDK 集成团队、Fuchsia Build 团队)最初就已承诺投入资源,与组件团队合作,帮助 Fuchsia 工程师顺利完成此工作。我们预计 Bazel SDK 集成团队和 Fuchsia Build 团队将负责完成首次迁移到 Bazel 和 SDK,然后根据这些经验,我们将为进一步迁移提供明智的人员配置/资金估算。

自动化工具

在将功能领域迁移到 Bazel 方面取得重大进展后,我们将探索如何添加自动化检查,以确保这些领域在未来的发展过程中不会意外配置为使用 GN 进行构建。此阶段会在流程接近尾声或结束时进行,即在我们证明 KPI 目标已达成且没有回归之前。

未来可能考虑的事项

如果有明确的路线图,我们将来可能会将 Fuchsia 的 100% 源代码迁移到 Bazel。在这种可能的未来场景中,我们设想实现纯 Bazel 构建,并弃用用于管理构建的 fxfint 命令,而是完全使用 Bazel 命名法来管理各种事项。

文档

RFC-139 的实施过程中,我们将更新 fuchsia.dev 上的文档,以说明和教导如何将该 SDK 与 Bazel 搭配使用。

我们将更新 fuchsia.dev 上面面向 Fuchsia 贡献者的文档,说明何时以及如何使用 Bazel in-tree。本文档将包含有关何时配置要使用 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 时长。我们预计,通过将密封性与 Remote Build Execution 结合使用,可提供更好的缓存利用率、提前截断和浅层 build,从而完全弥补差距。不过,如果我们发现密封性带来的开销超出预期,并且无法通过预期的性能优势来抵消,那么我们将考虑停用沙盒(即 --spawn_strategy=local)。

未知:Fuchsia 团队将从 Bazel 上游所有者那里获得哪种级别的支持。我们没有理由认为自己不会获得支持,而这份 RFC 的一个目标就是与 Bazel 的所有者建立更紧密的合作伙伴关系。我们与 Bazel 团队的初次会议和互动非常富有成效,他们非常支持我们,也愿意详细了解我们的要求和观察。我们向 Bazel 团队提出的问题会得到快速解答。我们相信,随着 Fuchsia 继续采用 Bazel 构建 SDK (RFC-139),并证明自己是 Bazel 的优秀客户,Bazel 团队将继续为 Fuchsia 提供支持和参与。

未知:我们将如何使用 Bazel 和 Fuchsia SDK 编译 Rust 代码。 我们发现,人们正在积极开发对 Bazel 的 Rust 支持,但我们需要对其进行测试,并探索其是否符合 Fuchsia 的需求。我们预计会发布一项后续 RFC,其中介绍了我们支持使用 Bazel 和 Fuchsia SDK 构建 Rust 代码的方法。这将与 Fuchsia 上的 Rust 团队密切合作。

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

未知:Fuchsia 团队正在评估如何支持 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 支持的其他代码库,而不是在 fuchsia.git 中采用 Bazel 和 SDK。这些代码库之间的集成只能通过预构建内容完成。最终结果是一样的:Fuchsia 团队已将 Bazel 和 SDK 作为面向 Fuchsia 开发的主要方式,并且正在以与 Google 以外的 Fuchsia 开发者相同的方式面向 Fuchsia 进行开发。我们曾考虑过此选项,但有些团队表示希望能够在同一提交中更改可通过 SDK 访问的代码(例如接口)及其组件代码。我们注意到,有些团队已经计划通过将其代码移至 fuchsia.git 之外的代码库来采用 Bazel 和 SDK。

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

在先技术和参考文档

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

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

其他构建系统迁移

Fuchsia 的 ZN->GN

不妨了解一下 Fuchsia 执行的另一项构建系统迁移:ZN 向 GN 迁移,也称为“构建统一”。

在 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 团队成员参与的另一项构建系统迁移是 Chrome 从 GYP 迁移到 GN。

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

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

Chrome 团队尝试过增量方法,但在 9 个月后决定暂停该工作。他们发现,build 配置中的阻抗不匹配是增量方法的主要阻碍因素。后来,该团队开始并行启动“自下而上”GN build 和 GYP build。先与“小贴士”聊天机器人互动,然后再与真人聊天机器人互动。此迁移扩展到了目标平台(Linux、Windows、Mac、Android、iOS)。迁移成功的一个关键原因是,他们拥有一位坚定的支持者,并致力于确保迁移顺利完成。

我们从这次迁移中学到了以下几点:

  • 由于支持请求过于笼统,因此很难以简单易懂的说明来描述具体该怎么做。进行转换的人员必须几乎完全了解旧版和新版构建系统。几乎没有人知道这些知识。
  • 进行此类迁移需要付出大量努力,而且很难长期持续投入。
  • 我们可以明确说明需要执行的操作顺序,并提供易于访问的跟踪和整理功能,让用户更轻松地参与迁移。
  • 对用户给予细心照顾和同理心,对于迁移成功至关重要。在这次迁移中,有大量的用户功能和用户工作流需要妥善迁移。