Bazel 迁移指南

上次更新时间:2025 年 2 月 21 日

本页面面向在树内(即使用 fuchsia.git 检出)工作的 Fuchsia 开发者提供了与 Bazel 相关的重要指南。由于 Bazel 迁移是一个动态目标,因此本页面将经常更新,以反映重要更改。

摘要

自 2025 年第 1 季度起,请遵循以下准则:

  • 如果您不执行产品组装,也不编写驱动程序软件包,请勿担心或开始在 Fuchsia 树中编写 Bazel 文件

  • 如果您要定义新的 product board 或输入 bundle,请使用 Bazel 目标和专用 Bazel SDK 规则进行定义,如此示例所示。

  • 如果您编写的 Bazel 驱动程序软件包仅供 Bazel 定义的开发板使用,则应仅在 Bazel 中使用专用 Bazel SDK 规则定义它们,如此另一个示例所示。

  • 所有新的开发板开发都应在 Bazel 中进行。如果开发板恰好依赖于现有的 GN 驱动程序软件包,请使用 bazel_driver_package() 模板将其公开到 Bazel 图(此处有示例)。

  • 基于 Bazel 的驱动程序软件包只能依赖于通过 @fuchsia_sdk@internal_sdk 代码库公开的平台库。

    @fuchsia_sdk 包含一组库,这些库分发为 OOT,但确实包含供驱动程序开发者使用的“不稳定”平台库(在上一个链接的各行中查找 (unstable) 标记)。

    例如,@fuchsia_sdk//pkg:driver_power_cpp 会从 GN //sdk/lib/driver/power/cpp 库定义中公开库。

    @internal_sdk 包含类似的定义,但仅适用于仅在树内 Fuchsia build 中可用的 SDK atom,因此可能更加不稳定。不过,我们计划移除该版本,改为将所有不稳定的原子移至 @fuchsia_sdk,因此请勿在此版本中添加原子

  • 请勿为内部平台库编写 BUILD.bazel 文件

    目前,应避免为同一非 SDK 库复制 BUILD.gn / BUILD.bazel 定义。

    如果您的驱动程序需要使用尚未作为不稳定的 IDK atom 公开的平台内部库,请与 fuchsia-build-discuss@google.com 联系以说明您的用例。

    理想情况下,添加新的不稳定 IDK atom 应该就足够了,但也可能存在例外情况,如下文的专门部分所述。

  • 所有 Bazel 目标仍必须由 GN 目标封装,才能对构建后客户可见。

    具体而言,一个或多个 Bazel 测试软件包必须由 GN bazel_test_package_group() 目标定义封装,以确保 fx testbotanist(我们的基础架构 CI 测试运行器工具)可以了解它们、按需构建它们并执行它们。

  • 从 GN/Ninja 操作调用 Bazel 的速度非常慢即使 Bazel 决定不执行任何操作,也需要几秒钟的时间。一次只能运行一项。

    这些操作由 GN 模板(例如 bazel_action 或其封装容器之一)定义,其实例使用 //build/bazel:bazel_action_allowlist 目标进行控制。

    bazel_build_group()bazel_test_package_group() 等模板只会调用一次 Bazel,以便一起构建多个 Bazel 目标。这样可以实现更好的并行性。

  • 避免使用依赖于 Bazel 工件的非终端 GN 目标

    由于跨越 GN/Bazel 边界成本高昂,因此类似于 GN -> Bazel -> GN -> Bazel -> GN 的依赖项链会导致增量 build 速度明显变慢,因此必须避免。

    如需了解详情,请参阅下文中的专门部分

双重 BUILD.bazelBUILD.gn 定义

在迁移的其余部分中,我们的许多目标都不可避免地需要在等效的 BUILD.gnBUILD.bazel 文件中进行双重定义。

至关重要的是,此类双重 build 文件(如果存在)应随时间同步。跟踪这两个图表中存在的双重定义目标也很重要。

一种初始且简单的方法是手动编写这些文件,并在两个文件中使用匹配的 LINT If-This-Then-That 块,以便在 CL 审核期间检测偏差。

更好的方法是使用最近推出的 bazel2gn 等工具自动将 BUILD.bazel 转换为等效的 BUILD.gn

目前,bazel2gn 只是一个原型,只能处理 Go 目标。该工具将随着时间的推移不断改进,以支持更多用例,但 Fuchsia 开发者尚不应使用该工具,其使用仅限于 Fuchsia Build 团队。

在这两种情况下,我们都会引入注释标记和相关工具,以跟踪哪些目标在两个图中都进行了双重定义。

GN 图和 Bazel 图之间的依赖项

由于跨越 GN/Bazel 边界的成本很高,因此类似 GN -> Bazel -> GN -> Bazel -> GN 的依赖项链会导致增量 build 速度明显变慢。由于在查看实际依赖项时缺乏清晰性,开发者体验也令人沮丧,并且构建失败可能变得更难理解和更难修复。

目前,最小链条如下所示:GN -> Bazel -> GN,因为整个 build 由 GN 控制,而 build 后客户端只能查看 GN 专用目标定义,因此必须通过 GN 专用目标封装任何 Bazel 工件。

构建团队正在积极努力,让 Bazel 目标能够原生显示,以避免出现最后一个 Bazel -> GN 边缘。

这需要修改构建后工具和脚本(例如 fx test 和许多其他工具),以便直接查看 Bazel 输出,并能够根据需要重新构建这些输出,而无需调用 Ninja。

将平台库公开为 SDK Atom

若要让目标在 @fuchsia_sdk 中显示,必须满足以下条件:

  • 其类型必须与我们支持的 IDK 原子架构之一相匹配,这意味着:

    • 无 Rust 源代码库。
    • 没有 Go 源代码库。
  • 它们必须在 GN 图中定义(目前不支持在 Bazel 中定义 SDK/IDK 原子)。

  • 由于 GN / Bazel 边界存在限制,因此它们不能是 testonly = true

  • 它们不能具有条件依赖项。我们的 IDK 架构不支持它们,原因很充分。

  • 源代码库不能依赖于非 SDK 库。所有传递依赖项也必须是 SDK 的一部分

  • 它们必须使用与 SDK 兼容的 GN 模板进行定义,例如:

如果您的平台库无法遵循这些限制,请与 fuchsia-build-discuss@google.com 邮寄名单联系,讨论替代方案。替代方案可能包括双图表目标定义,需要在迁移的其余部分中进行跟踪。

文档历史记录

2025 年 2 月 21 日:初始版本