blob: 538f72ed3c342d0a8ccf702b7003cad037676a86 [file] [log] [blame]
# Copyright 2016 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.
import("//build/dart/config.gni")
declare_args() {
# Debug build.
is_debug = true
}
if (target_os == "") {
target_os = "fuchsia"
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
host_platform = "${host_os}-${host_cpu}"
if (target_os == "fuchsia") {
target_toolchain = "//build/toolchain/fuchsia:${target_cpu}"
} else {
assert(false, "Target OS not supported")
}
if (host_os == "linux" || host_os == "mac") {
host_toolchain = "//build/toolchain:host_${host_cpu}"
} else {
assert(false, "Host OS not supported")
}
set_default_toolchain(target_toolchain)
# Some projects expect a default value for sources_assignment_filter.
sources_assignment_filter = []
declare_args() {
# This should never be set as a build argument. It exists only to be
# set in toolchain_args when defining a toolchain. These configs
# will be appended to the default list common to all target types.
toolchain_configs = []
# This should never be set as a build argument.
# It exists only to be set in toolchain_args.
# See //build/toolchain/clang_toolchain.gni for details.
is_pic_default = false
# This should never be set as a build argument.
# It exists only to be set in toolchain_args.
# See //build/toolchain/clang_toolchain.gni for details.
toolchain_variant = []
# This should never be set as a build argument.
# It exists only to be set in toolchain_args.
# See //build/toolchain/clang_toolchain.gni for details.
current_base_toolchain = target_toolchain
# Zircon projects to target - must match the target architecture.
zircon_projects = []
# DEPRECATED: zircon build project
# Use zircon_projects instead
zircon_project = ""
}
if (zircon_project != "") {
assert(zircon_projects == [], "Use zircon_projects instead of zircon_project")
zircon_projects = [ zircon_project ]
} else if (zircon_projects == []) {
if (current_cpu == "arm64") {
zircon_projects = [ "zircon-qemu-arm64" ]
} else if (current_cpu == "x64") {
zircon_projects = [ "zircon-pc-x86-64" ]
}
}
if (target_cpu == "x64") {
zircon_sysroot =
rebase_path("//out/build-zircon/build-zircon-pc-x86-64/sysroot")
} else {
assert(target_cpu == "arm64", "Unsupported target architecture $target_cpu")
zircon_sysroot =
rebase_path("//out/build-zircon/build-zircon-qemu-arm64/sysroot")
}
is_android = false
is_fuchsia = false
is_fuchsia_host = false
is_ios = false
is_linux = false
is_mac = false
is_win = false
is_clang = true
is_component_build = false
is_official_build = false
if (current_os == "fuchsia") {
is_fuchsia = true
} else if (current_os == "linux") {
is_linux = true
is_fuchsia_host = true
} else if (current_os == "mac") {
is_mac = true
is_fuchsia_host = true
}
# Each variant toolchain's name is formed by taking the base toolchain
# name and successively appending "-$name" for each config listed in
# the variant, where name is `get_label_name(config, "name")`.
# NOTE: This must match the method used in
# //build/toolchain/clang_toolchain.gni:clang_toolchain_suite
current_toolchain_variant = ""
foreach(subvariant, toolchain_variant) {
current_toolchain_variant += "-" + get_label_info(subvariant, "name")
}
# When we are in a variant of host_toolchain, change the value of
# host_toolchain so that `if (current_toolchain == host_toolchain)`
# tests still match, since that is the conventional way to detect being
# in host context. This means that any "...($host_toolchain)" label
# references from inside a variant of host_toolchain will refer to the
# variant (current_toolchain rather than host_toolchain). To handle
# this, the executable() template below will define its target in other
# variant toolchains as a copy of the real executable.
if (is_fuchsia_host) {
host_toolchain += current_toolchain_variant
}
# References should use "label($shlib_toolchain)" rather than
# "label(${target_toolchain}-shared)" or anything else.
shlib_toolchain = "${current_base_toolchain}${current_toolchain_variant}-shared"
# All binary targets will get this list of configs by default.
default_common_binary_configs = [
"//build/config:compiler",
"//build/config:relative_paths",
"//build/config:default_include_dirs",
"//build/config:default_warnings",
"//build/config:no_exceptions",
"//build/config:no_rtti",
"//build/config:symbol_visibility_hidden",
]
if (is_debug) {
default_common_binary_configs += [ "//build/config:debug" ]
} else {
default_common_binary_configs += [ "//build/config:release" ]
}
if (is_fuchsia) {
default_common_binary_configs += [
"//build/config/fuchsia:icf",
"//build/config/fuchsia:thread_safety_annotations",
"//build/config/fuchsia:werror",
# TODO(mcgrathr): Perhaps restrict this to only affected code.
# For now, safest to do it everywhere.
"//build/config/fuchsia:zircon_asserts",
]
# Add safestack when not using a variant that's incompatible.
# TODO(mcgrathr): When -fsanitize=safe-stack is the default in the
# compiler, this will not be necessary: -fsanitize=address et al will
# implicitly disable safestack.
_safestack_compatible = true
foreach(subvariant, toolchain_variant) {
if (subvariant == "//build/config/sanitizers:asan" ||
subvariant == "//build/config/sanitizers:asan_no_detect_leaks" ||
subvariant == "//build/config/sanitizers:sancov") {
_safestack_compatible = false
}
}
if (_safestack_compatible) {
default_common_binary_configs += [ "//build/config/fuchsia:safestack" ]
}
}
default_common_binary_configs += [ "//build/config/lto:default" ]
# Add configs set by the toolchain definition.
default_common_binary_configs += toolchain_configs + toolchain_variant
default_shared_library_configs =
default_common_binary_configs + [ "//build/config:shared_library_config" ]
# On most systems, the sanitizer runtimes are normally linked statically
# and so -shared links do not include them. Using -shared --no-undefined
# with sanitized code will get undefined references for the sanitizer
# runtime calls generated by the compiler. Assuming that variants are
# likely sanitizers prone to this issue, avoid that config for variants.
# It shouldn't do much harm, since the non-variant builds will catch the
# real undefined reference bugs.
if (toolchain_variant == []) {
default_shared_library_configs += [ "//build/config:symbol_no_undefined" ]
}
default_executable_configs = default_common_binary_configs + [
"//build/config:executable_config",
"//build/config:default_libs",
]
if (is_pic_default) {
default_common_binary_configs += [ "//build/config:shared_library_config" ]
}
# Apply that default list to the binary target types.
set_defaults("source_set") {
configs = default_common_binary_configs
}
set_defaults("static_library") {
configs = default_common_binary_configs
}
set_defaults("shared_library") {
configs = default_shared_library_configs
}
set_defaults("loadable_module") {
configs = default_shared_library_configs
}
set_defaults("executable") {
configs = default_executable_configs
}
if (is_fuchsia) {
if (!is_pic_default) {
# In the main toolchain, shared_library just redirects to the same
# target in the -shared toolchain.
template("shared_library") {
group(target_name) {
# Mark all variables as not needed to suppress errors for unused
# variables. The other variables normally passed to shared_library
# are actually used by the shared_library instantiation in the
# -shared toolchain, so any going truly unused will be caught there.
not_needed(invoker, "*")
forward_variables_from(invoker,
[
"data_deps",
"deps",
"public_deps",
"all_dependent_configs",
"public_configs",
"testonly",
"visibility",
])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":$target_name(${current_toolchain}-shared)" ]
}
}
} else {
# In the -shared toolchain, shared_library is just its normal self,
# but if the invoker constrained the visibility, we must make sure
# the dependency from the main toolchain is still allowed.
template("shared_library") {
shared_library(target_name) {
# Explicitly forward visibility, implicitly forward everything
# else. Forwarding "*" doesn't recurse into nested scopes (to
# avoid copying all globals into each template invocation), so
# won't pick up file-scoped variables. Normally this isn't too
# bad, but visibility is commonly defined at the file scope.
# Explicitly forwarding visibility and then excluding it from the
# "*" set works around this problem. See http://crbug.com/594610
# for rationale on why this GN behavior is not considered a bug.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker, "*", [ "visibility" ])
if (defined(visibility)) {
visibility += [ ":$target_name" ]
}
}
}
}
}
declare_args() {
# List of "selectors" to request variant builds of certain targets.
# Each selector specifies matching criteria and a chosen variant.
# The first selector in the list to match a given target determines
# which variant is used for that target. Each selector is a scope,
# defining some of these:
#
# variant
# Required: The variant that applies if this selector matches.
# This can be false to choose no variant, or a string that names
# the variant. Variant names are composed of the names of configs
# that make up the variant, separated by "-" if there is more than
# one config. `known_variants + extra_variants` is the set of
# available variants; see those build arguments.
#
# The rest below are matching criteria.
# The selector matches if and only if all of its criteria match.
# If none of these is defined, then the selector always matches.
# Each "Strings" criterion is a list of strings, and the criterion
# is satisfied if any of the strings match against the candidate string.
#
# host
# Boolean: If true, the selector matches in the host toolchain.
# If false, the selector matches in the target toolchain.
#
# testonly
# Boolean: If true, the selector matches targets with testonly=true.
# If false, the selector matches in targets without testonly=true.
#
# target_type
# Strings: Matches "executable", "loadable_module", or "driver_module"
#
# output_name
# Strings: Matches the target's `output_name` (default: its target name).
#
# label
# Strings: Matches the target's full label (without toolchain).
#
# name
# Strings: Matches the target's simple name (label after last / or :).
#
# dir
# Strings: Matches target's label directory.
select_variant = []
}
template("variant_target") {
target_type = target_name
target_name = invoker.target_name
target_invoker = {
# Explicitly forward visibility, implicitly forward everything else.
# See comment in template("shared_library") above for details.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker,
"*",
[
"_target_type",
"target_name",
"visibility",
])
if (!defined(output_name)) {
output_name = target_name
}
}
# target_type is the real GN target type that builds the thing.
# selector_target_type is the name matched against target_type selectors.
if (defined(invoker._target_type)) {
selector_target_type = invoker._target_type
} else {
selector_target_type = target_type
}
target_label = get_label_info(":$target_name", "label_no_toolchain")
# These are not actually used in all possible if branches below,
# so defang GN's extremely sensitive "unused variable" errors.
not_needed([
"selector_target_type",
"target_invoker",
"target_label",
"target_type",
])
target_variant = false
if (select_variant != []) {
# See if there is a selector that matches this target.
selected = false
foreach(selector, select_variant) {
# The first match wins.
# GN's loops don't have "break", so do nothing on later iterations.
if (!selected) {
# Expand the selector so we don't have to do a lot of defined(...)
# tests below.
select = {
}
select = {
target_type = []
output_name = []
label = []
name = []
dir = []
forward_variables_from(selector, "*")
}
selected = true
if (selected && defined(selector.host)) {
selected = is_fuchsia_host == selector.host
}
if (selected && defined(selector.testonly)) {
selected = (defined(target_invoker.testonly) &&
target_invoker.testonly) == selector.testonly
}
if (selected && select.target_type != []) {
selected = false
candidate = selector_target_type
foreach(try, select.target_type) {
if (try == candidate) {
selected = true
}
}
}
if (selected && select.output_name != []) {
selected = false
candidate = target_invoker.output_name
foreach(try, select.output_name) {
if (try == candidate) {
selected = true
}
}
}
if (selected && select.label != []) {
selected = false
candidate = target_label
foreach(try, select.label) {
if (try == candidate) {
selected = true
}
}
}
if (selected && select.name != []) {
selected = false
candidate = get_label_info(target_label, "name")
foreach(try, select.name) {
if (try == candidate) {
selected = true
}
}
}
if (selected && select.dir != []) {
selected = false
candidate = get_label_info(target_label, "dir")
foreach(try, select.dir) {
if (try == candidate) {
selected = true
}
}
}
if (selected && selector.variant != false) {
target_variant = "-${selector.variant}"
}
}
}
}
if (target_variant == false) {
target_variant = ""
}
builder_toolchain = current_base_toolchain + target_variant
if (invoker._variant_shared) {
builder_toolchain += "-shared"
}
if (current_toolchain == builder_toolchain) {
# This is the toolchain selected to actually build this target.
target(target_type, target_name) {
deps = []
forward_variables_from(target_invoker, "*")
foreach(variant, toolchain_variant) {
# Expand the label so it always has a :name part.
variant = get_label_info(variant, "label_no_toolchain")
deps += [ "${variant}_deps" ]
}
if (defined(visibility)) {
# Other toolchains will define this target as a group or copy
# rule that depends on this toolchain's definition. If the
# invoker constrained the visibility, make sure those
# dependencies from other toolchains are still allowed.
visibility += [ ":${target_name}" ]
}
}
} else if (current_toolchain == shlib_toolchain) {
# Don't copy from a variant into a -shared toolchain, because nobody
# looks for an executable or loadable_module there. Instead, just
# forward any deps to the real target.
group(target_name) {
forward_variables_from(target_invoker,
[
"testonly",
"visibility",
])
if (defined(visibility)) {
visibility += [ ":${target_name}" ]
}
deps = [
":${target_name}(${builder_toolchain})",
]
}
} else {
# When some variant was selected, then this target in all other
# toolchains is actually just this copy rule. The target is built in
# the selected variant toolchain, but then copied to its usual name in
# $root_out_dir so that things can find it there.
copy_vars = {
forward_variables_from(target_invoker,
[
"testonly",
"visibility",
])
if (defined(visibility)) {
visibility += [ ":${target_name}" ]
}
deps = [
":${target_name}(${builder_toolchain})",
]
variant_out_dir = get_label_info(deps[0], "root_out_dir")
full_output_name = target_invoker.output_name
if (defined(target_invoker.output_extension) &&
target_invoker.output_extension != "") {
full_output_name += ".${target_invoker.output_extension}"
}
sources = [
"$variant_out_dir/$full_output_name",
]
outputs = [
"$root_out_dir/$full_output_name",
]
}
# In the host toolchain, make a symlink rather than a hard link
# (which is what "copy" rules really do). Host tools are built with
# an embedded shared library lookup path based on $ORIGIN on Linux
# (//build/config/linux:compiler) and the equivalent @loader_path on
# macOS (//build/config/mac:mac_dynamic_flags). The dynamic linker
# translates this to "the directory containing the executable".
# With hard links, this gets the directory used to invoke the
# executable, which is host_toolchain's $root_out_dir. With
# symlinks, it instead gets the directory containing the actual
# executable file, which is builder_toolchain's $root_out_dir.
# Hence the program uses the variant builds of shared libraries that
# go with the variant build of the executable, rather using than the
# vanilla host_toolchain builds with the variant executable.
if (current_toolchain == host_toolchain) {
action(target_name) {
forward_variables_from(copy_vars, "*")
script = "/bin/ln"
args = [
"-snf",
rebase_path(sources[0], root_out_dir),
rebase_path(outputs[0]),
]
}
} else {
# For Fuchsia, //packages/gn/variant.py depends on hard links to
# identify the variants.
copy(target_name) {
forward_variables_from(copy_vars, "*")
}
}
}
}
template("executable") {
_executable_name = target_name
_variant_shared = false
variant_target("executable") {
target_name = _executable_name
# Explicitly forward visibility, implicitly forward everything else.
# See comment in template("shared_library") above for details.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker, "*", [ "visibility" ])
}
}
template("loadable_module") {
_module_name = target_name
_variant_shared = true
variant_target("loadable_module") {
target_name = _module_name
if (defined(invoker._target_type)) {
_target_type = invoker._target_type
}
# Explicitly forward visibility, implicitly forward everything else.
# See comment in template("shared_library") above for details.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker, "*", [ "visibility" ])
if (!defined(output_extension)) {
output_extension = "so"
}
}
}
# Some targets we share with Chromium declare themselves to be components,
# which means they can build either as shared libraries or as static libraries.
# We build them as static libraries.
template("component") {
if (!defined(invoker.sources)) {
# When there are no sources defined, use a source set to avoid creating
# an empty static library (which generally don't work).
_component_mode = "source_set"
} else {
_component_mode = "static_library"
}
target(_component_mode, target_name) {
# Explicitly forward visibility, implicitly forward everything else.
# See comment in template("shared_library") above for details.
forward_variables_from(invoker, [ "visibility" ])
forward_variables_from(invoker, "*", [ "visibility" ])
}
}
set_defaults("component") {
configs = default_common_binary_configs
}