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

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

创建一个工作流,让用户能够在 fuchsia.git 之外构建和组装 Workstation 产品。

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

摘要

注意:此 RFC 已被 RFC-0220 取代,后者废弃了工作站产品。我们保留此 RFC 是为了留作历史参考,但不会再继续推进其实现。

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

请务必注意,终端被视为 Workstation 产品的基本组件,但 Workstation 产品以外的其他产品也会使用该终端。因此,在我们就此组件的未来做出进一步决定之前,终端组件的开发将继续在 fuchsia.git 中进行,并将作为平台的一部分由 Workstation 使用。

设计初衷

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

在构建非树型产品方面,我们仍有许多未知之处,因此我们希望先从 Workstation 产品入手。Workstation 产品不是面向用户且受支持的正式版产品,而是面向开发者的参考文档,也是用于尽早采用平台功能和进行测试的环境,因此在此阶段对运行缓慢的容忍度较高。工作站产品还旨在为爱好者提供探索 Fuchsia 的环境;通过消除构建整个平台的需要,我们可以让开发者更轻松地使用 Fuchsia。

设计

将 Workstation 移出树外 build 基于独立映像汇编工具 RFC-0072,因此此提案假定此工具存在。

代码库

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

在本文档的其余部分中,workstation.git 将是指托管 Workstation 产品代码的代码库。

基础架构

此项目的重点是构建和组装树外 Workstation 产品,因此在我们确定如何执行此操作之前,不会将组件的开发工作移出 fuchsia.git。由于组件开发将保留在树中,因此我们可以依赖现有的测试和构建基础架构来验证 Workstation 产品不会出现回归问题。我们将使用脚本手动进行平台、sdk、experiences.git 和其他依赖项的新版本发布流程,以便团队专注于手头的问题。当我们开始将组件的开发转移到外部树时,需要重新考虑如何进行持续集成。

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

构建 Workstation 产品(而不是核心产品)可确保我们拥有产品组装所需的所有工件,即使我们目前不构建这些工件也是如此。我们在树外构建的工件将取代随平台一起发布的工件。这样一来,我们还可以使用现有的 Workstation 构建器,从而最大限度地减少需要管理的额外基础架构。

依赖项管理

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

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

目录结构

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

  • //src - 用于构建 Workstation 产品的源代码。
  • //tools - scripts and tooling to support the build.
  • //src/experiences - 现有的 experiences.git 代码库。
  • //prebuilt - any prebuilts that are needed.
  • //third_party - 由 src 目录使用的第三方代码。

构建系统

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

工作站团队之所以选择使用 Bazel 而非 gn/ninja,主要有几个原因,他们认为这些原因使 Bazel 成为更具吸引力的构建系统。

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

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

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

  • Fuchsia SDK Bazel SDK 正式版:通过为 Workstation 产品使用 Bazel,我们有机会将更改推送到 Fuchsia Bazel SDK,供其他项目使用。

  • 单个构建/分析阶段:刚开始使用 Fuchsia 的开发者常常会抱怨,不知道为什么某个软件包或工具未包含在其 build 中。这通常是因为用户未在 gn 参数中添加目标。Bazel 通过要求用户明确指定要构建、运行或测试的内容,来消除这种失败模式。

  • 封闭式 build:Bazel 默认具有封闭式 build,而 gn 不具有。

在此阶段,我们需要编写构建规则,以便根据 workstation.git buildroot 构建体验代码库。我们将使用 Bazel 的 new_local_repository 功能。使用此规则,我们可以在 workstation.git 中保留特定于 Bazel 的构建规则,以便根据从体验代码库映射的源代码进行构建。请务必注意,由于 Bazel 构建规则托管在与实际源代码分离的代码库中,因此这种设置很容易导致构建中断。这些破坏只会影响致力于获取树外汇编的团队,而不会影响任何树内开发者。我们知道这其中存在权衡,但认为干扰外部开发者的工作流程,而不是干扰内部开发者的工作流程,是更好的选择。外部开发者将负责在出现问题时进行修复。

//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")

语言支持

Workstation 组件目前使用 Dart 和 Rust 编写,其中 Dart 是目前唯一支持的树外开发语言。workstation_session 和 terminal 组件是用 Rust 和 Ermine 编写的,而 simple-browser 是用 dart 编写的。首先,我们将重点构建 Dart 组件,同时继续在树中构建 Rust 组件,并将其作为平台的一部分提供。

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

消毒用品

fuchsia.git 树支持各种清理程序,可用于本地开发和基础架构构建器。在 workstation.git 代码库中支持这些系统并不是我们的近期目标,因为在证明构建和汇编系统可与映射的代码库搭配使用之前,我们不会将源代码从 fuchsia.git 中移出。不过,在获得 sanitizer 支持之前,我们应阻止从 fuchsia.git 中移出代码,因为这会导致当前设置出现回归问题。

在本地 build 正常运行后,我们将添加对传递构建时标志的支持,以便在本地启用各种 sanitizer。这样,我们就可以针对启用了清理程序映射的源代码进行构建。当我们开始将代码从 fuchsia.git 中移出时,我们将设置构建器,使其与当前提交到 fuchsia.git 的体验一致。

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

代码覆盖率

我们不打算在项目之初为 workstation.git 启用代码覆盖率,并且近期也没有任何计划。造成这种情况的主要原因是,大多数 Workstation 代码都是使用 Dart 编写的,而 Dart 目前的代码覆盖率支持不足,因此我们不确定是否值得花时间来实现该功能。

我们将无法再提供与编写 C++ 代码的当前体验相同的体验,因为它确实会显示增量代码覆盖率,但在工作站中实现此功能的工作量太大,目前无法承担。不过,这确实提供了一个与 Fuchsia build 团队合作的绝佳机会,以便让这些工具更容易供树外开发者使用。

第三方支持

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

许可合规性将与 Fuchsia 项目使用的许可一致。在引入这些依赖项的代码审核流程中,代码库的顶级所有者将负责监控 third_party 代码库的合规性。

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

二进制文件大小监控

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

我们需要调查这些系统与当前 fuchsia.git build 系统之间的耦合程度,看看我们能否直接以其当前形式与它们集成,还是需要执行一些工作来解耦它们。如果我们需要努力将工具与构建系统分离,则应着重于使这些工具可重复使用,以便将来扩展到其他产品集成商。

性能测试

目前,我们正在开展一项项目,旨在为 Workstation 产品添加性能监控功能。该项目正在推进中,其重点是仅使用公开 SDK 中提供的工具,让非树内用户也能使用监控功能。随着代码从 fuchsia.git 中移出,我们计划将这些测试移至 workstation.git 代码库。

所有者

与 fuchsia.git 代码库的贡献者相比,workstation.git 代码库的初始贡献者数量将会较少。因此,在该项目的初始阶段,强制执行 OWNERS 文件不会是优先事项,但我们会继续将其添加到适当的目录中,以便于进行通信。随着我们开始将代码从 fuchsia.git 中移出,我们将引入 OWNERS 文件,并对代码审核强制执行与 fuchsia.git 中相同的规则。

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

bug 跟踪

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

工件流

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

为了允许 workstation.git 使用预构建工件,我们需要设置 fuchsia.git 的构建器来创建预构建工件,以及一个将工件拉取到 workstation.git 的自动滚动器。我们或许能够延长目前正在运行的某个构建器的有效期,但尚未完全调查是否可行。

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

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

所需数据大多是包含文件系统分区的文件,例如 fuchsia.zbi 和 fvm.sparse.blk。这些文件包含启动所需的所有数据,以及“base”和“cache”列表中的所有软件包。这些文件将收集到 .far“产品软件包”中,并且在此软件包的网址中找到的元数据将包含使用匹配的 SDK 构建组件软件包所需的所有元数据。可以通过修改“产品软件包”中包含的系统分区,将针对此元数据构建的软件包附加到基础软件包和缓存中。我们还并行进行创建独立的非树型映像汇编器的工作,因此“产品软件包”中捆绑的特定工件列表可能会因新工具的 API 而异。

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

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

ABI 注意事项

将 Workstation 产品与平台分开编译会导致潜在的 ABI 不兼容问题。我们需要确保始终使用与 Workstation 产品所使用的平台的发布版本相同或更低(但在 RFC-0002 的兼容性时间范围内)的 SDK 版本来编译 Workstation 组件。为了确保保持这一不变性,我们将在与平台一起发布的元数据中添加 SDK 版本,并且 workstation.git 代码库将更新为使用此版本的 SDK。

请注意,此 ABI 注意事项需要扩展到向 Workstation 产品贡献代码的其他花瓣,例如 Flutter 和 Dart 运行程序。为了最大限度地降低这些花瓣导致 ABI 损坏的风险,我们将在平台中初步发布这些花瓣,这意味着它们已在 Fuchsia 的全球集成中得到验证。

随着我们开始将花瓣移出平台 Surface,我们需要确保它们与我们的产品兼容。对于返回 Fuchsia 全球集成的花瓣,我们可以在构建平台本身时传播版本的元数据。不过,未来我们可能会遇到一些依赖项无法纳入 Fuchsia 全球集成的情况,这意味着我们将不会在平台版本中包含版本信息。我们需要使用平台版本控制来确保包含正确的版本,并且需要想出一种处理过时版本的方法。

实现

从 fuchsia.git 构建和组装 Workstation 产品的过程将分为几个阶段,每个阶段都基于前一个阶段。由于支持外部开发的系统部分目前正在开发中,因此必须采用分阶段方法。

第 1 阶段

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

第 2 阶段

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

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

第 3 阶段

转换的第三个阶段是将所有这些部分整合在一起,组成整个产品。这包括设置构建平台工件所需的所有基础架构,并按计划将其上传到 TUF 代码库。此构建器将在 Fuchsia 基础架构中运行。workstation.git 代码库将使用自动滚动器发布新版本。autoroller 会更新 workstation.git 代码库中的清单文件,运行集成测试并提交此更改。这与 fuchsia.git 不同,后者会将版本更新提交到集成代码库以协调版本变更。

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

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

性能

此项目仅用于构建和组装,因此不会对 Workstation 产品的性能产生任何影响。不过,由于 Workstation 开发者无需构建平台,因此他们的构建时间应该会开始显著缩短。

安全注意事项

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

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

隐私注意事项

目前,此问题对隐私没有影响。我们将使用不会收集用户数据的开源工具。

测试

Workstation 产品包含一组在每次提交时运行的测试。这些测试包括单元测试、集成测试、端到端测试和性能测试。随着 Workstation 组件的代码移植到 worksation.git,这些测试的代码也将移植到 workstation.git 代码库。

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

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

文档

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

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

一种替代方案是在 Fuchsia 树中完成所有这些工作,但使用完全在 SDK 中存在的工具。我们之所以放弃这种方法,是因为它太容易隐藏私有接口的使用情况。将汇编完全移出树外会迫使我们仅依赖于 SDK。

我们仍不确定需要将哪些内容上传到 TUF 代码库、上传内容的形式以及随工件上传的元数据的格式。我们认为,这些具体信息将在我们开始处理该流程时确定,并且我们会根据需要修改此 RFC。