GN 工具链和 Fuchsia build

GN 工具链概览

GN 构建工具允许一个 build 使用多个工具链以不同方式编译同一目标。

每个 toolchain() 实例对应于:

  • 唯一名称,表示为 GN 标签

    例如,“//build/toolchain/fuchsia:x64”使用 toolchain("x64") 定义为 //build/toolchain/fuchsia/BUILD.gn 文件中定义的工具链实例命名。

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

    例如,使用一个工具链调用 Clang,使用另一个工具链调用 Microsoft Visual C++,可让单个 build 使用两个编译器套件生成二进制文件。

  • build 图节点命名空间

    使用相同的 GN 路径分隔目标,但使用不同的工具链实例进行编译。完全限定 GN 标签的格式(类似于 "//<dir>:<target>(<toolchain_dir>:<toolchain_target>)")会反映这一点。

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

  • 单独的 GN 执行上下文

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

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

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

    虽然默认工具链中构建的目标放在 root_build_dir 下,但使用 //<toolchain_dir>:<toolchain_name> 实例构建的目标会放在 ${root_build_dir}/<toolchain_name> 下。

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

始终会有至少一种工具链称为“默认工具链”,它通过从 buildconfig 文件调用 set_default_toolchain() 来确定。

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

Fuchsia 构建如何使用 GN 工具链

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

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

    该 build 目前定义了 //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 上,必须使用 -fPIC 编译器和链接器选项构建进入共享对象的机器代码(即 GN Speak 中的 shared_library()loadable_module() 实例)。

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

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

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

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

    Fuchsia build 支持许多“build 变体”,可让您以略微不同的方式构建机器代码,例如:

    • asanubsan 变体分别用于通过 Clang 的 Address Sanitizer 和 Undefined Behaviour Sanitizer 和 Undefined Behaviour Sanitizer 构建机器代码。甚至还有一个结合了这两者的 asan-ubsan 变体。

    • coverage 变体用于在启用 Clang 的插桩性能分析的情况下构建机器代码,以支持代码覆盖率收集。

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

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

    • gcc 变体用于使用 GCC 编译器(而非 Clang)来构建 Zircon 内核的某些部分(这有助于消除微小的机器代码生成问题,这些问题可能会对内核产生非常重要的影响)。

    此外,build 的 BUILDCONFIG.gn 文件中还定义了许多其他变体。

  • 生成(或处理)源文件

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

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

    因此,Fuchsia build 定义了 fidling 工具链以生成 FIDL 绑定。请注意,此工具链仅用于使用 action() 目标运行少数脚本,绝不会用于实际编译这些脚本。

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

Fuchsia build 如何定义 toolchain() 实例。

Fuchsia build 提供了以下模板,用于定义具有各种功能的 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() 根据当前的 build 变体配置定义了一个或多个工具链实例。此方法优于直接调用 clang_toolchain(),因为只有这样,build 变体才能正常运行。

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

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

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

    此模板的一个值得注意的功能是,它还支持使用 GCC 编译器(而不是 Clang)来构建二进制文件。事实证明,这对于发现内核对其非常敏感的低级别代码生成问题非常有用。

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

  • zircon_toolchain_suite() 用于根据当前的 build 变体配置定义一个或多个工具链实例。此方法优于直接调用 zircon_toolchain()

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