Bazel 迁移指南

上次更新时间:2025-03-06

本页面为在源代码树中(即使用 fuchsia.git 代码库)工作的 Fuchsia 开发者提供了重要的 Bazel 相关指南。由于 Bazel 迁移是一个不断变化的目标,因此我们会经常更新本页面,以反映重要变更。

摘要

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

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

  • 如果您要定义新的产品主板或输入软件包,请使用 Bazel 目标和专用 Bazel SDK 规则来完成,如此示例所示。

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

  • 所有新的主板开发都应在 Bazel 中进行。如果您的主板恰好依赖于现有的 GN 驱动程序软件包,请先通过在其 GN fuchsia_driver_package() 定义中设置 export_to_bazel = true 实参,将其公开给 Bazel 图,然后创建一个引用 GN 软件包制品 (此处有示例,此处有示例) 的 Bazel fuchsia_prebuilt_package() 同伴目标定义。

  • 基于 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 原子公开的平台内部库,请联系 fuchsia-build-discuss@google.com 来介绍您的使用情形。

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

  • 所有 Bazel 目标都必须仍然由 GN 目标封装,才能对 build 后客户端可见。

    具体而言,一个或多个 Bazel 测试软件包必须由 GN bazel_test_package_group() 目标定义封装,以确保 fx test 和 botanist(我们的基础设施 CI 测试运行程序工具)能够了解这些软件包、按需构建它们并执行它们。

  • 从 GN/Ninja 操作调用 Bazel 的速度非常慢:即使 Bazel 决定不执行任何操作,也需要花费几秒钟的时间。Ninja 会将它们分批处理,并以组的形式运行所有等待中的可运行 Bazel 操作。

    这些操作由 GN 模板(例如 bazel_action 或其封装容器之一)定义。

  • 避免依赖于 Bazel 制品的非终端 GN 目标

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

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

双重 BUILD.bazel 和 BUILD.gn 定义

在迁移的剩余时间里,我们许多目标都将需要在等效的 BUILD.gn 和 BUILD.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 的依赖链会导致增量构建速度明显变慢。此外,由于在查看实际依赖项时不够清晰,开发者体验也会令人沮丧,并且构建失败会变得更难理解和修复。

目前,最小链看起来像 GN -> Bazel -> GN,因为整个 build 由 GN 控制,并且 build 后的客户端仅查看 GN 特定的目标定义,因此任何 Bazel 制品都必须通过 GN 特定的目标进行封装。

构建团队正积极努力,使 Bazel 目标原生可见,以避免出现最后一个 Bazel -> GN 边缘。

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

将平台库公开为 SDK atom

若要使目标在 @fuchsia_sdk 中可见,必须满足以下条件:

  • 其类型必须与我们支持的某个 IDK Atom 架构相匹配,这意味着:

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

  • 由于 GN / Bazel 边界存在限制,它们无法为 testonly = true。

  • 它们不能有条件依赖项。我们的 IDK 架构不支持这些功能,这是有充分理由的。

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

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

    • sdk_source_set():适用于 C++ 源库。
    • sdk_static_library():适用于预构建的静态 C++ 库。
    • sdk_shared_library():适用于预构建的共享 C++ 库。
    • 将 fidl() 设置为 sdk_category:适用于 FIDL 定义文件。
    • zx_library(),并将 sdk_publishable 设置为 SDK 类别:对于还需要链接到内核、引导加载程序或 userboot 程序的 C++ 源代码、静态库和共享库。
    • sdk_fuchsia_package():适用于预构建的 Fuchsia 软件包。

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

文档历史记录

2025-03-06:修复了有关如何将 Bazel 软件包导出到 GN 的示例。 2025-02-21:初始版本