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

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

创建一个工作流,允许用户在 fuchsia.git 之外构建和组装工作站产品。

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

总结

注意:此 RFC 已被 RFC-0220 所取代,后者废弃了工作站产品。保留此 RFC 是为了供后人参考,但我们不再寻求实现。

工作站产品定义为“station_session”“ermine”“终端”和“简单浏览器”,目前是在 Fuchsia 源代码树中构建和组装的。Fuchsia 作为平台和工作站产品没有明确的区别。这就要求平台和产品必须同时构建,以组装最终产品。我们提议打破这种耦合,以便将工作站组件与平台分开构建,并将最终产品组装在 Fuchsia 源代码树之外。此方案并不涉及从紫红色源代码树中移除工作站产品的实现,而是重点关注树外产品配置和组装的依赖问题。这项工作为最终将工作站产品完全移出树状结构奠定了基础,但这超出了本文档的讨论范围。

需要指出的是,终端被视为工作站产品的基本组件,但工作站产品以外的其他产品会使用终端。因此,终端组件的开发工作仍将保留在 fuchsia.git 中,并作为平台的一部分供工作站使用,直到就此组件的未来做出进一步决策为止。

设计初衷

目前,如果不同时构建整个 Fuchsia 平台,您无法在 Fuchsia 上构建和组装产品。我们可以使用 SDK 构建 fuchsia.git 之外的单独组件,但这些工件需要返回到 Fuchsia 全球集成才能组装到最终产品中。我们希望能够提供稳定版本的平台以用于构建产品,而无需访问 Fuchsia Global Integration。

在树外构建产品时,仍有许多未知问题,因此我们先从工作站产品入手。工作站产品不是面向用户的生产级产品,而是供开发者参考,同时也为提前采用平台功能和测试提供了环境,因此在此阶段对运行速度的容忍度较高。Workstation 产品也是一个可供爱好者探索 Fuchsia 的环境;我们无需构建整个平台,因此开发者能够更轻松地使用 Fuchsia。

设计

将工作站移出树外 build 以独立映像汇编工具 RFC-0072 为基础,此方案假定该工具存在。

代码库

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

对于本文档的其余部分,station.git 是指托管 Workstation 产品代码的代码库。

基础架构

此项目的重点是在树外构建和组装工作站产品,因此,在确定如何实现这一目标之前,我们不会将组件的开发从 fuchsia.git 转移到其他平台。由于组件开发仍将在树内进行,因此我们可以依赖现有的测试和构建基础架构来验证工作站产品是否回归。滚动新版本平台、sdk、experience.git 和其他依赖项的过程将通过脚本手动完成,以便团队专注于解决手头问题。当我们开始将组件的开发工作从树外开始迁移时,将需要重新审视持续集成的方式。

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

构建工作站产品而非核心产品可确保我们拥有组装产品所需的所有工件,即使此时我们不进行构建也是如此。我们在树外构建的工件将用于代替随平台一起发布的工件。这样一来,我们还可以使用现有的 Workstation Builder,最大限度地减少需要管理的额外基础架构的数量。

依赖项管理

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

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

目录结构

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

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

构建系统

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

Workstation 团队之所以选择使用 Bazel,而不是 gn/ninja,主要原因在于他们认为 Bazel 成为更有吸引力的构建系统。

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

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

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

  • Fuchsia SDK Bazel SDK 正式版:通过将 Bazel 用于工作站产品,我们得以将更改推送到 Fuchsia Bazel SDK 以供其他项目使用。

  • 单个构建/分析阶段:使用 Fuchsia 的开发者经常抱怨说他们不知道自己的 build 中未包含软件包或工具的原因。这通常源于用户没有在其 gn 参数中添加目标。Bazel 会要求用户明确指定想要构建、运行或测试的内容,从而移除此失败模式。

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

在此阶段,我们需要编写构建规则,以便根据工作站.git buildroot 构建体验代码库。我们将使用 Bazel 的 new_local_repository 功能。使用此规则,我们可以将 Bazel 专用构建规则保留在工作站.git 中,以根据从体验代码库中映射的源代码进行构建。请务必注意,此设置很容易造成破坏,因为 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")

语言支持

工作站组件目前使用 Dart 和 Rust 编写而成,其中 Dart 是目前唯一支持树外开发的语言。Workloads_session 和终端组件使用 Rust 和 Ermine 编写,简单浏览器使用 dart 编写。首先,我们将注意力集中在构建 Dart 组件上,同时继续在树中构建 Rust 组件,并将其作为平台的一部分进行提供。

目前,在树外构建 developers_session 时,有两个阻塞器。第一点是 station_session 使用多个私有 API,这意味着即使我们有语言支持,也无法在树外构建它。第二个阻碍因素是缺少树外 Rust 支持。工作站目前被拆分为多个较小的平台级组件,这消除了对专用接口的依赖,我们正在探索树外 Rust 支持。随着这两个项目的进展,我们将决定工作站_session 的处理结果。如果 Workstation_session 完全从专用接口迁出,但之后不会立即推出 Rust 支持,我们将使用 C++ 重写会话;而如果 Rust 支持在此之前或之后不久才提供,我们将保留使用 Rust 编写的会话。

消毒用品

fuchsia.git 树支持用于本地开发和基础架构构建器中的各种排错程序。在工作站.git 代码库中支持这些系统并不是一个近期目标,因为在证明构建和汇编系统可与代码库中映射的映射配合使用之前,我们不会将源代码移出 fuchsia.git。不过,我们应该阻止将代码移出 fuchsia.git,直到我们获得排错程序支持为止,因为该工具会导致当前设置回归。

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

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

代码覆盖率

我们不打算在项目开始时为 workspace.git 启用代码覆盖率,而且在短期内也不会制定任何计划。造成这种情况的主要原因是,大部分工作站代码都是用 Dart 编写的,而 Dart 目前没有足够的代码覆盖率支持,导致我们必须投入时间启用它。

我们将无法与目前的 c++ 代码编写方式保持一致,因为虽然这种代码确实会呈现增量代码覆盖率,但在 workstation.git 中启用此功能的工作范围过大,目前无法完成。但是,这也是与 Fuchsia 构建团队合作的好机会,可让树外开发者更轻松地使用这些工具。

第三方支持

使用工作站.git 编写的所有源代码都需要共享相同的第三方库。支持此功能的工具已在其他大型代码库中多次解决,因此我们不必从头开始,但它确实代表了我们必须为整个项目执行和支持的工作量。

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

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

二进制文件大小监控

监控二进制文件的大小对于项目的长期运行状况非常重要。Fuchsia 构建系统中有一些工具可以监控每次提交期间生成的二进制文件的大小。为工作站产品再次构建这些系统超出了此项目早期阶段的讨论范围,但我们最终需要添加这些内容。

我们需要调查这些系统与当前 fuchsia.git 构建系统的紧密耦合程度,看看我们是否可以简单地在其当前形式中集成这些系统,或者是否需要采取措施将其分离。如果我们需要努力将工具与构建系统分离开来,我们应专注于使这些工具可重复使用,以便将来扩容到其他产品集成商。

性能测试

目前正在进行一个项目,为工作站产品添加性能监控功能。此项目的开发重点是仅使用公共 SDK 中提供的工具,让树外的用户能够使用监控功能。我们计划在将代码移出 fuchsia.git 的过程中,将这些测试移至 destination.git 代码库。

所有者

与 fuchsia.git 的贡献者集相比,station.git 代码库的初始贡献者集将较少。因此,在此项目的初始阶段,强制执行 OWNERS 文件并不是首要任务,但我们会继续将这些文件添加到相应的目录中,以方便沟通。 我们开始将代码从 fuchsia.git 中移出,届时我们将引入 OWNERS 文件,并执行我们在 fuchsia.git 中执行的相同代码审核规则。

向工作站.git 代码库贡献的内容将受管理 fuchsia.git 贡献内容的同一政策约束,因为它们将托管在同一项目下。

错误跟踪

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

工件流

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

为了允许 station.git 使用预构建的工件,我们需要设置 fuchsia.git 的构建器来创建预构建的工件,以及一个 Autoroller(会将工件提取到工作站.git 中)。我们或许能够扩展当前正在运行的其中一个构建器,但如果可行,我们尚未进行全面调查。

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

Autoroller 每天将运行多次,以检查平台的新版本。运行多次可确保我们能够获取最新版本,而不考虑一天中不同时间可能会出现的不稳定回滚或 build。Autoroller 将轮询平台的最新版本,并拉取这些工件。Roller 还将读取包含所有相关版本的清单文件,并更新这些版本。这样可以确保平台和各种依赖项之间不存在版本偏差。

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

每日构建器将以某种方式上传工件,这样我们就能访问给定版本对应的“产品包”。修复的代码库将包含对平台所有版本的引用,以便 Roller 识别适合 ABI 兼容性的最新 build 或特定 build。我们正在此提案外部探索实现此功能的具体流程。

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

ABI 注意事项

如果将工作站产品与平台分开编译,可能会让我们遇到 ABI 不兼容问题。我们需要确保始终使用不高于或低于 RFC-0002 的兼容期限,即集成工作站产品时所用平台的发布版本的 SDK 发布版本来编译工作站组件。为了确保我们保持这一不变,我们会将 SDK 版本包含在与平台一起发布的元数据中,并且 workstation.git 代码库也将更新为使用此版本的 SDK。

请注意,此 ABI 考量需要扩展到为工作站产品贡献代码的其他花瓣,例如 Flutter 和 Dart runners。为了最大限度地降低因这些花瓣造成 ABI 合规性破坏的风险,我们最初会将其作为平台的一部分进行滚动,这意味着这些花瓣已在 fuchsia 的全球集成中经过验证。

当我们开始将花瓣移出平台表面时,我们需要确保这些花瓣与我们的产品兼容。对于返回到 Fuchsia 全球集成的花瓣,我们可以在构建平台本身时传播版本的元数据。但是,未来我们的某些依赖项不会纳入 Fuchsia 全球集成,这意味着平台更新中将不会包含版本信息。我们需要使用平台版本控制来确保添加的版本正确无误,并且需要设法处理过时的版本。

实现

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

第 1 阶段

使工作站在树外进行构建的初始阶段是设置开发环境。我们将创建 station.git 代码库来托管代码。 此代码库将包含引导脚本,该脚本将允许用户启动并运行。该代码库将使用 Bazel 工作区和 git 子模块来管理体验代码库中的第三方代码、预构建版本和 vendor。

第 2 阶段

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

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

第 3 阶段

过渡的第三个阶段是努力将所有这些部件组合在一起,以组装整个产品。这包括设置构建平台工件所需的所有基础架构,并定期将其上传到 TUF 代码库。此构建器将在 Fuchsia 基础架构中运行。station.git 代码库将使用 Autoroller 发布新版本。Autoroller 将更新 workspace.git 代码库中的清单文件,运行集成测试并提交此更改。这与 fuchsia.git 不同,fuchsia.git 将版本更新提交到集成代码库以协调版本更改。

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

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

性能

此项目应该不会对工作站产品的性能产生任何影响,因为它只是在构建和组装。但是,我们应该开始看到工作站开发者的构建时间明显缩短,因为他们不需要构建平台。

安全注意事项

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

工作站产品仅用作参考产品,但我们希望确保仍然遵循正确的安全协议。当我们接近发布任何类型的版本时,就需要在安全性方面着手,确保以安全合规的方式构建版本。

隐私注意事项

目前,这不会对隐私权造成影响。我们将使用不会收集用户数据的开放式工具。

测试

Workstation 产品有一套测试,会在每次提交时运行。这些测试包括单元测试、集成测试、e2e 测试和性能测试。随着 Workstation 组件的代码移植到 workation.git,这些测试的代码将会移植到 emulator.git 代码库。

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

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

文档

我们只需要向 workspace.git 添加文档,其中说明了如何设置代码库以用于开发。工作站开发仍将保留在树内,因此我们应继续将文档指向该工作流。

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

一种替代方案是仅在 Fuchsia 树内部执行所有这些工作,但使用完全存在于 SDK 中的工具。我们放弃了这种方法,因为它太容易隐藏我们对专用接口的使用。将汇编程序完全移出树后,我们就不得不依赖于 SDK。

关于我们需要向 TUF 代码库上传哪些内容、上传的形式以及通过工件上传的元数据的格式,仍存在一些未知情况。我们认为,随着我们开始逐步完成此流程,我们将确定这些细节,并将根据需要修改此 RFC。