RFC-0095 - 在树外构建和组装工作站

RFC-0095:在树外构建和组装工作站
状态已接受
区域
  • 常规
说明

创建工作流,使用户能够在 fuchsia.git 之外构建和组装工作站产品。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-03-24
审核日期(年-月-日)2021-05-19

摘要

注意:此 RFC 已被 RFC-0220 取代,后者已弃用工作站产品。此 RFC 将保留以供后人参考,但我们不会再继续实施。

工作站产品(定义为 workstation_session、ermine、终端和简单浏览器)目前在 Fuchsia 源代码树中构建和组装。Fuchsia 平台与工作站产品之间没有明确的区别。这要求平台和产品必须同时构建,才能组装成最终产品。我们建议打破这种耦合,以便工作站组件可以与平台分开构建,并且最终产品可以在 Fuchsia 源代码树之外组装。此提案并非旨在从 Fuchsia 源代码树中移除工作站产品的实现,而是侧重于解决树外产品配置和组装的相关问题。这项工作将为最终完全将工作站产品移出树外奠定必要的基础,但这超出了本文档的范围。

需要注意的是,终端被视为工作站产品的基础组件,但终端也用于工作站产品之外的其他产品。因此,终端组件的开发将继续在 fuchsia.git 中进行,并且在就该组件的未来做出进一步决定之前,该组件将作为平台的一部分被工作站使用。

设计初衷

目前,在 Fuchsia 上构建和组装产品时,必须同时构建整个 Fuchsia 平台。我们可以使用 SDK 在 fuchsia.git 之外构建各个组件,但这些制品需要流回 Fuchsia Global Integration,才能组装成最终产品。我们希望能够提供平台的稳定版本,以便产品基于该版本构建,而无需访问 Fuchsia 全球集成。

在构建树外产品方面,仍有许多未知因素,因此我们希望从 Workstation 产品开始。工作站产品不是生产级、受支持的用户端产品,而是面向开发者的参考产品,也是用于抢先体验平台功能和进行测试的环境,因此在此阶段对速度变慢的容忍度更高。工作站产品还旨在成为发烧友探索 Fuchsia 的环境;通过消除构建整个平台的需求,我们可以让开发者更轻松地使用 Fuchsia。

设计

Standalone Image Assembly Tool RFC-0072 的基础上,将工作站移出树外构建,此提案假设该工具存在。

代码库

工作站代码库的源代码将托管在 git-on-borg 上。将代码托管在 git-on-borg 上,可让我们使用现有的 Fuchsia 基础设施和工具,从而专注于构建和组装产品。

在本文档的其余部分,workstation.git 将指代托管工作站产品代码的代码库。

基础架构

此项目的重点是构建和组装树外的工作站产品,因此在确定如何执行此操作之前,我们不会将组件的开发移出 fuchsia.git。由于组件开发将保留在树内,我们可以依靠现有的测试和构建基础架构来验证工作站产品不会出现回归。平台、SDK、experiences.git 和其他依赖项的新版本将通过脚本手动推出,以便团队专注于当前的问题。当我们开始将组件的开发转移到树外时,需要重新审视持续集成的方式。

平台每日 build 需要自动化,但我们可以使用现有的 Fuchsia 基础架构。需要创建一个每日构建器,用于构建组装工作站产品所需的软件包,并将这些制品上传到某个存储库。此工作流的技术设计仍需确定。在开始处理此问题之前,我们会先咨询基础设施和安全团队。

构建工作站产品(而非核心)将确保我们拥有产品组装所需的所有制品,即使我们目前不构建这些制品也是如此。我们构建的树外制品将取代随平台一起推出的制品。这样做还可以让我们使用现有的工作站构建器,从而最大限度地减少需要管理的额外基础设施。

依赖项管理

工作站代码库将结合使用 Git 子模块和 Bazel 工作区来管理依赖项。Git 子模块将用于拉取外部代码库,而 Bazel 将用于下载构建所需的预构建项和工具链。

Bazel 的工具链将与工作区规则结合使用,以下载相应构建规则所需的预构建。目前的一个限制是,我们的许多预建版本都存储在 CIPD 中,并且需要使用 cipd 命令行工具。初始原型要求 cipd 工具位于开发者的路径中的某个位置。我们正在积极寻找替代方案,并且认为这不会成为长期要求。

目录结构

工作站代码库的目录结构将与 fuchsia.git 目录结构非常相似,以保持 Fuchsia 开发者的一致性。根目录将包含项目所需的各种元数据文件以及以下顶级目录。

  • //src - 用于构建工作站产品的源代码。
  • //tools - 支持 build 的脚本和工具。
  • //src/experiences - 现有的 experiences.git 代码库。
  • //预构建 - 需要的任何预构建。
  • //third_party - 由 src 目录使用的第三方代码。

构建系统

工作站产品将使用 Bazel 构建系统进行构建。使用 Bazel 的决定与使用 gn/ninja 的 fuchsia.git 不同。这一决定意味着,我们将无法利用 Fuchsia 团队获得的专业知识,也无法重复使用现有的 build 规则。不过,当前的 GN SDK 不包含任何用于构建或测试 Flutter 应用的逻辑,而这正是工作站产品的主要部分。无论选择哪种构建系统,团队都需要编写这些规则。树内存在的示例 Bazel SDK 确实具有一些用于构建 Dart 和 Flutter 应用的基本逻辑,我们可以基于此逻辑进行构建。

工作站团队之所以选择使用 Bazel 而不是 gn/ninja,主要是因为他们认为 Bazel 是一种更具吸引力的构建系统。

  • 开源采用:Bazel 不是最受欢迎的构建系统,但其采用率高于 gn/ninja,因此开源社区应该会更熟悉它。

  • 丰富的规则生态系统:Bazel 拥有丰富的现有规则生态系统,有助于促进重用。

  • 依赖项管理:Bazel 可以管理外部依赖项。

  • Fuchsia SDK Bazel SDK 生产化:使用 Bazel 开发工作站产品,让我们有机会将更改推送到可供其他项目使用的 Fuchsia Bazel SDK 中。

  • 单一构建/分析阶段:对于刚接触 Fuchsia 的开发者来说,一个常见的抱怨是他们不知道为什么某个软件包或工具未包含在他们的 build 中。这通常是因为用户未在 gn 实参中包含目标。Bazel 要求用户明确指定要构建、运行或测试的内容,从而消除了这种故障模式。

  • 封闭构建:Bazel 默认具有封闭构建,而 GN 没有。

在此阶段,我们需要编写构建规则,以针对 workstation.git buildroot 构建体验代码库。我们将使用 Bazel 的 new_local_repository 功能。使用此规则可让我们将 Bazel 特定的 build 规则保留在 workstation.git 中,以便针对从体验代码库映射的来源进行 build。请务必注意,此设置容易导致 build 损坏,因为 Bazel build 规则托管在与实际源代码不同的代码库中。这些中断只会影响正在努力实现树外组建的团队,而不会影响任何树内开发者。我们知道这里存在权衡取舍,但我们认为,中断树外开发者的工作流程比中断树内开发者的工作流程更好。树外开发者将负责修复发生的中断。

//WORKSPACE

new_local_repository(
    name = "ermine",
    path = "vendor/experiences/session_shells/ermine/shell/",
    build_file = "src/ermine/BUILD.ermine",
)

//src/ermine/BUILD.ermine

flutter_component(name = "ermine_component", ...)
flutter_package(name = "ermine", deps = ":ermine_component")

语言支持

工作站组件目前是用 Dart 和 Rust 编写的,其中 Dart 是目前唯一支持树外开发的语言。 workstation_session 和终端组件是用 Rust 和 Ermine 编写的,而 simple-browser 是用 Dart 编写的。我们将首先专注于构建 Dart 组件,同时继续在树内构建 Rust 组件并将其作为平台的一部分交付。

目前,在树外构建 workstation_session 存在两个阻碍因素。首先,workstation_session 使用了多个私有 API,这意味着即使我们有语言支持,也无法在树外构建它。第二个阻碍因素是缺少树外 Rust 支持。目前,workstation_session 正在被拆分为更小的平台级组件,从而消除其对私有接口的依赖,并且正在探索树外 Rust 支持。随着这两个项目的进展,我们将决定 workstation_session 的命运。如果工作站会话完全从私有接口迁移,但 Rust 支持不会很快推出,我们将使用 C++ 重写该会话;如果 Rust 支持在迁移之前或之后不久推出,我们将保留使用 Rust 编写的会话。

消毒用品

fuchsia.git 树支持各种清理器,可用于本地开发和基础架构构建器。由于在证明 build 和 assembly 系统可与映射的代码库搭配使用之前,我们不会将源代码移出 fuchsia.git,因此在 workstation.git 代码库中支持这些系统并非当前目标。不过,在获得清理器支持之前,我们应该阻止将代码移出 fuchsia.git,因为这会造成当前设置的回归。

当本地 build 正常运行后,我们将添加对传递 build 时标记的支持,以便在本地启用各种 sanitizer。这样一来,我们就可以针对已映射的来源进行构建,并启用 Sanitizers。当我们开始将代码移出 fuchsia.git 时,我们将设置构建器,以匹配当前向 fuchsia.git 提交代码的体验。

目前,您可以在代码搜索中搜索 Fuchsia 代码。workstation.git 代码库将作为单独的项目添加到同一项目中,以便在线搜索和查看。

代码覆盖率

我们不打算在项目开始时为 workstation.git 启用代码覆盖率,并且近期也没有任何相关计划。主要原因是,大多数工作站代码都是用 Dart 编写的,而 Dart 目前没有足够多的代码覆盖率支持,无法保证我们投入所需的时间来启用它。

我们将无法与当前编写 C++ 代码的体验保持一致,因为当前体验会显示增量代码覆盖率,但在 workstation.git 中启用此功能的任务范围过大,目前无法承担。不过,这确实为我们提供了一个与 Fuchsia build 团队合作的好机会,以便让这些工具更易于供树外开发者使用。

第三方支持

在 workstation.git 中编写的所有源代码都需要共享相同的第三方库。支持此功能的工具已在其他大型代码库中多次解决,因此我们不必从头开始,但确实需要完成大量工作,并为整个项目提供支持。

许可合规性将与 Fuchsia 项目所用的许可保持一致。第三方代码库的合规性将由引入这些依赖项的代码审核流程中的代码库顶级 OWNERS 监控。

我们目前没有在 build 期间和代码检查期间检查许可和生成 NOTICE 文件的明确计划。我们需要研究如何在 fuchsia.git 代码库中完成此操作,并将该逻辑移植到此代码库。

二进制文件大小监控

监控二进制文件的大小对于项目的长期健康发展至关重要。Fuchsia build 系统中存在一些工具,用于监控每次提交期间生成的二进制文件的大小。为工作站产品重新构建这些系统不在该项目的早期阶段范围内,但我们最终需要添加这些系统。

我们需要调查这些系统与当前 fuchsia.git 构建系统的耦合程度,以确定我们是否可以简单地以当前形式与它们集成,或者是否需要进行解耦工作。如果我们需要将工具与 build 系统分离,则应专注于使这些工具可重用,以便将来扩展到其他产品集成商。

性能测试

目前,我们正在开展一个项目,旨在为 Workstation 产品添加性能监控功能。此项目目前正专注于仅使用公共 SDK 中提供的工具,让树外用户也能使用监控功能。随着代码从 fuchsia.git 移出,我们计划将这些测试移入 workstation.git 代码库。

所有者

与 fuchsia.git 的贡献者集相比,workstation.git 代码库的初始贡献者集将很小。因此,在项目的初始阶段,强制执行 OWNERS 文件不是优先事项,但我们会继续将它们添加到相应的目录中,以方便沟通。 随着我们开始将代码移出 fuchsia.git,我们将引入 OWNERS 文件,并强制执行与 fuchsia.git 中相同的代码审核规则。

对 workstation.git 代码库的贡献将受与对 fuchsia.git 的贡献相同的政策约束,因为它们将托管在同一项目下。

bug 跟踪

该代码库将使用现有的 Fuchsia 问题跟踪器来跟踪 bug。使用 Fuchsia 问题跟踪器的原因是,它已设置为跟踪 Fuchsia bug,并且工作站产品的主板支持仍将保留在 fuchsia.git 中。有一个顶级工作站组件可用于一般分类,还有几个现有组件用于跟踪工作站产品的各种功能。

制品的流向

为了在 fuchsia.git 之外组装 Workstation 产品,我们需要提供预构建的 Fuchsia 平台,供 Workstation 产品使用。这意味着我们将有两类制品:构成平台的制品,以及分层叠加在上面以组装最终工作站产品映像的制品。平台映像将提前构建,以供 workstation.git 代码库使用,而工作站组件将在组装最终产品映像时构建。由于平台在不断发展,因此目前尚未明确定义构成平台的制品集,但工作站组件将包括 workstation_session、Ermine 和 simple-browser。需要注意的是,即使是仅支持工作站产品的驱动程序,我们也不会在此提案中提议将任何驱动程序开发移出树外。

为了让 workstation.git 使用预构建的制品,我们需要设置 fuchsia.git 的构建器来创建预构建的制品,并设置一个自动滚动器将制品拉入 workstation.git。我们或许可以延长目前正在运行的某个 build 的运行时间,但尚未完全调查是否可行。

fuchsia.git 构建器每天运行一次,用于构建 Fuchsia 平台。 此构建器的配置方式尚未确定,因为我们需要确切了解构建的内容。构建器会将所有合适的制品上传到自动滚动器可以使用的位置。上传这些制品的确切位置和机制仍有待确定。制品中将包含一个清单文件,其中包含用于创建平台的所有依赖项版本。这包括但不限于 SDK 版本、experiences.git 的版本、Flutter 和 Dart 版本、Fuchsia 工具链版本等。

自动滚动器每天会运行多次,以检查平台是否有新版本。多次运行可确保我们获取最新版本,无论这些版本是否不稳定,或者是否在一天中的不同时间发布。自动滚动器将轮询平台的最新版本并提取这些制品。滚动更新器还会读取包含所有依赖版本的清单文件,并更新这些版本。这样可确保平台与各种依赖项之间不会出现版本偏差。

所需的数据主要是包含文件系统分区的文件,例如 fuchsia.zbi 和 fvm.sparse.blk。这些文件包含启动所需的所有数据,以及“base”和“cache”列表中的所有软件包。这些文件将收集到 .far“产品软件包”中,并且此软件包的网址中找到的元数据将包含构建具有匹配 SDK 的组件软件包所需的所有元数据。通过修改“产品软件包”中包含的系统分区,可将基于此元数据构建的软件包附加到基本软件包和缓存中。目前正在并行开发独立的树外映像组装器,因此“产品软件包”中捆绑的具体制品列表可能会发生变化,具体取决于新工具的 API。

每日构建器将以允许我们按给定版本访问“产品软件包”的方式上传制品。固定代码库将包含对平台所有版本的引用,以便滚动者识别适合 ABI 兼容性的最新 build 或特定 build。目前正在本提案之外探索实现此功能的具体流程。

在组装最终产品时,构建系统应先下载由网址标识的“产品软件包”。然后,它会下载匹配的 SDK。(SDK 也可以捆绑在“产品软件包”中)。它应针对该 SDK 构建软件包,为每个软件包创建一个 meta.far 文件,并将所有必需的 blob 收集到一个位置。然后,汇编工具会将这些 blob 和软件包附加到“产品软件包”,从而生成完整的产品 build。生成的制品还应包含生成 OTA 软件包所需的所有必要信息。

ABI 考虑事项

将工作站产品与平台分开编译会使我们面临潜在的 ABI 不兼容问题。我们需要确保始终使用 SDK 发布版本(该版本等于或低于,但在 RFC-0002 中规定的兼容性窗口内)来编译工作站组件,该 SDK 发布版本与工作站产品组装时所用的平台发布版本相同。为了确保我们保持这种不变性,我们将 SDK 版本纳入随平台一起推出的元数据中,并且将更新 workstation.git 仓库以使用此版本的 SDK。

需要注意的是,此 ABI 考虑因素需要扩展到为工作站产品贡献代码的其他花瓣,例如 Flutter 和 Dart runner。为了最大限度地降低这些花瓣带来的 ABI 破坏风险,我们将首先将它们作为平台的一部分进行发布,这意味着它们已在 Fuchsia 的全局集成中得到验证。

随着我们开始将花瓣移出平台表面,我们需要确保它们与我们的产品兼容。对于重新进入 Fuchsia 全局集成中的花瓣,我们可以在构建平台本身时传播有关版本的元数据。不过,在未来,我们的一些依赖项不会进入 Fuchsia 全球集成,这意味着我们不会在平台滚动更新中包含版本信息。我们需要使用平台版本控制来确保包含正确的版本,并且需要找到一种处理过时版本的方法。

实现

从 fuchsia.git 构建和组装工作站产品的过程将分多个阶段进行,每个阶段都基于前一个阶段。由于支持树外开发的系统组件目前正在开发中,因此必须采用这种分阶段的方法。

第 1 阶段

让工作站构建树外源代码的初始阶段是设置开发环境。我们将创建 workstation.git 代码库来托管代码。 此代码库将包含一个引导脚本,可让用户快速上手。该代码库将使用 Bazel 工作区和 Git 子模块来管理第三方代码、预构建项和供应商提供的体验代码库。

第 2 阶段

第二阶段将重点介绍如何设置构建系统以构建 Flutter 组件。我们将使用 fuchsia.git 中现有的 Bazel 规则作为编写 build 规则的起点。最终状态是,我们可以成功构建一个可在已运行的设备上启动的 Flutter 组件。

在设置用于构建 Flutter 组件的规则的同时,我们将探索如何使用产品组装工具进行树外组装。此过程将手动完成,只需将 fuchsia.git 中构建的所有制品复制到 workstation.git 代码库的 out 目录中,然后尝试创建可启动的映像。最终状态是,我们可以使用在树外组装但在树内构建的映像来配置设备。

第 3 阶段

此过渡的第三阶段是我们努力将所有这些部分整合在一起,以组装整个产品。这包括设置构建平台制品并按计划将其上传到 TUF 代码库所需的所有基础设施。此构建器将在 Fuchsia 基础架构中运行。workstation.git 代码库将通过自动滚动器推出新版本。自动滚动器将更新工作站的 manifest 文件,运行集成测试并提交此更改。这与 fuchsia.git 不同,后者会将版本更新提交到集成代码库,以协调版本更改。

构建和组装的工作流程如下:

  1. 通过更新标识平台版本和所有依赖版本的清单条目来推出新版平台。需要设计如何读取和识别版本。
  2. 使用 Bazel 和 git 下载构建树内组件所需的相应预构建项和第三方代码库。
  3. 使用平台下载工具从 TUF 代码库下载所有平台制品。
  4. 构建树内组件。
  5. 将构建的组件和预构建的组件组装到最终映像中。

性能

此项目不应影响工作站产品的性能,因为它仅用于构建和组装。不过,由于工作站开发者无需构建平台,因此我们应该会看到工作站开发者的 build 时间显著缩短。

安全注意事项

我们正在重复使用 fuchsia.git 使用的许多相同的基础架构,但我们需要确保以安全的方式构建和发布工作站。我们将与安全和基础设施团队合作,创建安全的发布流程和构建流水线。

工作站产品仅用作参考产品,但我们仍需确保遵循适当的安全协议。一旦我们接近发布任何类型的版本,就需要与安全团队合作,确保我们以符合安全要求的方式构建版本。

隐私注意事项

目前无需考虑隐私方面的影响。我们将使用不收集用户数据的开放式工具。

测试

工作站产品有一套在每次提交时运行的测试。这些测试包括单元测试、集成测试、端到端测试和性能测试。这些测试的代码将移植到 workstation.git 代码库,就像工作站组件的代码移植到 worksation.git 一样。

这些测试的逻辑位于 experiences.git 中,因此我们能够运行这些测试,因为在实际移动代码之前,我们会将此代码库映射到 workstation.git 中。所有这些测试都使用公共 SDK 中提供的代码和工具,因此我们可以在树外运行它们,但需要更新我们的 build 系统,以确保它们可以在 fuchsia.git 之外构建和运行。

测试将在每次提交到 workstation.git 代码库时运行,以防止出现回归。这需要与基础架构团队协作,以确定如何在 CQ 中构建和运行这些测试。当前的基础架构假定 fuchsia.git 存在,因此我们需要开发一种与 build 系统和代码库布局无关的方法。

文档

我们只需要向 workstation.git 添加文档,说明如何设置代码库以进行开发。工作站开发将继续在树内进行,因此我们应继续将文档指向该工作流。

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

一种替代方案是在 Fuchsia 树中完成所有这些工作,但使用完全位于 SDK 中的工具。我们之所以不再采用这种方法,是因为它太容易隐藏我们对私有接口的使用。将程序集完全移出树外会迫使我们仅依赖 SDK。

我们仍不清楚需要将哪些内容上传到 TUF 代码库,上传内容的格式以及与工件一起上传的元数据的格式。我们认为,随着流程的推进,这些具体细节会逐渐明确,届时我们会根据需要修改此 RFC。