Fuchsia 构建系统:变体

Fuchsia GN 构建机制允许在不同的“变体”中构建单独的组件。 变体通常仅表示使用额外的编译器选项,但如果您编写更多 GN 代码,它们可以执行更多操作。 到目前为止定义的变体支持 SanitizerLTO等功能。

指定变体

变体规范使用 GN 构建实参 select_variant传递给构建。请注意,这些变体选择器的顺序很重要,如下面的语法部分中所述。

使用 fx set,通过 --variant= 标志传递字符串变体选择器:

fx set core.x64 --variant=asan/cat --variant=asan/ledger --variant=host_asan

此示例告知构建系统使用地址 Sanitizer 编译“cat”和“ledger”Fuchsia 二进制文件以及所有主机工具(请参阅下文,了解这些字符串的确切语法)。

如果您有现有的构建目录,则可以通过直接修改 GN 实参来添加或修改变体(根据需要将构建的 GN 输出目录替换为 out/default):

fx gn args out/default

该命令会打开一个编辑器。在该文件中附加一行,将变体选择器作为字符串列表分配给 select_variant 构建实参:

select_variant = [ "asan/cat", "asan/ledger", "host_asan" ]

选择器语法

通常,您将使用一组字符串作为变体选择器。每个字符串都定义一个变体名称,并可以选择定义其适用范围。系统会按照您指定的顺序测试选择器,并应用第一个匹配的选择器。

  • 如需全局应用命名变体,请单独使用变体名称,例如 asanhost_ubsan。这些全局选择器应列在最后。

  • 如需将命名变体应用于特定目标,请使用 variant_name/target_output_name 形式,例如 asan-ubsan/ledgerhost_asan/zxdb_tests。这些选择器应列在全局选择器之前,以替换更通用的规则。

变体与二进制文件(例如 executableloadable_moduletestfuchsia_driver)匹配,而不是与 Fuchsia 软件包、Fuchsia 组件、共享库、静态库或源集匹配。变体与目标匹配后,它所依赖的所有库都将使用该变体进行编译。由于 Fuchsia 软件包和组件与变体选择无关,因此在变体规范中指定软件包或组件名称不会产生任何影响。软件包中的每个可执行文件或模块都可以指定自己的变体。

默认情况下,目标输出名称是您提供给 GN 目标定义的带引号的名称。 此目标定义了 my_program 目标,您可以使用选择器 asan/my_program 将 asan 应用于该目标:

executable("my_program") { ... }

某些目标使用 GN output_name 变量替换输出名称(这通常是为了提供全局唯一的二进制文件名称,以避免冲突)。在这种情况下,变体选择器与替换的输出名称匹配,因此您仍将使用 asan/my_program 将 asan 应用于该目标:

executable("bin") {
  output_name = "my_program"
}

在某些情况下,模板可能会以不明显的方式替换输出名称。如果您发现变体不匹配,一种简单的方法是直接在构建目录中查找二进制文件并使用该名称。

高级选择器

您还可以将 GN“范围”放在大括号中作为变体选择器提供,这样可以完全控制匹配目标的构建方式。这些选择器必须在“gn 实参”中设置,而不是在“fx set”命令行中设置。如需了解详情,请参阅 select_variant构建实参文档。

如需查看可用变体的列表并详细了解如何定义 新变体,请参阅 known_variants 构建实参。

常见变体名称

  • debug:未优化的编译。
  • release:优化的编译。
  • asan地址 Sanitizer,用于在编译时 检查内存误用情况,例如释放后使用和超出范围的数组访问。
  • ubsan未定义行为 Sanitizer,用于在编译时检查 未定义行为,例如整数溢出和指针未对齐。
  • asan-ubsan:asan + ubsan 的组合。
  • lto链接时优化,用于 整个程序优化。
  • thinlto精简链接时优化是一种 轻量级的整个程序优化,可加快编译速度。
  • coverage:插桩编译,用于为 C++ 生成 代码覆盖率信息。
  • coverage-rust:将覆盖率应用于 Rust。由于 Rust 和 C++ 编译器之间的 LLVM 库版本偏差,因此不能与 coverage 同时使用。
  • kasan:仅将 asan 应用于内核。
  • gcc:使用 GCC 而不是 Clang 进行编译。此功能仅支持启动配置,并且仅影响某些目标(包括内核)。

在 Sanitizer 的模糊测试器下运行测试时,会使用 asan-fuzzer 等模糊测试器变体。这些变体不适用于手动选择,请改为按照模糊测试说明设置构建。

此外,还有一些速记选择器可将变体应用于主机二进制文件(在 Linux 或 Mac 主机上运行的工具): * host_asan * host_asan-ubsan * host_coverage * host_coverage-rust * host_profile

某些预构建文件可能不适用于所有变体。特别是对于 ffmpeg,请参阅 //src/media/lib/ffmpeg/BUILD.gn

问题排查备注

检查变体是否已应用于二进制文件

每个变体都有唯一的输出目录和工具链名称,命名为 <architecture>-<variant name>。然后,这些二进制文件会在构建过程中复制到根构建目录。例如,以 x64 设备为目标的 asan-ubsan 变体将使用 //build/toolchain/fuchsia:x64-asan-ubsan 工具链进行编译,并将二进制文件放入 out/default/x64-asan-ubsan(将“default”替换为您的构建目录)。

运行 GN(通常是构建的第一步)后,会生成一个文件 binaries.json,其中包含每个二进制文件的信息。您可以根据 dist 文件名和 label(工具链名称位于括号中)判断用于编译二进制文件的变体。如果您的二进制文件可能会在目标和主机上编译,请注意记录中的 os 字段。以下是使用“asan-ubsan”变体为 x64 编译的 Fuchsia 二进制文件的示例:

  {
    "cpu": "x64",
    "debug": "x64-asan-ubsan/exe.unstripped/blobfs",
    "dist": "x64-asan-ubsan/blobfs",
    "elf_build_id": "x64-asan-ubsan/blobfs.build-id.stamp",
    "label": "//src/storage/blobfs/bin:blobfs(//build/toolchain/fuchsia:x64-asan-ubsan)",
    "os": "fuchsia",
    "type": "executable"
  },

重现 ASan 失败

我们的基础架构在启用 ASan 的配置中运行测试。如需重现启用 ASan 的基础架构构建,请使用 fx repro <build_id> 并运行其发出的命令。

请注意,这会构建基础架构运行的所有测试,并将它们安装在系统映像中。这可能不可取,原因如下:

  • 构建所有测试通常很慢且不必要。开发者可能会发现,将软件包标签限制为他们需要的测试会更有效。
  • 提前在系统映像中安装所有测试意味着软件部署工作流不会得到执行。

从启用 ASan 的二进制文件中启动可执行文件

如果您尝试使用 ASan 变体,可能会遇到如下错误:

launcher: error: Launch: elf_load: handle_interp failed
dlsvc: could not open 'asan/ld.so.1'

Fuchsia 围绕软件包和组件构建。每个组件都包含运行所需的所有共享库。这有助于 Fuchsia 避免困扰其他操作系统的库版本控制问题。这也意味着,如果您想从组件中运行二进制文件,则必须为该二进制文件提供相应的共享库加载程序。

Fuchsia 安装的 /boot/ 目录中有一组命令行程序,这些程序不包含在软件包中,而是包含在启动文件系统中。 这些程序没有自己的共享库加载程序,并且将使用执行它们的组件提供的任何共享库。这通常有效,因为 shls 等程序具有非常少的非常常见的依赖项。但是,无法保证组件的软件包将具有满足命令行程序需求的足够或兼容的共享库。启用 ASan 的软件包通常不包含这些程序的正确启动器,因此大多数启用 ASan 的组件都无法运行 /boot 中的可执行文件。如果启用 ASan 的组件尝试这样做,则会收到上述错误。

幸运的是,事实证明,修复方法涉及执行所有软件包都应执行的操作,即显式声明其依赖项。如果您的软件包依赖于二进制文件,则应将其声明为依赖项,然后使用该声明的依赖项,而不是 /boot 目录中的依赖项。对于我们的构建系统,在 //build/config/fuchsia/zircon_images.gni 中定义的 zircon_extras_manifest 规则将允许您依赖于 /boot 目录中找到的任何二进制文件。它们将安装在 /pkg/bin/ 中,您应从此处执行它们。