blob: 3727c329c8b1e6c86ba2abb40b3d9a6362246cee [file] [log] [blame] [view]
# Build variants in the Fuchsia build
## Abstract
This document explains the implementation of "build variants", a feature of
the Fuchsia build system that allows building _instrumented_ or
_specially-optimized_ versions of the host and device binaries.
The reader must be familiar with [GN toolchain() instances][gn-toolchain]
and should have read the following documents:
- [GN toolchains overview][gn-toolchains-overview]
- [ELF shared library redirection][elf-shared-library-redirection]
## Overview of build variants
The Fuchsia build defines several types of build variants, for example:
- The `asan` and `ubsan` variants are used to build machine code with
Clang's [Address Sanitizer][clang-asan], and
[Undefined Behaviour Sanitizer][clang-ubsan], 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).
- The `release` and `debug` variants, which are provided to override
the default compilation mode, which is determined by the value of
the `is_debug` build configuration variable in `args.gn`.
- A few other variants for specialized needs, which are all defined
in the `//build/config/BUILDCONFIG.gn` file, using conventions
described in the rest of this document.
Generally speaking, a single build variant models:
- A set of extra configs that define compiler, assembler or linker flags
to be applied when building variant binaries and their dependencies.
- A set of optional implicit dependencies to be added to the final
variant binary targets in the build graph (i.e. executables, loadable
modules and even sometimes shared libraries).
For example, let's consider the "asan" build variant, which is used to
enable [Clang's Address Sanitizer][clang-asan]{:.external} support. In
practice, building a Fuchsia executable program with Address Sanitizer
enabled requires (at a minimum):
- Passing the `-fsanitize=address` flag both to the Clang compiler and linker
when building the executable and all its dependencies (including the
C library in the case of Fuchsia).
- The Asan runtime (`libclang_rt.asan.so`) being available at runtime,
as well as its own dependencies (i.e. special prebuilt versions of the
`libc++.so`, `libc++abi.so` and `libunwind.so` binaries).
## Base and variant toolchains
Build variants are always applied to a specific "base" toolchain, which
provides the default settings that are augmented by the variant itself.
This creates a new [GN toolchain()][gn-toolchain]{:.external} instance,
called a _"variant toolchain"_, which has its own `root_out_dir`. For
example:
- `//build/toolchain:host_x64` is the base toolchain used to build
host binaries, and its `root_out_dir` is `${root_build_dir}/host_x64`.
`//build/toolchain:host_x64-ubsan` is the variant toolchain created
by applying the `ubsan` variant, and its `root_out_dir` is
`${root_build_dir}/host_x64-ubsan`.
- `//build/toolchain/fuchsia:x64` is the default toolchain (when targetting
x64-based devices), used to build Fuchsia user-level binaries. Because it is
the default, its `root_out_dir` is the same as `root_build_dir`.
`//build/toolchain/fuchsia:x64-asan` is the variant toolchain created by
applying the `asan` variant to the default toolchain. Its `root_out_dir`
will be `${root_build_dir}/x64-asan`.
As a general rule, `//${tc_dir}:${tc_name}-${variant}` will be
a variant toolchain created by applying the `${variant}` variant to a base
toolchain named `//${tc_dir}:${tc_name}`, and its `root_out_dir` will
always be `${root_build_dir}/${tc_name}-${variant}`.
If a base toolchain has an [shlib toolchain][elf-shared-library-redirection],
then any of its variant toolchains will have one too. Finally, a single variant
can be applied to several base toolchains.
For example `//build/toolchain:host_x64-asan` and
`//build/toolchain/fuchsia:x64-asan` are variant toolchains created
by applying the same `asan` variant to the base toolchains used to
build host and Fuchsia device binaries.
The latter would also have `//build/toolchain/fuchsia:x64-asan-shared` as
an shlib toolchain to generate ELF-based shared libraries.
Base toolchains must be defined in the build using either `clang_toolchain_suite()`
or `zircon_toolchain_suite()`. Both templates end up calling `variant_toolchain_suite()`
which implements the magic that automatically creates variant toolchains when needed.
### Toolchain and variant tags {: #toolchain-and-variant-tags }
Each base toolchain in the Fuchsia build can have a number of tags, which
are free-form strings describing properties of the toolchain. For example,
the `"kernel"` tag is used to indicate that a toolchain is used to build kernel
artifacts (this is important because there is no C library, no standard C++
library and a few other constraints that are important for certain target
definitions).
The list of all valid toolchain tags is in
[`//build/toolchain/toolchain_tags.gni`][toolchain_tags.gni].
Similarly, each variant definition has a number of tags, describing properties
of the variant. For example, the `"instrumentation"` tag is used to
indicate that this variant creates machine code that performs runtime
instrumentation (e.g. sanitizers or profilers)
The list of all valid variant tags and their documentation is in
[`//build/toolchain/variant_tags.gni`][variant_tags.gni].
When a variant toolchain is created, the global `toolchain_variant.tags` value
will contain both the tags inherited from the base toolchain, and those inherited
from the variant.
### Toolchain variant instantiation
The build system will only create variant toolchains when they are needed.
There is a very large number of possible toolchain+variant combinations,
and creating all of them at once would make `gn gen` considerably slower.
Instead of eagerly creating all variants, the build system decides which
toolchain variants to create based on the following conditions:
- The list of [variant selectors](#variant-selectors) that appear in
the `select_variant` global variable.
- The list of [variants descriptor](#variant-descriptors) names that appear as
the [`enable_variants`][enable-variants]{:.external} argument of the
`variant_toolchain_suite()` template. It is seldom used to force-enable a
few variants even if `select_variant` is empty.
For example, the ASan and UBSan variants of the toolchain used to build the C library
are always enabled, because these are needed when building the Core Fuchsia IDK
(see `//zircon/system/ulib/c/BUILD.gn`).
- The list of variant tags that appear in the
[`exclude_variant_tags`][exclude-variant-tags]{:.external}
argument of `variant_toolchain_suite()`. It is seldom used to exclude specific
variants from being applied to a given base toolchain.
For example, the bootloader excludes variants with the `"instrumented"`
tag, since it is not possible to run a sanitizer or profiling runtime
while booting the device (see `//
## Variant descriptors {: #variant-descriptors }
A variant descriptor is a GN scope that describe the properties of
a given build variant to the build system. These are defined through
the `known_variants` variable in `//build/config/BUILDCONFIG.gn`, and
each scope should follow the following strict schema:
- **`configs`**: An optional list of GN config labels, that will be
automatically added to every target with this variant.
Note that for each config `${label}`, in this list, there must also
be a target `${label}_deps`, which each target built in this variant
will automatically depend on. Most of the time, this will be an
empty `group()`.
- **`remove_common_configs`**: An optional list of GN config labels
that should be _removed_, if present, from any target built with
this variant. This is sometimes necessary when some of the default
configs that the build system sets up for binaries should not be
used for a specific variant.
- **`remove_shared_configs`**: An optional list of GN config labels,
similar to `remove_common_configs`, but will only apply when
building `shared_library()` targets and their dependencies.
- **`deps`**: An optional list of GN target labels, which will be
added as implicit dependencies to any _linkable_ target that
is built with this variant.
- **`name`**: A string uniquely naming the variant descriptor, as
typically used in `select_variant`. If `name` is omitted, `configs`
must be non-empty and will be used to derive a name (by joining
their names with dashes).
- **`tags`**: An optional list of free-form strings describing properties
of the variant (see
[toolchain and variant tags](#toolchain_and_variant_tags]).
- **`toolchain_args`**: An optional scope, where each variable defined
in this scope overrides a build argument in the toolchain context
of this variant.
- **`host_only`** and **`target_only`**: Optional scopes that can
contain any of the fields above. These values are used only for host or
target (i.e. device) toolchains, respectively. Any fields
included here should not also be in the outer scope.
Here are a few examples:
### Example variant descriptor with a single config
```
{
configs = [ "//build/config/lto" ]
tags = [ "lto" ]
}
```
The scope above defines a variant descriptor named `"lto"` (since
there is no `name` key in the scope, the name is deduced from the
values in `configs` which here only contains a single item).
Applying this variant will add the `//build/config/lto:lto` config,
defined in `//build/config/lto/BUILD.gn`, and that file should also
contain a `//build/config/lto:lto_deps` empty group if such a
config has no implicit dependencies. For example:
```
# //build/config/lto/BUILD.gn
config("lto") {
cflags = [ "-flto" ]
asmflags = cflags
ldflags = cflags
rustflags = [ "-Clto=fat" ]
}
group("lto_deps") {
# Implicit dependencies for "lto" config.
# This is an empty group since there are none.
}
```
This descriptor uses the `"lto"` tag to indicate that this
variant performed link-time optimization. This tag can also
be used by the `"thinlto"` descriptor, which would be using
a different config.
### Example variant descriptor with several configs
```
{
configs = [
"//build/config/sanitizers:ubsan",
"//build/config/sanitizers:sancov",
]
remove_common_configs = [ "//build/config:no_rtti" ]
tags = [
"instrumented",
"instrumentation-runtime",
"kernel-excluded",
"sancov",
"ubsan",
]
}
```
This defines a variant descriptor named `"ubsan-sancov"` (the
name is derived from the `configs` list by joining the config
names with dashes), used to build machine code that detect
undefined behaviour at runtime, and collects code coverage
information at the same time.
Note that this also requires `//build/config/sanitizers:ubsan_deps`
and `//build/config/sanitizers:sancov_deps` to be defined to list
implicit dependencies from these configs.
This uses `remove_common_config` because `//build/config:no_rtti`
is part of the default configs of many base toolchains, but RTTI
must be enabled for UBSan instrumentation to work properly.
The list of tags used is also much more extensive. Note the
`"kernel-excluded"` tag which is used to prevent this variant
to be applied to any kernel machine code.
### Example variant descriptor with `toolchain_args`
```
{
name = "release"
toolchain_args = {
is_debug = false
}
}
```
This variant descriptor is named explicitly, and does not
add any configs or dependencies. On the other hand, it ensures
that the global build configuration variable `is_debug` will
be set to false, which change how many default configs are
defined in a corresponding variant toolchain context.
### Universal variants {: #universal-variants }
A lesser known feature of the build system is called "universal variants". These
are additional variant descriptors that _combine_ with other known variants,
they work as follows:
- If `is_debug=false` is set in `args.gn`, meaning that all binaries should
be built with maximal optimizations, then the `"debug"` variant descriptor
is defined by the build. This allows building specific targets in debug mode if
necessary.
- Similarly, if `is_debug=true` (the default), then the `"release"` variant
descriptor is defined by the build. This allows building specific targets with
full optimizations if necessary.
- Additionally, the universal variants above are combined with all other known
variant descriptors automatically by the build. E.g. if `is_debug=false`,
then the build will also create `"asan-debug"`, `"ubsan-debug"`,
`"thinlto-debug"`, etc. If `is_debug=true`, then it will define `"asan-release"`,
`"ubsan-release"`, `"thinlto-release"` and so on instead.
Note that these variant descriptors are _conditionally_ defined by the build,
based on the value of `is_debug`. I.e. there is no `"release"` variant and its
combinations when `is_debug=false`, and there is no `"debug"` variant and its
combinations when `is_debug=true`!
## The `toolchain_variant` global variable {: #toolchain_variant }
When in a `BUILD.gn` or `*.gni` file, the global `toolchain_variant` variable
can be used to retrieve variant-related information for the `current_toolchain`.
This is a scope with the following schema:
- **`name`**: Name of the build variant descriptor. This is an empty string in
the context of a base toolchain, or the name of the variant descriptor that
was used to created the current GN `toolchain()` instance otherwise.
Examples names for various toolchain contexts:
```
//build/toolchain/fuchsia:x64 ""
//build/toolchain/fuchsia:x64-shared ""
//build/toolchain/fuchsia:x64-asan "asan"
//build/toolchain/fuchsia:x64-asan-shared "asan"
```
- **`base`**: A fully-qualified GN label to the base toolchain for the
current one. Note that for the shlib toolchain of a toolchain variant,
this points to the final base toolchain. Examples:
```
//build/toolchain/fuchsia:x64 //build/toolchain/fuchsia:x64
//build/toolchain/fuchsia:x64-asan //build/toolchain/fuchsia:x64
//build/toolchain/fuchsia:x64-shared //build/toolchain/fuchsia:x64
//build/toolchain/fuchsia:x64-asan-shared //build/toolchain/fuchsia:x64
```
- **`tags`**: A list of free-form strings, each one describing a property of the
current toolchain instance and its variant. This is simply the union of
[toolchain and variant tags](#toolchain-and-variant-tags).
- **`instrumented`**: A boolean flag which will be set to true iff the `tags` list
contains the `"instrumentation"` tag value, provided as a convenience to replace
a complicated testing instruction in GN like:
```
if (toolchain_variant.tags + [ "instrumentation" ]
- [ "instrumentation" ] != toolchain_variant.tags) {
# toolchain is instrumented
...
}
```
With:
```
if (toolchain_variant.instrumented) {
# toolchain is instrumented
...
}
```
- **`is_pic_default`**: A boolean that is true in a toolchain that can build
ELF position independent code (PIC). This means either an shlib toolchain
(e.g. `//build/toolchain/fuchsia:x64-shared`), or a base toolchain that
produces such code directly (e.g. `//zircon/kernel/lib/userabi/userboot:userboot_arm64`).
- **`with_shared`**: A boolean that is true if the current toolchain has an shlib
toolchain to build ELF shared libraries (e.g. `//build/toolchain/fuchsia:x64`)
*or* when in such a toolchain (e.g. `//build/toolchain/fuchsia:x64-shared`).
- **`configs`**, **`remove_common_configs`**, **`remove_shared_configs`**: List
of GN labels to `config()` items, that come directly from the current
[variant descriptor](#variant-descriptors), if any, or empty lists otherwise.
- **`deps`**: List of GN labels to targets that are added as dependencies to
any _linkable_ target, inherited from the variant descriptor itself, if any.
- **`libprefix`**: For instrumented variants, this is an installation prefix string
for shared libraries, or an empty string otherwise. See the
[toolchain variant libprefix](#toolchain-variant-libprefix) section for full
details.
- **`exclude_variant_tags`**: Used internally by the variant selection logic.
Inherited from a `clang_toolchain_suite()` or `zircon_toolchain_suite()`
call, or directly from a target definition. It is is a list of tags used to
exclude variants to be applied to a base toolchain, or target, as is
sometimes necessary.
- **`suffix`**: This is `"-${toolchain_variant.name}"`, or `""` if name is empty.
Used internally to simplify expansions without conditionals.
The content of this global variable is seldom used by target definitions
to alter their configuration based on the current toolchain context. This
mostly happen for low-level targets, like the C library, kernel artifacts,
or instrumentation runtime support that do not come as prebuilts.
### Toolchain variant libprefix {: #toolchain-variant-libprefix }
In order to be able to mix instrumented and non-instrumented binaries in a single
Fuchsia package, special steps must be performed by the build system:
- The shared libraries that are built using an *instrumented* variant toolchain
must be installed to `"lib/<variant>/"` instead of the default `"lib/"` location.
- The executable binaries must be compiled with a linker argument like
`"-Wl,-dynamic-linker=<variant>/ld.so.1"`, which overrides the default value
(`"ld.so.1"`, which is hard-coded in the Fuchsia clang prebuilt toolchain binaries).
- As a special case, fuzzing build variants use the non-fuzzing build variant
name for the library sub-directory.
The `toolchain_variant.libprefix` variable is defined in the following way to help
support all of this easily:
```none
variant name libdir libprefix note
no variant ---> lib/ "" (default target toolchain)
thinlto ---> lib/ "" (uninstrumented)
asan-ubsan ---> lib/asan-ubsan/ "asan-ubsan/" (instrumented)
asan-fuzzer ---> lib/asan/ "asan/" (instrumented + fuzzing)
```
This can be used to determine the install location as `"lib/${toolchain_variant.libprefix}"`
and the linker flag as `"-Wl,-dynamic-linker=${toolchain_variant.libprefix}ld.so.1"`.
## Variant selection {: #variant-selection }
The Fuchsia build system supports selecting which build variants are
enabled, and to which individual targets, or groups of targets, they
apply. This is done by defining the `select_variant` variable in the
build configuration file (`args.gn`). Consider the following example:
```
# From out/default/args.gn
...
select_variant = [
{
label = [ "//src/sys/component_manager:bin" ]
variant = "release"
},
"host_asan",
"thinlto/blobfs",
"ubsan",
]
```
Each value in the list is an expression, called a
[**variant selector**](#variant-selectors) which can be a scope or a string,
used to configure how the build will apply variants to different sets of targets.
When `select_variant` is defined and not an empty list, its value will
be used to determine how to build *linkable* targets like executables,
loadable modules and shared libraries that appear in the build graph
in the context of a base toolchain, as well as all their dependencies.
The variant selectors that appear in `select_variant` are compared
in order, and the first one that matches the current target is selected.
As such, the example above means that:
- The `//src/sys/component/manager:bin` program binary, and its dependencies
should always be built with the `release` variant (NOTE: This example
will result in an error at `gn gen` time is `is_debug=false` is in
the `args.gn` file, because the `"release"` variant will not exist
in this case, see [universal variants](#universal-variants) to see why).
- Host binaries should be built in the `"asan"` variant.
Note that `"host_asan"` is not a variant descriptor name, but a
[variant shortcut](#variant-shortcuts).
- The `blobfs` program device binary should always be
built using the `"thinlto"` variant, which performs link-time
optimizations.
- All other device binaries should be built with the `"ubsan"` variant.
### Variant selectors {: #variant-selectors }
A variant selector is a value that can appear in the global `select_variant` build
configuration variable. They are used by the build system to control variant
selection when defining linkable targets in the context of base toolchains.
Three types of values are supported:
- A scope that defines a set of matching criteria for a set of targets.
The format of that scope is the following:
- **`variant`**: The name of a given [variant descriptor](#variant-descriptors)
that will be used if, and only if, the current target matches all the criteria
defined in the rest of the scope.
- **`label`**: If defined, this must be a list of qualified GN labels
(with `:` but without toolchain labels, e.g. `//src/sys/foo:foo`).
- **`name`**: If defined, a list of GN label target names (e.g. the name
of the `//src/sys/foo:bar` target is '"bar"`).
- **`dir`**: If defined, a list of GN label directory paths (e.g. the path
of the `//src/sys/foo:bar` target is `"//src/sys/foo"`).
- **`output_name`**: If defined, a list of target `output_name` value
(the default being its `target_name`).
- **`target_type`**: If defined, a list of strings matching the target type.
Valid values are: `"executable"`, `"test"`, `"loadable_module"`,
`"shared_library"` and a few others.
- **`testonly`**: If defined, a boolean. If true the selector matches targets
with `testonly=true`. If false, the selector matches targets without
`testonly=true`.
- **`host`**: If defined, a boolean. If true the selector matches targets in
a host toolchain. If false, the selector matches in the target toolchain.
- A string that contains a simple name (e.g. `"asan"`) that points to a
[variant shortcut](#variant-shortcuts), which is an alias for a pre-existing
selector scope value.
For example, the `"coverage"` value is equivalent to the following scope:
```
{
variant = "coverage"
host = false
}
```
- A string that contains a variant shortcut name and an output name separated by a
directory path (e.g. `"thintlo/blobfs"`). This is a convenience format that
avoids writing an equivalent scope, which would look like this in the previous
example as:
```
{
variant = "thinlto"
host = false
output_name = [ "blobfs" ]
}
```
The order of selectors in the `select_variant` list is important: the first selector
that matches the current target wins and determines how said target will be built.
### Variant shortcuts
In addition to variant descriptors, the build sets up a number of "shortcuts", which
are named aliases for a few hard-coded variant selector scope values. The build adds
a few hard-coded ones, and creates others from the list of known variants:
- The `"host_asan"` shortcut is defined to build host binaries with the `"asan"`
variant descriptor, and is technically equivalent to the following list of
selector scope values:
```
# Definition for the `host_asan` variant shortcut
[
{
variant = "asan"
host = true
}
]
```
Similarly, there exists `host_asan-ubsan`, `host_coverage`, `host_profile`
and several others.
- _Every_ variant descriptor name has a corresponding shortcut that applies
it exclusively to device binaries. I.e. the `"ubsan"` shortcut is equivalent
to this list of one selector scope value:
```
[
{
variant = "ubsan"
host = false
}
]
```
This is the reason why using a variant descriptor name in `select_variant`
only applies it to device binaries, as in:
```
# Applies the `ubsan` variant to device binaries, not host ones!
select_variant = [
"ubsan",
]
```
- Similarly, a shortcut is defined for every universal variant and its
cobinations, which again only apply them to device binaries.
This means, that assuming that `is_debug=true` in `args.gn`, the
following would force all device binaries to be built in release
mode, while the host ones would still be built in debug mode.
```
is_debug = true
select_variant = [ "release" ]
```
Which is equivalent to:
```
is_debug = true
select_variant = [
{
variant = "release"
host = false
}
]
```
A way to force host binaries to be compiled in release mode would
be to use an explicit scope value, since there is no shortcut for
this use case, as in:
```
is_debug = true
select_variant = [
{
variant = "release"
host = true
}
]
```
## Variant target redirection
### The `variant_target()` template {: #variant_target }
The `variant_target()` template defined in `//build/config/BUILDCONFIG.gn`
implements the core build variant selection mechanism.
This template should not be called directly from `BUILD.gn` files, instead,
it is invoked by the wrapper templates defined by the Fuchsia build for
`executable()`, `loadable_module()`, `shared_library()` and a few others
that correspond to _linkable targets_ (i.e. those created with the static
linker).
What it does is, for each target, in each toolchain context of the build
graph, compare the content of `select_variant` with the target's
properties (i.e. target type and a few additional arguments) to:
1) Compute the "builder toolchain" for the target, which is the GN
toolchain instance that will be used to build the real binary,
and its dependencies.
2) If the current toolchain is the builder toolchain, just build the
target as usual.
3) Otherwise, create either a `group()` or `copy()` target that will
redirect (i.e. publicly depend) on the target in the builder toolchain.
Whether this is a group or a copy depends on subtle conditions
that are fully documented in the implementation of `variant_target()`,
but see the following sub-section for some explanations.
The `copy()` target is required to preserve the output location
of some linkable targets, while the `group()` is used when this
is not needed.
Most of the time, an `executable()` or `loadable_module()` target
will require a `copy()`, and a `shared_library()` one will require
a `group()` instead.
### Output location of linkable variant binaries
A critical design limitation of the GN configuration language is that,
with a few exceptions, a given target definition _doesn't know anything
about its dependencies_, except for their labels. This is problematic
because there are many cases where a given target needs to know where
its dependencies' outputs are located, or what type of targets these
dependencies really are.
To illlustrate this, let's consider the following example:
- An `executable()` target named `//src/my/program:bin` that generates a
Fuchsia program binary named `my_program`. Due to the way the build works
this generates `${root_build_dir}/exe.unstripped/my_program` and
`${root_build_dir}/my_program`, plus a few minor files (ignored here).
- An `action()` target named `//src/my/program:verify_binary` used to parse
the program binary to check it or extract information out of it (let's say
it verifies its import symbol references). This target needs to depend on
the first one, but also locate where the binary's output location, as in:
```none
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
executable("//src/my/program:bin")
output_name = "my_program"
# outputs: [
${root_build_dir}/exe.unstripped/my_program,
${root_build_dir}/my_program,
]
```
Here, the `action()` can guess the location of the program binary by
using `get_label_info("<label>", "root_out_dir")` for its directory,
and hard-code the `output_name` value in the action itself. This violates
abstraction layers, but this is necessary given GN's limitations.
When build variants are enabled, the actual output location of binary targets
will change depending on `select_variant`. If variant redirection is implemented
with a simple `group()`, the graph becomes:
```none
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
group("//src/my/program:bin")
|
| public_deps
|
v
executable("//src/my/program:bin(//build/toolchain/fuchsia:x64-asan")
output_name = "my_program"
# outputs: [
# ${root_build_dir}/x64-asan/exe.unstripped/my_program,
# ${root_build_dir}/x64-asan/my_program,
# ]
```
The problem is that the value of `inputs` in the top-level action didn't change,
so its command will try to find the program binary at the old location
(`${root_build_dir}/my_program`) instead of the new one
(`${root_build_dir}/x64-asan/my-program`). Either the build will use a stale artifact,
or will fail due to a missing file.
Parsing `select_variant` in the action itself would be too expensive, so solving
this situation, for executable and loadable module targets required a `copy()`
target, instead of `group()`, to ensure that the unstripped binary is copied
to its original location. The graph becomes:
```none
action("//src/my/program:verify_imports")
script = "check-my-imports.py"
deps = [ "//src/my/program:bin" ]
inputs = [ get_label_info(deps[0], "root_out_dir") + "/my_program" ]
...
|
| deps
|
v
copy("//src/my/program:bin")
outputs = [ "${root_build_dir}/my_program" ]
sources = [ "${root_build_dir}/x64-asan/my_program" ]
|
| public_deps
|
v
executable("//src/my/program:bin(//build/toolchain/fuchsia:x64-asan")
output_name = "my_program"
# outputs: [
${root_build_dir}/x64-asan/exe.unstripped/my_program,
${root_build_dir}/x64-asan/my_program,
]
```
With this setup, the build always succeeds, and the action command always
processes the right binary.
All of this is done automatically in the build. The final effect is that
dependent do not need to care whether their dependencies were built with
a specific variant or not, they can rely on the output locations to be
stable, at least for the unstripped binary paths.
### Output location of ELF shared libraries
TBW
### The special `novariant` descriptor {: #novariant }
TBW
## Special global variables {: #global_variables }
### The `host_toolchain` and `host_out_dir` global variables {: #host_variables }
TBW
### The `zircon_toolchain` variable {: #zircon_variable }
TBW
### The `variant()` template {: #variant_template }
TBW
[gn-toolchain]: https://gn.googlesource.com/gn/+/main/docs/reference.md#toolchain-overview
[gn-toolchains-overview]: /docs/concepts/build_system/internals/toolchains/gn_toolchains_overview.md
[elf-shared-library-redirection]: /docs/concepts/build_system/internals/toolchains/elf_shared_library_redirection.md
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
[clang-ubsan]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
[enable-variants]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/toolchain/variant_toolchain_suite.gni;drc=28b4b027204084d695ba0659a7ecb733196b543f;l=180
[exclude-variant-tags]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/toolchain/variant_toolchain_suite.gni;drc=28b4b027204084d695ba0659a7ecb733196b543f;l=151
[toolchain_tags.gni]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/toolchain/toolchain_tags.gni
[variant_tags.gni]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/toolchain/variant_tags.gni