Fuchsia 构建系统:变体

Fuchsia GN 构建机器允许在不同的“变体”中构建单独的组件。变体通常只表示使用额外的编译器选项,但如果您编写更多 GN 代码,则具有更多用途。到目前为止,定义的变体支持排错程序LTO

指定变体

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

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

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

此示例指示 build 使用地址排错程序编译“cat”和“ledger” Fuchsia 二进制文件以及所有主机工具(如需了解这些字符串的确切语法,请参阅下文)。

如果您已有 build 目录,则可以通过直接修改 GN 参数来添加或修改变体(如有必要,将 build 的 GN 输出目录替换为 out/default):

fx gn args out/default

该命令将调出编辑器。向该文件附加一行,将变体选择器作为字符串列表分配给 select_variant build 参数:

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 为其应用目标:

executable("my_program") { ... }

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

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

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

高级选择器

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

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

常见变体名称

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

在带有排错程序的模糊测试工具下运行测试时,会使用 asan-fuzzer 等模糊测试工具变体。这些变体不适合手动选择,而是按照模糊测试说明来设置 build。

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

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

问题排查说明

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

每个变体都有一个唯一的输出目录和工具链名称,名为 <architecture>-<variant name>。然后,这些二进制文件会在构建过程中复制到根 build 目录中。例如,以 x64 设备为目标的 asan-ubsan 变体将使用 //build/toolchain/fuchsia:x64-asan-ubsan 工具链进行编译,并将二进制文件放在 out/default/x64-asan-ubsan 中(将 build 目录替换为“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 的基础架构 build,请使用 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/ 中,您应该从那里执行它们。