GN toolchains and the Fuchsia Build

Overview of GN toolchains

The GN build tool allows one build to compile the same target in different ways, using multiple toolchains.

Each toolchain() instance corresponds to:

  • A unique name, expressed as a GN label.

    For example '//build/toolchain/fuchsia:x64' names the toolchain instance defined in the //build/toolchain/fuchsia/BUILD.gn file, with a toolchain("x64") definition.

  • A set of commands and build flags used to compile the source code and link binaries.

    For example, using one toolchain to invoke Clang, and another one to invoke Microsoft Visual C++, allows a single build to generate binaries using both compiler suites.

  • A build graph node namespace.

    Separating targets with the same GN path, but compiled with different toolchain instances. This is reflected in the format of fully-qualified GN labels, that look like "//<dir>:<target>(<toolchain_dir>:<toolchain_target>)".

    For example //src/foo:bar(//toolchain:debug) corresponds to the bar target defined in //src/foo/BUILD.gn, when it is compiled with the commands of the //toolchain:debug toolchain.

  • A separate GN execution context.

    Each toolchain instance executes its own parse of the GN buildconfig file, which sets up global variables and default values for all rules defining targets in that toolchain.

    In practice, if the same target is built with two different toolchains, the corresponding BUILD.gn file will be parsed twice, but each time with a different set of global variables, default configs and custom templates defined in BUILDCONFIG.gn.

  • A separate root directory for target outputs.

    While the targets built in the default toolchain are placed under root_build_dir, the ones that are built with a //<toolchain_dir>:<toolchain_name> instance are placed under ${root_build_dir}/<toolchain_name> instead.

    This location is available at GN gen time through the root_out_dir variable.

There is always at least one toolchain, called the default toolchain, which is determined by calling set_default_toolchain() from the buildconfig file.

For more information read the toolchain() reference documentation.

How the Fuchsia build uses GN toolchains

The Fuchsia build uses GN toolchains in several ways:

  • To build host and device executables.

    The build currently defines //build/toolchain/fuchsia:x64 and //build/toolchain/fuchsia:arm64 to build Fuchsia executable binaries for the 64-bit Intel and ARM architectures.

    It also defines //build/toolchain:host_x64 to build code for the host machine (i.e. the one where the build happens).

    It also defines //build/toolchain:linux_x64 and //build/toolchain:linux_arm64 to generate Linux 64-bit code as well, even if the host is not running one of these architectures.

    There are also a number of specialized toolchains used to compile bootloaders and parts of the kernel, described later.

  • To build ELF shared libraries.

    On Fuchsia, machine code that goes into shared objects (i.e. shared_library() and loadable_module() instances in GN speak) must be built with the -fPIC compiler and linker option.

    This is unlike executable code, that uses -fPIE instead.

    To deal with this, separate toolchain instances are defined to compile code for shared librarie.

    See ELF Shared Library Redirection for more details.

  • To build different variants (e.g. instrumented or optimized) of binaries.

    The Fuchsia build supports a number of "build variants" which allow building machine code in a slightly different way, for example:

    • The asan and ubsan variants are used to build machine code with Clang's Address Sanitizer, and Undefined Behaviour Sanitizer, respectively. There is even an asan-ubsan variant that combines both.

    • The coverage variant is used to build machine code with Clang's instrumentation-based profiling enabled, to support code coverage collection.

    • The profile variant is used to build instrumented code as well, but to support profile-guided optimization.

    • The thinlto and lto variants are used to build binaries with link-time optimization enabled.

    • The gcc variant is used to build certain pieces of the Zircon kernel with the GCC compiler instead of Clang (which has been useful to weed out subtle machine code generation issues that can affect the kernel in very important ways).

    There are many other variants defined in the build's BUILDCONFIG.gn file as well.

  • To generate (or process) source files.

    The build requires generating source files to be used in other targets in many places. For example, FIDL protocol definition files are processed to generate bindings for various languages (C++, Rust, Go and Dart), which are later used by other source_set(), or similar, targets.

    Because the targets that use these sources can be defined in different toolchain instances, it is useful to ensure that this generation is only performed once, instead of once per toolchain instance, since the output will be exactly the same in all cases.

    The Fuchsia build thus defines the fidling toolchain to perform FIDL bindings generation. Note that this toolchain is only used to run a few scripts using action() targets, never to actually compile them.

    Similarly, a number of other "basic" toolchains are defined in the build to perform processing tasks that should not be repeated needlessly.

How the Fuchsia build defines toolchain() instances.

The Fuchsia build provides these templates that define toolchain() instances with various features:

  • basic_toolchain() defines a "basic" toolchain, i.e. one that does only copy() or action() target, and never needs to use GN's builtin support for C++ and Rust compilation.

    These are used to generate outputs that are used by other targets in several other toolchains (e.g. language bindings) to avoid duplicate work.

    Note that one basic toolchain is used to build Go binaries, and another one for Dart ones, since GN doesn't support these languages at all.

  • clang_toolchain() defines a toolchain instances that invokes the Clang compiler. It provides support for building C++ and Rust sources using GN's builtin rules.

    Supported target platforms are Fuchsia, Linux, MacOS, Win32 PE/COFF (as required by the UEFI bootloader) and even WebAssembly!

  • clang_toolchain_suite() defines one more toolchain instances based on the current build variant configuration. It is preferred over calling clang_toolchain() directly because this is what allows build variants to work.

  • clang_host_toolchain_suite() is used for toolchains that generate host machine code.

  • zircon_toolchain() defines a toolchain instances that can be used to build part of the Zircon kernel, bootloaders or even the C library. These binaries typically require non-standard compile and linker commands (e.g. a different ABI, or lack of standard link environment).

    NOTE: Toolchains created by `zircon_toolchain() do not support the Rust programming language.

    One notable feature of this template is that it also supports building binaries using the GCC compiler, instead of Clang. This has proven useful to find low-level code generation issues that the kernel is very sensitive about, by its nature.

    NOTE: There is no plan to support building the rest of the platform with GCC.

  • zircon_toolchain_suite() is used to define one or more toolchain instances based on the current build variant configuration. It is preferred over calling zircon_toolchain() directly.

Note that the distinction between zircon_toolchain() and clang_toolchain() is mostly historical, they might be merged into a common template in the future.