blob: 97c54138ac533e8c202ef71e93d8726c45d18687 [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
declare_args() {
# *This must never be set as a build argument.*
# It exists only to be set via c_toolchain().
# See environment() for more information.
toolchain = {
configs = []
environment = "stub"
label = "//zircon/public/gn/toolchain:stub"
globals = {
}
}
}
# The default toolchain is a very boring one. It can only be used for
# action and copy rules where current_cpu et al don't matter. Doing
# anything else happens in another toolchain.
set_default_toolchain(toolchain.label)
# The rest of this file pertains to what happens in other toolchains
# defined by environment().
# The toolchain can specify pervasive globals.
forward_variables_from(toolchain.globals, "*")
# This is the name for $current_cpu that's used in Zircon file names.
if (current_cpu == "x64") {
zircon_cpu = "x86"
} else if (current_cpu != "") {
zircon_cpu = current_cpu
}
# Shorthands.
current_platform = "${current_os}-${current_cpu}"
host_platform = "${host_os}-${host_cpu}"
# Shorthand for `current_os == "fuchsia"`.
is_fuchsia = current_os == "fuchsia"
if (!defined(is_gcc)) {
# True iff $current_toolchain builds with GCC rather than Clang.
is_gcc = false
}
if (!defined(is_host)) {
# True iff $current_toolchain builds for a host platform rather than for
# the Fuchsia system being built. This doesn't mean that it builds for
# $host_cpu and $host_os--it could be a cross-compilation toolchain for
# building host tools for a different host.
is_host = false
}
if (!defined(is_kernel)) {
# True iff $current_toolchain builds kernel-like code.
is_kernel = false
}
if (!defined(is_efi)) {
# True iff $current_toolchain builds efi bootloader code.
is_efi = false
}
###
### "Non-terminal" (library) target types.
### These are compiling targets that never do variant selection.
###
# This is for doing the actual shared_library() or loadable_module()
# target in a toolchain that supports them directly. That is, either
# ${toolchain.label} == ${toolchain.shlib} or this is a toolchain that
# was defined with `solink = true` so there is no ${toolchain.shlib}.
template("_shlib_toolchain_target") {
assert(!is_kernel,
"${invoker.target_type}() targets don't work in kernel toolchains")
target(invoker.target_type, target_name) {
# visibility and data_deps get special treatment in ${toolchain.shlib}.
if (defined(toolchain.shlib)) {
if (defined(invoker.visibility)) {
# Make sure we're visible to the redirector groups defined below.
visibility = invoker.visibility + [ ":$target_name" ]
}
if (defined(invoker.data_deps)) {
# Redirect data_deps to the non-shlib toolchain. Any label listed
# with an explicit toolchain is fine as is. But a bare label was
# intended for the main toolchain and the shlib toolchain is an
# implementation detail that's mostly hidden. There's no way to
# distinguish a bare label from one that explicitly used the shlib
# toolchain, but there should be no reason to do that.
data_deps = []
foreach(label, invoker.data_deps) {
if (get_label_info(label, "toolchain") == current_toolchain) {
label = get_label_info(label, "label_no_toolchain")
label += "(${toolchain.label})"
}
data_deps += [ label ]
}
}
} else {
forward_variables_from(invoker,
[
"data_deps",
"visibility",
])
}
# Everything else is passed through (everything in solink toolchains).
forward_variables_from(invoker,
"*",
[
"data_deps",
"match",
"target_type",
"visibility",
])
if (!defined(no_implicit_deps) || !no_implicit_deps) {
# Apply the toolchain's implicit_deps. Note the logic here should
# match the ${toolchain.configs} handling in the set_defaults()
# loop at the end of this file.
if (!defined(deps)) {
deps = []
}
foreach(dep, toolchain.implicit_deps) {
# Either it's an absolute label string or it's a scope with filters
# and mutators. See environment().
if (dep == "$dep") {
deps += [ dep ]
} else if (defined(dep.types) && dep.types + [ invoker.match ] -
[ invoker.match ] == dep.types) {
# The types list doesn't include this target's type, so skip this
# element.
} else if (configs + dep.unless_configs - dep.unless_configs !=
configs) {
# Something in the unless_configs list is in this target's configs,
# so skip this element.
} else {
deps += dep.add
deps -= dep.remove
}
}
}
}
}
# See `gn help shared_library`. Instead use library() with `shared = true`.
#
# This has the same API as described by `gn help shared_library`. The
# difference from the built-in behavior is the implicit redirect to the
# shlib toolchain when in an environment() that set `shlib = true`.
template("shared_library") {
if (!defined(toolchain.shlib) || current_toolchain == toolchain.shlib) {
# This is the toolchain that actually builds the libraries.
_shlib_toolchain_target(target_name) {
target_type = "shared_library"
match = target_type
forward_variables_from(invoker,
"*",
[
"metadata",
"sdk_migrated",
"target_type",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# Elaborate the toolchain's defaults to compute the output file name.
if (!defined(output_dir)) {
output_dir = target_out_dir
}
if (!defined(output_name)) {
output_name = target_name
}
output_name += toolchain.output_name_suffix
if (!defined(output_extension)) {
output_extension = "so"
}
output_file_base = "$output_dir/lib$output_name"
output_file = output_file_base
if (output_extension != "") {
output_file += ".$output_extension"
}
link_output_file = output_file + toolchain.link_output_suffix
metadata = {
binaries = []
# Used by link_output_rspfile(), see its documentation for details.
link_output_barrier = []
link_output_path = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
link_output_path += [ rebase_path(link_output_file, root_build_dir) ]
if (!defined(invoker.sdk_migrated) || !invoker.sdk_migrated) {
# For build_api_module("binaries") in //zircon/BUILD.gn.
binaries += [
{
type = "shared_library"
label = get_label_info(":$target_name", "label_with_toolchain")
environment = toolchain.environment
cpu = current_cpu
os = current_os
debug = rebase_path(link_output_file, root_build_dir)
if (toolchain.link_output_suffix != "") {
dist = rebase_path(output_file, root_build_dir)
}
if (current_os != "mac" && current_os != "win") {
elf_build_id = rebase_path("${output_file_base}.build-id.stamp",
root_build_dir)
}
if (toolchain.breakpad_syms) {
breakpad =
rebase_path("${output_file_base}.sym", root_build_dir)
}
},
]
}
# Each shared_library() that's not on the allowlist gets a poison
# pill to flag it if it appears in the dependency graph from a
# driver() target.
if (!defined(driver_shlib_denylist)) {
driver_shlib_denylist =
[ get_label_info(":$target_name", "label_with_toolchain") ]
}
}
}
} else {
# In the main toolchain, just redirect to the shlib toolchain.
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
not_needed(invoker, "*")
public_deps = [ ":$target_name(${toolchain.shlib})" ]
}
}
}
# Build a library, possibly in several ways.
#
# This template is the way to specify any library that follows the convention
# of having an `include/` subdirectory of the source directory that needs to
# get into the `#include` search path of all users of the library.
#
# This defines multiple related targets, depending on the parameters. Each
# subtarget is named `$target_name.<something>`. The main target name is an
# alias for the "preferred" subtarget (see below). When the target is the
# main target for its directory (i.e. the target name matches the directory
# name), then each subtarget also has an alias called just `<something>`. So
# for example, `//some/lib:static` is an alias for `//some/lib:lib.static` and
# dependents can refer to either label equivalently. Other targets in the
# same directory must be referred to directly, e.g. `//some/lib:otherlib` or
# `//some/lib:otherlib.static`.
#
# The subtargets are described below, after the parameters.
#
# Parameters
#
# host
# Optional: True if this library can be used in host-side code, e.g.
# to be a dependency of a host_tool().
# Type: bool
# Default: false
#
# kernel
# Optional: True if this library can be used in the kernel.
# Type: bool
# Default: false
#
# static
# Optional: True if this library can be statically linked in user code.
# Type: bool
# Default: !$kernel
#
# shared
# Optional: True if this library can be made a shared library for user code.
# Type: bool
# Default: false
#
# sdk
# Optional: Indicates this library will be published for use in the legacy
# Fuchsia build. (Later, this will control publishing to the SDK proper;
# but the details may well change by then.) If this is present, then
# $sdk_headers is required.
# Type: "static" or "shared" or "source"
#
# sdk_publishable
# Optional: Indicates that this library can be added to SDKs in the legacy
# Fuchsia build.
# Type: bool
# Default: false
#
# sdk_headers
# Optional: Required if $sdk is set. This must list all the header files
# in the `include/` directory; names should be relative to `include/`.
# Note this must list not only the "public" header files, but also any
# internal headers used by those, since it controls what files will be
# available at all to legacy/SDK users of the library.
# Type: list(string)
#
# sdk_name
# Optional: Name under which the library is published to the GN build.
# Type: string
# Default: target_name
#
# sdk_migrated
# Optional: Indicates that this library is built directly by the Fuchsia
# build, and no longer needs to be exported as a legacy target. This is
# used to ease build unification, and will disappear once it is completed.
# Type: bool
# Default: false
#
# See source_set() for other parameters.
#
# Subtargets
#
# "$target_name.headers"
# This target is always generated. Dependents get access to the header
# files in the `include/' subdirectory but do not link in any code. The
# main target is an alias for this *only* in contexts where no other
# subtargets are defined.
#
# "$target_name.shared"
# This target is generated only if $shared is true, and only ever
# generated in user environments. The kernel and host environments never
# build shared libraries. The main target is an alias for this when it's
# generated.
#
# "$target_name.static"
# This target is generated in user environments if $static is true (the
# default), in kernel environments if $kernel is true, and in host
# environments if $host is true. The main target is an alias for this
# when $shared is not set (and in non-user environments when $shared is
# ignored).
#
template("library") {
_library_name = target_name
host = defined(invoker.host) && invoker.host
kernel = defined(invoker.kernel) && invoker.kernel
shared = defined(invoker.shared) && invoker.shared
if (defined(invoker.static)) {
static = invoker.static
} else {
static = !kernel
}
if (defined(invoker.sdk)) {
# TODO(fxbug.dev/3156): This is currently used for the legacy build integration.
# Eventually it will instead drive the proper SDK production directly, but
# it's likely the details will change when that happens. For now it's not
# meant to be very clean, just to do what works in the legacy build.
sdk = invoker.sdk
sdk_headers = invoker.sdk_headers
sdk_name = _library_name
if (defined(invoker.sdk_name)) {
sdk_name = invoker.sdk_name
}
sdk_publishable =
defined(invoker.sdk_publishable) && invoker.sdk_publishable
if (sdk != false) {
assert(static || shared,
"Public library(\"$_library_name\") must be static or shared")
}
if (sdk == "shared") {
assert(
shared,
"library(\"$_library_name\") must set shared=true to use sdk=\"shared\"")
} else if (sdk == "static") {
assert(
static,
"library(\"$_library_name\") cannot set static=false sdk=\"static\"")
} else if (sdk == "source") {
assert(
static,
"library(\"$_library_name\") cannot set static=false sdk=\"source\"")
}
sdk_migrated = defined(invoker.sdk_migrated) && invoker.sdk_migrated
} else {
sdk = false
sdk_headers = []
sdk_name = _library_name
sdk_publishable = false
}
_library_params = [
"kernel",
"host",
"sdk",
"sdk_headers",
"sdk_name",
"sdk_publishable",
"shared",
"static",
]
assert(host || kernel || static || shared,
"library(\"$target_name\") must build somewhere!" +
" `host`, `kernel`, `static`, or `shared` must be true")
# Not all of these will be referenced in all toolchains.
not_needed(_library_params)
if (shared && defined(invoker.no_implicit_deps)) {
not_needed(invoker, [ "no_implicit_deps" ])
}
# A specialized toolchain might not support shared libraries.
shared = shared && defined(toolchain.shlib)
# TODO(fxbug.dev/3156): temporary hacks
if (sdk != false && !sdk_migrated &&
((toolchain.environment == "user" &&
current_toolchain == toolchain.shlib) ||
toolchain.environment == "host")) {
if (toolchain.environment == "host") {
# Host libraries are always exported as sources.
sdk = "source"
}
sdk_deps = []
if (defined(invoker.public_deps)) {
foreach(label, invoker.public_deps) {
if (get_label_info(label, "name") == "headers" ||
get_path_info(get_label_info(label, "name"), "extension") ==
"headers" ||
get_path_info(get_label_info(label, "dir"), "dir") ==
"//zircon/system/fidl") {
sdk_deps += [ label ]
}
}
}
if (sdk != "shared" && defined(invoker.deps)) {
sdk_deps += invoker.deps
}
install_files = []
debug_files = []
if (sdk != "source") {
if (defined(invoker.output_prefix_override) &&
invoker.output_prefix_override) {
libfile = sdk_name
} else {
libfile = "lib$sdk_name"
}
libfile = "$target_out_dir/$libfile"
if (sdk == "static") {
libs_file = "${libfile}.a"
link_file = libs_file
} else {
libs_file = "${libfile}.so"
debug_files += [ rebase_path("${libs_file}.debug", root_build_dir) ]
install_files += [
{
source = rebase_path(libs_file, root_build_dir)
dest = "dist/" + get_path_info(libs_file, "file")
},
]
# TODO(fxbug.dev/27181): The stripped binary doesn't even have section
# headers, so the linker can't handle it. Eventually we'll have
# linker stubs. For now, just use the unstripped library to link.
link_file = "${libs_file}.debug"
}
install_files += [
{
source = rebase_path(link_file, root_build_dir)
dest = "lib/" + get_path_info(libs_file, "file")
},
]
}
local_include_dirs = []
if (defined(invoker.include_dirs)) {
foreach(dir, invoker.include_dirs) {
local_include_dirs += [ "//zircon/" + rebase_path(dir, "//zircon/") ]
}
}
local_defines = []
if (defined(invoker.defines)) {
local_defines += invoker.defines
}
ubsan_config = "//build/config/zircon:temporarily_disable_ubsan_do_not_use"
ubsan_disabled =
defined(invoker.configs) &&
invoker.configs + [ ubsan_config ] - [ ubsan_config ] != invoker.configs
sanitizer_config = "//build/config/zircon:no_sanitizers"
sanitizers_disabled =
defined(invoker.configs) && invoker.configs + [ sanitizer_config ] -
[ sanitizer_config ] != invoker.configs
legacy_metadata = {
legacy_barrier = []
legacy_dirs = [ "lib/$sdk_name" ]
legacy_targets = [
{
_label = get_label_info(":$sdk_name", "label_with_toolchain")
_zircon_public = "lib"
forward_variables_from(invoker, [ "testonly" ])
import = "//build/zircon/zircon_library.gni"
target_type = "zircon_library"
target_name = "$sdk_name"
dir = "//zircon/" + rebase_path(".", "//")
include_dirs = [ "//zircon/" + rebase_path("include", "//zircon/") ]
compilation_include_dirs = local_include_dirs
compilation_defines = local_defines
disable_ubsan = ubsan_disabled
disable_sanitizers = sanitizers_disabled
headers = sdk_headers
publishable = sdk_publishable
if (sdk == "source") {
source_dir = "//zircon/" + rebase_path(".", "//zircon/")
sources = []
foreach(file, rebase_path(invoker.sources, "//zircon/")) {
sources += [ "//zircon/$file" ]
}
} else {
libs = [ rebase_path(link_file, root_build_dir) ]
}
deps = []
public_deps = []
foreach(dep, sdk_deps) {
if (get_label_info(dep, "name") == "c") {
dep = get_label_info(dep, "dir")
assert(get_path_info(dep, "dir") == "//zircon/system/fidl")
dep = get_label_info(dep, "name")
deps += [ "//zircon/system/fidl/${dep}:${dep}_c" ]
} else if (get_label_info(dep, "name") == "c.headers") {
dep = get_label_info(dep, "dir")
assert(get_path_info(dep, "dir") == "//zircon/system/fidl")
dep = get_label_info(dep, "name")
public_deps += [ "//zircon/system/fidl/${dep}:${dep}_c" ]
} else if (get_label_info(dep, "name") == "llcpp") {
dep = get_label_info(dep, "dir")
assert(get_path_info(dep, "dir") == "//zircon/system/fidl")
dep = get_label_info(dep, "name")
deps += [ "//zircon/system/fidl/${dep}:${dep}_llcpp" ]
} else if (get_label_info(dep, "name") == "llcpp.headers") {
dep = get_label_info(dep, "dir")
assert(get_path_info(dep, "dir") == "//zircon/system/fidl")
dep = get_label_info(dep, "name")
public_deps += [ "//zircon/system/fidl/${dep}:${dep}_llcpp" ]
} else if (get_label_info(dep, "name") == "headers") {
dep = get_path_info(get_label_info(dep, "dir"), "name")
if (dep != "c" && dep != "llcpp") {
public_deps += [ "//zircon/public/lib/$dep" ]
}
} else if (get_path_info(get_label_info(dep, "name"),
"extension") == "headers") {
dep = get_path_info(get_label_info(dep, "name"), "name")
if (dep != "c" && dep != "llcpp") {
public_deps += [ "//zircon/public/lib/$dep" ]
}
} else {
if (get_label_info(dep, "name") == "static" ||
get_label_info(dep, "name") == "shared") {
dep = get_path_info(get_label_info(dep, "dir"), "name")
deps += [ "//zircon/public/lib/$dep" ]
} else {
dep = get_path_info(get_label_info(dep, "name"), "name")
if (dep != "common" && dep != "handler") {
deps += [ "//zircon/public/lib/$dep" ]
}
}
}
}
install = install_files
debug = debug_files
},
]
}
} else {
legacy_metadata = {
legacy_barrier = []
}
}
not_needed([ "legacy_metadata" ])
# Empty libraries are useless, so do a source set instead.
# TODO(crbug.com/gn/16): Empty library works OK and the source_set
# case tickles a GN bug. Remove `&& false` when the bug is fixed.
if (invoker.sources == [] && false) {
static_library_target_type = "source_set"
} else {
static_library_target_type = "static_library"
}
not_needed([ "static_library_target_type" ])
targets = false
if (is_kernel) {
if (kernel) {
targets = true
if (toolchain.environment == "kernel") {
# In the kernel proper, library() is always a source_set().
# Everything goes into the kernel and anything unused gets linker GC.
kernel_library_target_type = "source_set"
} else {
# In other is_kernel environments, libraries are really libraries.
kernel_library_target_type = static_library_target_type
}
target(kernel_library_target_type, _library_name) {
if (kernel_library_target_type == "static_library") {
complete_static_lib = true
}
forward_variables_from(invoker, "*", _library_params)
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":${_library_name}.headers" ]
}
}
} else if (is_host) {
if (host) {
targets = true
target(static_library_target_type, _library_name) {
if (static_library_target_type == "static_library") {
complete_static_lib = true
}
forward_variables_from(invoker, "*", _library_params)
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":${_library_name}.headers" ]
# TODO(fxbug.dev/3156): temporary hacks
metadata = legacy_metadata
}
} else {
# Set up a group target with enough metadata that no-op dependencies are
# correctly reflected in the GN build.
targets = true
not_needed(invoker, "*")
group(_library_name) {
forward_variables_from(invoker, [ "testonly" ])
public_deps = [ ":${_library_name}.headers" ]
metadata = {
legacy_barrier = []
legacy_dirs = [ "lib/$sdk_name" ]
legacy_targets = [
{
_label = get_label_info(":$sdk_name", "label_with_toolchain")
_zircon_public = "lib"
import = "//build/zircon/zircon_header_library.gni"
target_type = "zircon_header_library"
target_name = "$sdk_name"
include_dirs =
[ "//zircon/" + rebase_path("include", "//zircon/") ]
},
]
}
}
}
} else if (static || shared) {
targets = true
source_set("${_library_name}._sources") {
visibility = [
":${_library_name}.shared",
":${_library_name}.static",
]
forward_variables_from(invoker,
"*",
_library_params + [
"data_deps",
"install_path",
"public_deps",
"visibility",
])
if (!defined(deps)) {
deps = []
}
deps += [ ":${_library_name}.headers" ]
if (defined(invoker.data_deps)) {
# Redirect data_deps to the non-shlib toolchain.
data_deps = []
foreach(label, invoker.data_deps) {
if (get_label_info(label, "toolchain") == current_toolchain) {
label += "(${toolchain.label})"
}
data_deps += [ label ]
}
}
}
if (static) {
target(static_library_target_type, "${_library_name}.static") {
if (static_library_target_type == "static_library") {
complete_static_lib = true
}
output_name = _library_name
forward_variables_from(invoker,
[
"configs",
"data_deps",
"public_configs",
"public_deps",
"testonly",
"visibility",
])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":${_library_name}.headers" ]
deps = [ ":${_library_name}._sources" ]
# TODO(fxbug.dev/3156): temporary hacks
metadata = legacy_metadata
}
}
if (shared) {
if (defined(invoker.install_path)) {
install_path = invoker.install_path
} else {
install_path = "lib/${toolchain.libprefix}lib${_library_name}.so"
}
zx_manifest_file = "$target_gen_dir/$target_name.zx_manifest.txt"
library_file = "$target_out_dir/lib${_library_name}.so"
manifest_line =
"${install_path}=" + rebase_path(library_file, root_build_dir)
write_file(zx_manifest_file, [ manifest_line ], "list lines")
shared_library("${_library_name}.shared") {
output_name = _library_name
forward_variables_from(invoker,
[
"configs",
"ldflags",
"libs",
"lib_dirs",
"no_implicit_deps",
"public_configs",
"testonly",
"visibility",
])
# Everything that depends on the library gets the headers.
# It also gets the explicit `public_deps`, which includes
# any header dependencies or other targets with public_configs.
public_deps = [ ":${_library_name}.headers" ]
if (defined(invoker.public_deps)) {
public_deps += invoker.public_deps
}
# The library depends on the source_set(). It also depends on the
# `deps` from the source_set() so as to get any public_configs from
# dependencies that affect linking rather than just compilation.
# Other dependencies are redundant since the source_set() already
# has them, but they don't hurt.
deps = [ ":${_library_name}._sources" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
metadata = {
if (defined(invoker.metadata)) {
# The $metadata set on library() goes on the source_set().
# Generally that's fine, since it will be found via the deps
# from either static or shared library. But shared_library()
# injects the driver_shlib_denylist data implicitly if it's not
# set explicitly, so make sure to propagate those keys to it.
forward_variables_from(invoker.metadata,
[
"driver_shlib_denylist",
"driver_shlib_denylist_barrier",
])
}
# An explicit `install_path = false` means this DSO is not installed.
if (install_path != false) {
manifest_inputs = [ library_file ]
manifest_lines = [ manifest_line ]
}
# TODO(fxbug.dev/3156): temporary hacks
forward_variables_from(legacy_metadata, "*")
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, [ "legacy_sysroot" ])
}
}
}
}
group(_library_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
if (shared) {
public_deps = [ ":${_library_name}.shared" ]
} else {
public_deps = [ ":${_library_name}.static" ]
}
}
}
if (!targets) {
# In this toolchain there are no actual targets, only the headers.
not_needed(invoker, "*")
group(_library_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${_library_name}.headers" ]
}
}
group("${_library_name}.headers") {
# The public_deps here represent header dependencies.
# Direct use of public_configs should be rare but is sometimes needed.
forward_variables_from(invoker,
[
"public_configs",
"public_deps",
"testonly",
"visibility",
])
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [ ":_library.config.$_library_name" ]
if (defined(visibility)) {
visibility += [ ":$_library_name" ]
if (!is_kernel) {
visibility += [
":${_library_name}.shared",
":${_library_name}.sources",
":${_library_name}.static",
]
}
}
}
config("_library.config.$_library_name") {
visibility = [ "${_library_name}.headers" ]
include_dirs = [ "include" ]
}
# If this library is the main target for the directory, then give its
# auxiliary targets aliases `dir:headers`, `dir:static`, `dir:shared`.
if (get_label_info(":$_library_name", "name") ==
get_path_info(get_label_info(":$_library_name", "dir"), "file")) {
group("headers") {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${_library_name}.headers" ]
}
# This is really only used in the //zircon/kernel:kernel environment().
config("headers.config") {
include_dirs = [ "include" ]
}
if (!is_kernel && !is_host) {
if (static) {
group("static") {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${_library_name}.static" ]
}
}
if (shared) {
group("shared") {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${_library_name}.shared" ]
}
}
}
}
}
###
### "Terminal" (executable and loadable_module) target types.
### These are the targets that do variant selection.
###
# Subroutine of all public terminal target types (executable, host_tool).
#
# Parameters
#
# target
# Required: Details about the caller template.
# Type: scope as below
# type
# Required: The actual target type to define.
# Type: string
# match
# Optional: The target type to match in $variants selectors.
# Type: string
# Default: $type
# label
# Optional: The target label to match in $variants selectors.
# Type: label_no_toolchain
# Default: ":$target_name"
# output_name
# Optional: The $output_name to match in $variants selectors.
# Type: string
# Default: $output_name (which defaults to $target_name)
# shlib
# Optional: Target always builds in shlib toolchain (loadable_module).
# Type: bool
# Default: false
# main_metadata, variant_metadata
# Optional: Metadata for main target and variant-suffixed target.
# Type: metadata scope
# Default: {}
# exclude_variant_tags
# Optional: Never select a variant with any of these tags.
# Type: list(string)
# Default: []
# variant_suffix_target
# Optional: Define $target_name.$variant targets.
# Type: bool
# Default: true
#
# See executable() or loadable_module() for other parameters.
# They will be forwarded to the underlying ${target.type} target.
#
template("_variant_target") {
target = {
main_metadata = {
}
variant_metadata = {
}
exclude_variant_tags = []
variant_suffix_target = true
forward_variables_from(invoker.target, "*")
if (!defined(match)) {
match = type
}
if (!defined(label)) {
label = ":$target_name"
}
}
main_target_name = target_name
# Elaborate the default to simplify deriving names later.
if (defined(invoker.output_name)) {
output_name = invoker.output_name
} else {
output_name = target_name
}
if (!defined(target.output_name)) {
target.output_name = output_name
}
if (toolchain.variant_selectors == []) {
# This toolchain does not participate in variant selection.
# Each target is just what it seems.
target(target.type, main_target_name) {
# Always forward visibility and testonly explicitly so that
# they are picked up if defined at file/global scope, which
# "*" does not pick up.
forward_variables_from(invoker,
"*",
[
"visibility",
"testonly",
])
forward_variables_from(invoker,
[
"visibility",
"testonly",
])
}
} else {
# Most toolchains defined with environment() do variant selection. The
# ${toolchain.variant_selectors} list (usually ultimately from the
# $variants build argument and some defaults) controls which sibling
# toolchain among all the offspring of the same environment() is the usual
# builder for each target. The first matching selector in the list wins.
builder_toolchain = false
foreach(selector, toolchain.variant_selectors) {
# An empty selector always matches, so start with the flag set.
# Each `if` block below applies each inclusion criterion: if it's
# present and it does not include this target, then clear the flag.
selector_matches = true
if (selector.cpu != []) {
if (selector.cpu + [ current_cpu ] - [ current_cpu ] == selector.cpu) {
selector_matches = false
}
}
if (selector.dir != []) {
dir = get_label_info(target.label, "dir")
if (selector.dir + [ dir ] - [ dir ] == selector.dir) {
selector_matches = false
}
}
if (selector.environment != []) {
if (selector.environment + [ toolchain.environment ] -
[ toolchain.environment ] == selector.environment &&
selector.environment + [ toolchain.base_environment ] -
[ toolchain.base_environment ] == selector.environment) {
selector_matches = false
}
}
if (selector.label != []) {
label = get_label_info(target.label, "label_no_toolchain")
if (selector.label + [ label ] - [ label ] == selector.label) {
selector_matches = false
}
}
if (selector.name != []) {
name = get_label_info(target.label, "name")
if (selector.name + [ name ] - [ name ] == selector.name) {
selector_matches = false
}
}
if (selector.os != []) {
if (selector.os + [ current_os ] - [ current_os ] == selector.os) {
selector_matches = false
}
}
if (selector.output_name != []) {
if (selector.output_name + [ target.output_name ] -
[ target.output_name ] == selector.output_name) {
selector_matches = false
}
}
if (selector.tags != []) {
if (selector.tags + target.exclude_variant_tags -
target.exclude_variant_tags != selector.tags) {
selector_matches = false
}
}
if (selector.target_type != []) {
if (selector.target_type + [ target.match ] - [ target.match ] ==
selector.target_type) {
selector_matches = false
}
}
if (defined(selector.host) && selector.host != is_host) {
selector_matches = false
}
if (defined(selector.kernel) && selector.kernel != is_kernel) {
selector_matches = false
}
# If the flag stayed set, this is the winner. Since GN's foreach()
# has nothing like a `break` command to bail out of the loop early,
# the best we can do is ignore later matches when builder_toolchain
# is already set.
if (selector_matches && builder_toolchain == false) {
if (defined(target.shlib) && target.shlib &&
defined(selector.shlib_toolchain)) {
builder_toolchain = selector.shlib_toolchain
} else {
builder_toolchain = selector.toolchain
}
}
}
# Validation of the selector list in environment()
# should have made sure there was a catch-all selector.
assert(builder_toolchain != false)
# So that's handy for choosing a non-default variant for a target based on
# build-time configuration (i.e. build arguments like $variants). But
# another handy feature is building both the default target and one or
# more separate variant builds of the same target under a different name
# (i.e. the $output_name gets a per-variant suffix) just by putting the
# suffixed target label into the dependency graph. That is, just use:
# `deps += [ "//some/dir:foobin.foovariant" ]` without worrying about the
# exact toolchain name. Only "//some/dir:foobin" is defined directly.
# When that target name is used, the variant chosen via the selector list
# is installed as "foobin". But there's also a "foobin.$variant" target
# defined that will install the given variant of that binary as
# "foobin.$variant" so that multiple variants can exist side by side in
# the same install directory.
extra_visibility = []
if (target.variant_suffix_target) {
variant_target_name = main_target_name + toolchain.variant_suffix
# To get this ease of use, a lot more goes on under the covers. Firstly,
# each toolchain in the environment must define a "$target_name.$variant"
# target for each *other* variant that just redirects to that toolchain.
foreach(other, toolchain.other_variants) {
other_target_name = main_target_name + other.suffix
extra_visibility += [ ":other_target_name" ]
group(other_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":$other_target_name(${other.label})" ]
}
}
}
# Since the variant-suffixed targets exist and so might be in the
# dependency graph, every toolchain needs to define an actual linking
# target to build this binary. In the chosen builder_toolchain, that same
# binary needs be installed under a different name as well. There's no
# reason to link or compile the same thing twice, so we don't want two
# compiling targets. Instead, one can simply refer to the other and
# change the installed name. That is, either a copy() target to
# physically copy the binary to a different $output_name or a group() that
# simply provides metadata that says where to place the binary in a
# filesystem image. In either case the (physical or virtual) copy target
# has the "real" compiling target in `deps`.
#
# The fly in the ointment with that scheme is the metadata. The metadata
# to build up a filesystem image (e.g. `manifest_lines`) means that if a
# target is in the dependency graph then it will wind up in the image.
# So, say we were to define "foobin.foovariant" as the real compiling
# target, with metadata to install it as "foobin.foovariant"; and "foobin"
# as a group() with `deps = [ ":foobin.foovariant" ]` and metadata to
# install that same binary as "foobin". Now, if the intent is to include
# only "foobin.foovariant" as so only "foobin.foovariant" is in the
# dependency graph, it works out fine. However, more often the intent is
# instead to include only "foobin" in the dependency graph and have only
# "foobin" installed. In that case, since "foobin" depends on
# "foobin.foovariant", the metadata collection finds both and the binary
# winds up installed under both names when that was not requested.
#
# One approach is to use a dependency barrier protocol with the
# get_metadata() calls done for filesystem image construction. That is,
# those calls use `manifest_barrier` as a walk key. Then "foobin" has
# `manifest_barrier = []` in its `metadata` so that the collection picks
# up the metadata to install as "foobin" but stops there. If and only if
# the dependency graph in the get_metadata() call separately reaches
# "foobin.foovariant" will the metadata to install as "foobin.foovariant"
# be seen by that walk. The trouble with this scheme is that it also cuts
# off the `deps` and `data_deps` of "foobin.foovariant" from the metadata
# walk so the runtime files the binary requires (shared libraries,
# resources) are omitted from the filesystem image. Perhaps that could be
# mitigated by putting all those `deps` and `data_deps` from the real
# builder target (expanded to "label_with_toolchain" to get what they
# would be in the builder) into the `manifest_barrier` list (and thus they
# would have to be in the group's `data_deps` too).
#
# Instead, we use an approach that is not specific to any particular
# metadata protocol. The real linking target is yet a third target, an
# internal target called "foobin._build" that's defined *without* the
# `metadata` set by the invoker. Both "foobin" and "foobin.foovariant"
# are just group() targets with `deps = [ ":foobin._build" ]`, but each
# has different metadata.
#
# This internal target is only really required in the chosen builder
# toolchain, since in other toolchains the variant-suffixed target is the
# only one that causes something to be installed rather than just being a
# pure redirect to builder_toolchain. But for consistency and (relative)
# simplicity of this code, we always define it.
builder_target_name = "${main_target_name}._build"
# TODO(docs): NOTE!!! NOTE!!! This whole scheme means that visibility
# lists of deps of terminal targets can't really list individual targets,
# only directory wildcards like ":*". `visibility = [ ":foobin" ]` would
# not allow the "foobin.foovariant" target (or the internal
# "foobin._build" target) to have that dependency. (GN has no general
# wildcards, so ":foobin*" doesn't work. See `gn help label_pattern`.)
extra_visibility += [ ":$main_target_name" ]
if (current_toolchain == builder_toolchain) {
# In the chosen builder toolchain, the main target redirects
# to the builder target but also adds the metadata.
group(main_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":$builder_target_name" ]
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
assert(!defined(manifest_inputs) && !defined(manifest_lines),
"the `manifest_inputs` and `manifest_lines` metadata keys" +
" are reserved for standard templates; use resource()")
}
forward_variables_from(target.main_metadata, "*")
}
}
} else {
# In all other toolchains, the main target just redirects
# to the builder_toolchain (where the metadata lives).
group(main_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":$main_target_name($builder_toolchain)" ]
}
}
# The metadata-defining targets must be defined in the shlib toolchain so
# that their $root_out_dir-based expansions are correct. Both targets in
# the main toolchain just redirect to the shlib toolchain.
if (defined(target.shlib) && target.shlib && defined(toolchain.shlib) &&
current_toolchain != toolchain.shlib) {
if (target.variant_suffix_target) {
group(variant_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":$variant_target_name(${toolchain.shlib})" ]
}
}
not_needed(invoker, "*")
not_needed([
"builder_target_name",
"output_name",
])
} else {
if (target.variant_suffix_target) {
# The variant-suffixed target redirects to the builder target but also
# adds the metadata--different metadata than the main target above.
extra_visibility += [ ":$variant_target_name" ]
group(variant_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":$builder_target_name" ]
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
assert(
!defined(manifest_inputs) && !defined(manifest_lines),
"the `manifest_inputs` and `manifest_lines` metadata keys" +
" are reserved for standard templates; use resource()")
}
forward_variables_from(target.variant_metadata, "*")
}
}
}
# Finally, define the actual builder target--with no metadata.
target(target.type, builder_target_name) {
forward_variables_from(invoker,
"*",
[
"metadata",
"no_implicit_deps",
"output_name",
"target",
"testonly",
"visibility",
])
output_name = output_name
# This might actually be ignored if target.type is "action".
not_needed([ "output_name" ])
forward_variables_from(invoker, [ "testonly" ])
if (defined(invoker.visibility)) {
# Make sure we're visible to the redirector groups.
visibility = invoker.visibility + extra_visibility
}
if (!defined(invoker.no_implicit_deps) || !invoker.no_implicit_deps) {
# Apply the toolchain's implicit_deps. Note the logic here should
# match the ${toolchain.configs} handling in the set_defaults()
# loop at the end of this file.
if (!defined(deps)) {
deps = []
}
foreach(dep, toolchain.implicit_deps) {
# Either it's an absolute label string or it's a scope with filters
# and mutators. See environment().
if (dep == "$dep") {
deps += [ dep ]
} else if (defined(dep.types) && dep.types + [ target.match ] -
[ target.match ] == dep.types) {
# The types list doesn't include this target's type, so skip this
# element.
} else if (defined(configs) && configs + dep.unless_configs -
dep.unless_configs != configs) {
# Something in the unless_configs list is in this target's
# configs, so skip this element.
} else {
deps += dep.add
deps -= dep.remove
}
}
}
}
}
}
}
# Subroutine of executable() and host_tool().
#
# Parameters
#
# target_type
# Required: Caller template name.
# Type: string
#
# Others are as documented for executable().
template("_basic_executable") {
assert(current_toolchain != default_toolchain)
_variant_target(target_name) {
target = {
match = invoker.target_type
type = "executable"
forward_variables_from(invoker,
[
"variant_suffix_metadata",
"variant_suffix_outputs",
])
}
forward_variables_from(invoker,
"*",
[
"metadata",
"sdk_migrated",
"testonly",
"variant_suffix_metadata",
"variant_suffix_outputs",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# Elaborate the toolchain's defaults to compute the output file name.
if (!defined(output_dir)) {
output_dir = target_out_dir
}
if (!defined(output_name)) {
output_name = target_name
}
output_name += toolchain.output_name_suffix
if (!defined(output_extension)) {
output_extension = toolchain.executable_extension
}
output_file_base = "$output_dir/$output_name"
output_file = output_file_base
if (output_extension != "") {
output_file += ".$output_extension"
}
link_output_file = output_file + toolchain.link_output_suffix
metadata = {
binaries = []
# Used by link_output_rspfile(), see its documentation for details.
link_output_barrier = []
link_output_path = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
link_output_path += [ rebase_path(link_output_file, root_build_dir) ]
# For build_api_module("binaries") in //zircon/BUILD.gn.
if (!defined(invoker.sdk_migrated) || !invoker.sdk_migrated) {
binaries += [
{
type = "executable"
label = get_label_info(":$target_name", "label_with_toolchain")
environment = toolchain.environment
cpu = current_cpu
os = current_os
debug = rebase_path(link_output_file, root_build_dir)
if (toolchain.link_output_suffix != "") {
dist = rebase_path(output_file, root_build_dir)
}
if (current_os != "mac" && current_os != "win") {
elf_build_id = rebase_path("${output_file_base}.build-id.stamp",
root_build_dir)
}
if (toolchain.breakpad_syms) {
breakpad = rebase_path("${output_file_base}.sym", root_build_dir)
}
},
]
}
}
if (!is_host && !is_kernel) {
# An explicit `install_path = false` means this binary is not installed.
if (!defined(install_path)) {
install_path = "bin/" + get_path_info(output_file, "file")
}
if (install_path != false) {
target.main_metadata = {
manifest_inputs = [ output_file ]
manifest_lines =
[ "${install_path}=" + rebase_path(output_file, root_build_dir) ]
}
# Also define an alias with the variant suffix. _variant_target will
# make this redirect to the specific variant toolchain chosen for
# this target. In only that toolchain, the metadata will give the
# binary a second install path with the variant suffix.
target.variant_metadata = {
manifest_inputs = [ output_file ]
manifest_lines = [ "${install_path}${toolchain.variant_suffix}=" +
rebase_path(output_file, root_build_dir) ]
}
}
}
}
}
# Build a C/C++ executable to run on the target system.
#
# Parameters
#
# install_path
# Optional: Install path where this executable will appear in a BOOTFS
# or package filesystem image built containing it. Set this to false
# to prevent the file being included in the filesystem image altogether.
# Default: "bin/$output_name"
# Type: string or false
#
# See `gn help executable` for additional parameters and details.
template("executable") {
_basic_executable(target_name) {
target_type = "executable"
forward_variables_from(invoker,
"*",
[
"target_type",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
}
}
# Build a test executable.
#
# test() is like executable() except that it's a separate target type for
# $variants matching, and it always sets $testonly so that dependents must
# also set $testonly.
#
# A test() target aggregates metadata with a data key of "test_runtime_deps",
# which is in turn recorded in the "test_specs" metadata in a "runtime_deps"
# field. See //:tests for more information on that schema.
#
# Parameters
#
# install_path
# Optional: Install path where this test executable will appear in a
# BOOTFS or package filesystem image built containing it. Set this to
# false to prevent the file being included in the filesystem image
# altogether.
# Default: "test/sys/$output_name"
# Type: string or false
#
# disabled
# Optional: A nonempty value indicates that the test is disabled, and the
# value gives the reason.
# Type: string
#
# See executable() for additional parameters and details.
template("test") {
if (is_kernel) {
# test() can appear in kernel toolchain contexts only because it's used in
# the BUILD.gn files for some libraries that are shared between user and
# kernel. The test() can only be instantiated in the user environment.
# In the kernel environment, it's just silently ignored.
not_needed(invoker, "*")
not_needed([ "target_name" ])
} else {
additional_deps = []
if (is_host) {
runtime_deps_target_name = "_test.$target_name.runtime_deps"
runtime_deps_file = "$target_gen_dir/$target_name.deps.json"
generated_file(runtime_deps_target_name) {
testonly = true
data_keys = [ "test_runtime_deps" ]
output_conversion = "json"
rebase = root_build_dir
outputs = [ runtime_deps_file ]
forward_variables_from(invoker,
[
"deps",
"data_deps",
])
}
additional_deps += [ ":$runtime_deps_target_name" ]
}
_basic_executable(target_name) {
target_type = "test"
testonly = true
forward_variables_from(invoker,
"*",
[
"disabled",
"metadata",
"target_type",
"testonly",
"visibility",
])
forward_variables_from(invoker, [ "visibility" ])
if (!defined(output_name)) {
output_name = "${target_name}-test"
}
output_name += toolchain.output_name_suffix
if (!defined(output_extension)) {
output_extension = toolchain.executable_extension
}
if (!defined(output_dir)) {
output_dir = target_out_dir
}
if (!is_host && !defined(install_path)) {
install_path = "test/sys/$output_name"
if (output_extension != "") {
install_path += ".$output_extension"
}
}
if (!defined(data_deps)) {
data_deps = []
}
data_deps += additional_deps
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
tests = [
{
test = {
name = get_label_info(":$target_name", "name")
label = get_label_info(":$target_name", "label_with_toolchain")
cpu = current_cpu
os = current_os
if (is_host) {
path = rebase_path("$output_dir/$output_name", root_build_dir)
} else {
# Inside of a package, this will actually be a package-relative
# path.
path = install_path
}
forward_variables_from(invoker, [ "disabled" ])
if (defined(runtime_deps_file)) {
runtime_deps = rebase_path(runtime_deps_file, root_build_dir)
}
}
},
]
}
}
}
}
# Build a C/C++ executable for a "host-style" command-line tool.
#
# A host_tool() target can be the $tool parameter to host_tool_action().
# If it's a dependency of //zircon/tools:tools then it will be included
# in SDK builds.
#
# A host_tool() target might also be built for the Fuchsia target system,
# if it's compatible. When built for Fuchsia, a host_tool() automatically
# depends on fdio so the POSIX-like environment common to command-line tools
# built for other systems like Linux and Mac will always be available. It's
# otherwise just like executable().
#
# Parameters are the same as for executable(), with the addtion of
# `sdk_migrated` used for build unification (see `library` documentation).
#
template("host_tool") {
assert(!is_kernel, "host_tool() targets don't work in kernel toolchains")
not_needed(invoker, [ "sdk_migrated" ])
_basic_executable(target_name) {
target_type = "host_tool"
forward_variables_from(invoker,
"*",
[
"target_type",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
if (!defined(output_dir)) {
output_dir = target_out_dir
}
if (!defined(output_name)) {
output_name = target_name
}
output_name += toolchain.output_name_suffix
if (!defined(output_extension)) {
output_extension = toolchain.executable_extension
}
if (is_host) {
tool_executable = "$output_dir/$output_name"
tool_extension = ""
if (output_extension != "") {
tool_extension = ".$output_extension"
}
sdk_migrated = defined(invoker.sdk_migrated) && invoker.sdk_migrated
# The stripped file is what goes into `tool_executables` to drive
# the installation copies.
install_file = rebase_path(
get_path_info(tool_executable, "dir") + "/" +
get_path_info(tool_executable, "file") + tool_extension,
root_build_dir)
# The unstripped file is what we actually run, so that compiled-in
# sanitizer code can symbolize itself.
run_file = install_file + toolchain.link_output_suffix
metadata = {
# List of tool binaries that could be presented to users' command line.
tool_executables = [ install_file ]
# For the //:tool_paths build_api_module().
if (!sdk_migrated) {
tool_paths = [
{
cpu = current_cpu
label = get_label_info(":$target_name", "label_with_toolchain")
name = output_name
os = current_os
path = run_file
},
]
}
# See host_tool_action().
host_tool_barrier = []
host_tool_rspfile = [
run_file,
"--",
]
if (defined(toolchain.host_run_env)) {
# The variant can specify a wrapper prefix for running the tool,
# usually ["/usr/bin/env", "VAR=VAL"].
host_tool_rspfile += toolchain.host_run_env
}
host_tool_rspfile += [ run_file ]
# TODO(fxbug.dev/3156): Temporary hacks for integrating with the legacy
# Fuchsia GN build.
if (!sdk_migrated) {
legacy_barrier = []
legacy_dirs = [ "tool/$target_name" ]
legacy_targets = [
{
_label = get_label_info(":$target_name", "label_with_toolchain")
_zircon_public = "tool"
import = "//build/zircon/zircon_host_tool.gni"
target_type = "zircon_host_tool"
target_name = target_name
path = install_file
},
]
}
}
variant_suffix_outputs = [ tool_executable + tool_extension ]
variant_suffix_metadata = {
tool_executables = variant_suffix_outputs
}
}
}
}
# Subroutine of loadable_module() and driver().
template("_loadable_module") {
assert(!is_kernel)
assert(!is_host)
_variant_target(target_name) {
# This is for _variant_target().
target = {
shlib = true
match = invoker.match
type = "_shlib_toolchain_target"
}
# This is for _shlib_toolchain_target().
target_type = "loadable_module"
match = target.match
forward_variables_from(invoker,
"*",
[
"match",
"metadata",
"sdk_migrated",
"target_type",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# Elaborate the toolchain's defaults to compute the output file name.
if (!defined(output_dir)) {
output_dir = target_out_dir
}
if (!defined(output_name)) {
output_name = target_name
}
output_name += toolchain.output_name_suffix
if (!defined(output_extension)) {
output_extension = "so"
}
output_file_base = "$output_dir/$output_name"
output_file = output_file_base
if (output_extension != "") {
output_file += ".$output_extension"
}
link_output_file = output_file + toolchain.link_output_suffix
metadata = {
binaries = []
# Used by link_output_rspfile(), see its documentation for details.
link_output_barrier = []
link_output_path = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
link_output_path += [ rebase_path(link_output_file, root_build_dir) ]
if (!defined(invoker.sdk_migrated) || !invoker.sdk_migrated) {
# For build_api_module("binaries") in //zircon/BUILD.gn.
binaries += [
{
type = "loadable_module"
label = get_label_info(":$target_name", "label_with_toolchain")
environment = toolchain.environment
cpu = current_cpu
os = current_os
debug = rebase_path(link_output_file, root_build_dir)
if (toolchain.link_output_suffix != "") {
dist = rebase_path(output_file, root_build_dir)
}
if (current_os != "mac" && current_os != "win") {
elf_build_id = rebase_path("${output_file_base}.build-id.stamp",
root_build_dir)
}
if (toolchain.breakpad_syms) {
breakpad = rebase_path("${output_file_base}.sym", root_build_dir)
}
},
]
}
}
if (defined(invoker.install_path)) {
target.main_metadata = {
manifest_inputs = [ output_file ]
manifest_lines =
[ "${install_path}=" + rebase_path(output_file, root_build_dir) ]
}
# Also define an alias with the variant suffix. _variant_target will
# make this redirect to the specific variant toolchain chosen for
# this target. In only that toolchain, the metadata will give the
# binary a second install path with the variant suffix.
extension = get_path_info(invoker.install_path, "extension")
if (extension != "") {
extension = ".$extension"
}
target.variant_metadata = {
manifest_inputs = [ output_file ]
manifest_lines = [ get_path_info(invoker.install_path, "dir") + "/" +
get_path_info(invoker.install_path, "name") +
toolchain.variant_suffix + extension + "=" +
rebase_path(output_file, root_build_dir) ]
}
}
}
}
# Build a C/C++ loadable module to run on the target system.
#
# Parameters
#
# install_path
# Optional: If false (the default), nothing happens--the binary won't
# be installed anywhere automatically. Set this to a string containing
# the install path where this module will appear in a BOOTFS
# or package filesystem image built containing it.
# Default: false
# Type: string or false
#
# See `gn help loadable_module` for additional parameters and details.
template("loadable_module") {
_loadable_module(target_name) {
match = "loadable_module"
forward_variables_from(invoker,
"*",
[
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
}
}
# Build a Zircon driver module.
#
# A driver() target is like a loadable_module() target but with special
# defaults. It takes all the same parameters as loadable_module().
# Drivers automatically depend on the driver ABI library and use configs
# appropriate for drivers, including the static C++ standard library.
# Drivers are prohibited from depending on arbitrary shared libraries.
#
# Parameters
#
# deprecated_inhibit_driver_shlib_allowlist
# Optional: TODO(fxbug.dev/32708): If set (must be true), disable the
# shared library deps policy check for this driver. Remove this ASAP!
#
# install_path
# Optional: Where to install the driver in the BOOTFS.
# Default: "driver/${output_name}.so"
# Type: string or false
#
# See loadable_module() for others
#
template("driver") {
_loadable_module(target_name) {
match = "driver"
data_deps = []
deps = []
# Save the target name here as it will be overridden by the call to
# `forward_variables_from`.
original_target_name = target_name
forward_variables_from(invoker,
"*",
[
"testonly",
"visibility",
# TODO(fxbug.dev/32708): Remove this ASAP!
"deprecated_inhibit_driver_shlib_allowlist",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# All drivers implicitly get the driver ABI.
deps += [ "//zircon/system/ulib/driver" ]
# Elaborate the defaults to compute the output file name.
if (!defined(output_name)) {
output_name = original_target_name
}
# Set the standard install_path.
if (!defined(install_path)) {
install_path = "driver/${output_name}.so"
}
# Enforce the shared library allowlist.
deps += [ ":_driver.shlib-denylist-check.$original_target_name" ]
}
driver_label = get_label_info(":$target_name", "label_with_toolchain")
driver_target = target_name
check_target = "_driver.shlib-denylist-check.$target_name"
denylist_target = "_driver.shlib-denylist.$target_name"
denylist_file = "$target_gen_dir/$target_name.driver-shlib-denylist"
# It's an error to link against any shared library that's not on the
# allowlist. Those libraries set `driver_shlib_denylist=[]` while others (by
# default, see shared_library() above) set `driver_shlib_denylist=[label]`.
# Hence the file generated here should be empty.
generated_file(denylist_target) {
forward_variables_from(invoker,
[
"deps",
"public_deps",
"testonly",
])
# TODO(fxbug.dev/32708): Remove this ASAP.
if (defined(invoker.deprecated_inhibit_driver_shlib_allowlist)) {
assert(invoker.deprecated_inhibit_driver_shlib_allowlist,
"omit deprecated_inhibit_driver_shlib_allowlist unless true")
deps = []
public_deps = []
}
visibility = [ ":$check_target" ]
data_keys = [ "driver_shlib_denylist" ]
walk_keys = [ "driver_shlib_denylist_barrier" ]
output_conversion = "list lines"
outputs = [ denylist_file ]
}
# GN doesn't provide a way to assert it was empty at gen time.
# So use a script to check it and give good diagnostics.
# TODO(mcgrathr): Give GN an assert_no_metadata feature for this.
# Maybe set `output_conversion="empty"` in generated_file().
action(check_target) {
# For prebuilt_dir:
import("//zircon/public/gn/prebuilt.gni")
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${driver_target}._build($current_toolchain)" ]
script = "//zircon/public/gn/driver-denylist-check.sh"
deps = [ ":$denylist_target" ]
sources = [ denylist_file ]
outputs = [ "${denylist_file}.stamp" ]
args = [ driver_label ] +
rebase_path(sources + outputs + [
"$prebuilt_dir/third_party/gn/$host_platform/gn",
"//zircon/",
],
root_build_dir)
}
}
# Build a driver() target meant only for testing.
#
# This is just like driver() with $testonly set to true and with
# $install_path set to place the driver under driver/test/ rather than
# driver/ (the default).
#
# Parameters
#
# install_path
# Optional: Where to install the driver in the BOOTFS.
# Default: "driver/test/${output_name}.so"
# Type: string or false
#
# See driver().
#
template("test_driver") {
driver(target_name) {
forward_variables_from(invoker,
"*",
[
"install_path",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
if (defined(testonly)) {
assert(testonly, "Test drivers can only be marked as testonly")
} else {
testonly = true
}
# Elaborate the defaults to compute the output file name.
if (!defined(output_name)) {
output_name = target_name
}
install_path = "driver/test/${output_name}.so"
}
}
###
### Unification kludges.
###
###
### set_defaults()
###
# These are all the target types (both stock GN and Fuchsia templates) that
# compile `sources` with the $current_toolchain tools. All these get a
# default `configs` below, so targets use `configs +=` and `configs -=`.
#
# Note that because set_defaults() can only be used in BUILDCONFIG.gn, all
# these templates must be defined in this one file rather than in separate
# .gni files explicitly referenced with import(). So because of this
# constraint in GN, the rule is that target types that pre-set $configs are
# defined in BUILDCONFIG.gn while other templates are defined in their own
# //zircon/public/gn/${target_type}.gni file.
_compile_target_types = [
"executable",
"host_tool",
"library",
"loadable_module",
"shared_library",
"source_set",
"static_library",
"test",
]
_unification_wrapper_types = [
"executable",
"host_tool",
"library",
"test",
]
if (toolchain.environment == "user") {
_compile_target_types += [
"driver",
"test_driver",
]
_unification_wrapper_types += [
"driver",
"test_driver",
]
}
# Apply set_defaults($target_name) according to ${toolchain.configs}.
#
# See environment() for the rules of the ${toolchain.configs} scope.
# This template takes no parameters. The $target_name is taken to be
# the target type as in set_default().
#
# ${toolchain.configs} gives the initial `configs` set for every compile
# target in this toolchain. Targets use `+=` rather than `=` unless they
# are explicitly doing `= []` to remove all the toolchain defaults; they
# can use `-=` to remove specific members from the default set.
foreach(target_type, _compile_target_types) {
foreach(unification_hack,
[
false,
true,
]) {
if (unification_hack) {
# On the second run, apply to the unification wrapper template whose
# name is the target type with the "zx_" prefix.
if (_unification_wrapper_types + [ target_type ] - [ target_type ] ==
_unification_wrapper_types) {
# Not in the list. Skip this iteration.
unification_hack = ""
} else {
# Also apply set_defaults() to the wrapper template, but use the
# real target_type for `types` list matching.
unification_hack = "zx_$target_type"
}
} else {
unification_hack = target_type
}
if (unification_hack != "") {
set_defaults(unification_hack) {
configs = []
foreach(config, toolchain.configs) {
# Either it's an absolute label string or it's a scope with filters
# and mutators. See environment().
if (config == "$config") {
configs += [ config ]
} else if (!defined(config.types) ||
config.types + [ target_type ] - [ target_type ] !=
config.types) {
configs += config.add
configs -= config.remove
}
}
}
}
}
}
# For prebuilt_dir:
import("//zircon/public/gn/prebuilt.gni")
# Variables used to refer to the in-tree python interpreter.
python_version = "3.8"
python_exe_src = "$prebuilt_dir/third_party/python3/${host_platform}/bin/python${python_version}"
foreach(_target_type,
[
"action",
"action_foreach",
]) {
template(_target_type) {
target(_target_type, target_name) {
forward_variables_from(invoker,
"*",
[
"args",
"script",
"testonly",
"visibility",
])
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# If the script is Python, use the in-tree interpreter.
# This prevents the script from relying on the Python that happens to
# be in the PATH, making the build more hermetic.
if (get_path_info(invoker.script, "extension") == "py") {
args = [ rebase_path(invoker.script, root_build_dir) ]
if (defined(invoker.args)) {
args += invoker.args
}
script = python_exe_src
if (!defined(inputs)) {
inputs = []
}
inputs += [ invoker.script ]
} else {
forward_variables_from(invoker,
[
"args",
"script",
])
}
}
}
}