GN 工具链和 Fuchsia build

GN 工具链概览

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

每个 toolchain() 实例对应于:

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

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

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

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

  • build 图节点命名空间

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

    例如,在使用 //toolchain:debug 工具链的命令编译时,//src/foo:bar(//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 生成时通过 root_out_dir 变量获取。

始终有至少一个名为“默认工具链”的工具链,该工具链是通过从 buildconfig 文件调用 set_default_toolchain() 来确定的。

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

Fuchsia build 如何使用 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 Talk 中的 shared_library()loadable_module() 实例)中的机器代码。

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

    为了处理这种情况,需要定义单独的工具链实例,以便为共享库编译代码。

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

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

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

    • asanubsan 变体分别用于使用 Clang 的 Address Sanitizer 和 Unundefined Behaviour Sanitizer 构建机器代码。甚至有一种结合了这两种方式的 asan-ubsan 变体。

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

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

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

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

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

  • 生成(或处理)源文件

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

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

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

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

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

Fuchsia build 提供以下模板,用于定义具有各种功能的 toolchain() 实例:

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

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

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

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

    支持的目标平台包括 Fuchsia、Linux、MacOS、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() 之间的区别主要是历史的,将来可能会合并到一个通用模板中。