GN 工具链和 Fuchsia build

GN 工具链概览

借助 GN 构建工具,一次构建可以使用多个 工具链以不同的 方式编译同一目标。

每个 toolchain() 实例对应于:

  • 一个唯一名称,以 GN 标签 的形式表示。

    例如,`//build/toolchain/fuchsia:x64` 命名了 在 //build/toolchain/fuchsia/BUILD.gn 文件中定义的工具链实例,并具有 toolchain("x64") 定义。

  • 一组用于编译源代码和链接二进制文件的命令和构建标志

    例如,使用一个工具链调用 Clang,并使用另一个工具链调用 Microsoft Visual C++,允许单个构建使用这两个编译器套件生成二进制文件。

  • 构建图节点命名空间

    使用不同的工具链实例编译具有相同 GN 路径的目标。这反映在 完全限定的 GN 标签的格式中,该格式类似于 "//<dir>:<target>(<toolchain_dir>:<toolchain_target>)"

    例如,//src/foo:bar(//toolchain:debug) 对应于 bar//src/foo/BUILD.gn 中定义的 bar 目标,当使用 //toolchain:debug 工具链的命令进行编译时。

  • 单独的 GN 执行上下文

    每个工具链实例都会执行自己的 GN 构建配置文件的解析,该文件会为在该工具链中定义目标的所有规则设置全局 变量和默认值。

    实际上,如果使用两个不同的工具链构建同一目标,则相应的 BUILD.gn 文件将被解析两次,但每次都使用 BUILDCONFIG.gn 中定义的不同全局变量、默认配置和自定义模板。

  • 用于目标输出的单独根目录

    在默认工具链中构建的目标放置在 root_build_dir下,而使用 带有//<toolchain_dir>:<toolchain_name>实例构建的目标则放置在 下${root_build_dir}/<toolchain_name>

    此位置在 GN 生成时通过 root_out_dir 变量提供。

始终至少有一个工具链(称为 默认工具链),该工具链 通过从 set_default_toolchain() 构建配置文件调用来确定。

如需了解详情,请参阅 toolchain() 参考文档。

Fuchsia 构建如何使用 GN 工具链

Fuchsia 构建以多种方式使用 GN 工具链:

  • 构建主机和设备可执行文件

    该构建目前定义了 //build/toolchain/fuchsia:x64//build/toolchain/fuchsia:arm64,用于为 64 位 Intel 和 ARM 架构构建 Fuchsia 可执行二进制文件。

    它还定义了 //build/toolchain:host_x64,用于为宿主机(即构建发生的主机)构建代码。

    它还定义了 //build/toolchain:linux_x64//build/toolchain:linux_arm64,用于生成 Linux 64 位代码,即使主机未运行这些架构之一也是如此。

    此外,还有许多专用工具链用于编译引导加载程序和内核的各个部分,稍后会对此进行介绍。

  • 构建 ELF 共享库

    在 Fuchsia 上,进入共享对象(即 GN 中的 shared_library()loadable_module() 实例)的机器代码必须使用 -fPIC 编译器和链接器选项进行构建。

    这与使用 -fPIE 的可执行代码不同。

    为了解决这个问题,我们定义了单独的工具链实例来编译共享库的代码。

    如需了解详情,请参阅 ELF 共享库重定向

  • 构建二进制文件的不同变体(例如插桩或优化)

    Fuchsia 构建支持许多“构建变体”,这些变体允许以略有不同的方式构建机器代码,例如:

    • asanubsan 变体分别用于使用 Clang 的地址清理器和未定义行为清理器构建机器代码。甚至还有一个 asan-ubsan 变体,它将两者结合在一起。

    • coverage 变体用于构建启用了 Clang 基于插桩的分析功能的机器代码,以支持代码覆盖率收集。

    • profile 变体也用于构建插桩代码,但用于支持基于配置文件的优化。

    • thinltolto 变体用于构建启用了 链接时优化的二进制文件。

    • gcc 变体用于使用 GCC 编译器(而不是 Clang)构建 Zircon 内核的某些部分(这有助于消除可能以非常重要的方式影响内核的细微机器代码生成问题)。

    构建的 BUILDCONFIG.gn 文件中还定义了许多其他变体。

  • 生成(或处理)源文件

    在许多地方,构建都需要生成源文件以供其他目标使用。例如,FIDL 协议定义文件经过处理后会生成各种语言(C++、Rust、Go 和 Dart)的绑定,这些绑定稍后会被其他 source_set() 或类似目标使用。

    由于使用这些来源的目标可以在不同的工具链实例中定义,因此确保此生成仅执行一次(而不是每个工具链实例执行一次)非常有用,因为在所有情况下输出都完全相同。

    因此,Fuchsia 构建定义了 fidling 工具链来执行 FIDL 绑定生成。请注意,此工具链仅用于使用 action() 目标运行一些脚本,而从不用于实际编译它们。

    同样,构建中还定义了许多其他“基本”工具链,用于执行不应不必要地重复的处理任务。

Fuchsia 构建如何定义 toolchain() 实例。

Fuchsia 构建提供了这些模板,用于定义具有各种功能的 toolchain() 实例:

  • basic_toolchain() 定义了一个“基本”工具链,即仅执行 copy()action() 目标,并且从不需要使用 GN 对 C++ 和 Rust 编译的内置支持。

    这些用于生成其他几个工具链(例如语言绑定)中的其他目标使用的输出,以避免重复工作。

    请注意,一个基本工具链用于构建 Go 二进制文件,另一个用于构建 Dart 二进制文件,因为 GN 根本不支持这些语言。

  • clang_toolchain() 定义了调用 Clang 编译器的工具链实例。它支持使用 GN 的内置规则构建 C++ 和 Rust 来源。

    支持的目标平台包括 Fuchsia、Linux、Win32 PE/COFF(UEFI 引导加载程序需要)甚至是 WebAssembly!

  • clang_toolchain_suite() 根据当前的构建变体配置定义了一个或多个工具链实例。与直接调用 clang_toolchain() 相比,它更受欢迎,因为这允许构建变体正常运行。

  • clang_host_toolchain_suite() 用于生成宿主机机器代码的工具链。

  • zircon_toolchain() 定义了一个工具链实例,该实例可用于构建 Zircon 内核、引导加载程序甚至 C 库的一部分。 这些二进制文件通常需要非标准编译和链接器命令(例如不同的 ABI 或缺少标准链接环境)。

    注意:`zircon_toolchain()` 创建的工具链不支持 Rust 编程语言。

    此模板的一个显著特点是,它还支持使用 GCC 编译器(而不是 Clang)构建二进制文件。事实证明,这有助于发现内核本质上非常敏感的底层代码生成问题。

    注意:我们没有计划支持使用 GCC 构建平台的其余部分。

  • zircon_toolchain_suite() 用于根据当前的构建变体配置定义一个或多个工具链实例。与直接调用 zircon_toolchain() 相比,它更受欢迎。

请注意,zircon_toolchain()clang_toolchain() 之间的区别主要是历史性的,将来可能会将它们合并到一个通用模板中。