| # 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. |
| |
| assert(!defined(current_toolchain), "only for buildconfig context") |
| |
| declare_args() { |
| # *This must never be set as a build argument*. |
| # |
| # "$zx/" is the prefix for GN "source-absolute" paths in the Zircon |
| # build. When Zircon is built standalone, the Zircon repository is the |
| # root of the build (where `.gn` is found) so "$zx/" becomes "//". When |
| # Zircon is part of a larger unified build, there is a higher-level `.gn` |
| # file that uses `default_args` to set "$zx/" to "//zircon/". |
| zx = "/" |
| } |
| |
| declare_args() { |
| # *This must never be set as a build argument.* |
| # It exists only to be set via define_toolchain(). |
| # See define_environment() for more information. |
| toolchain = { |
| configs = [] |
| environment = "stub" |
| label = "$zx/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 define_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 |
| } |
| |
| # Shorthand for `current_os == "fuchsia"`. |
| is_fuchsia = current_os == "fuchsia" |
| |
| # Shorthand for `current_os == "linux"`. |
| is_linux = current_os == "linux" |
| |
| # Shorthand for `current_os == "mac"`. |
| is_mac = current_os == "mac" |
| |
| 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 |
| } |
| |
| ### |
| ### config() revamp |
| ### |
| |
| # Assert in a template that its invoker doesn't use legacy configs. |
| # |
| # Always invoked: `assert_no_legacy_configs(target_name) { type = "..." }` |
| # |
| # Parameters |
| # |
| # invoker |
| # Required: Implicitly set in every template() scope. |
| # |
| # type |
| # Required: The name of the calling template(). |
| # |
| template("assert_no_legacy_configs") { |
| label = get_label_info(":$target_name", "label_no_toolchain") |
| target_invoker = invoker.invoker |
| type = invoker.type |
| assert(!defined(target_invoker.all_dependent_configs), |
| "all_dependent_configs is not supported in this build") |
| assert(!defined(target_invoker.public_configs), |
| "use public_deps instead of public_configs in $label $type()") |
| not_needed([ "target_invoker" ]) |
| } |
| |
| # This is used exclusively by the config() template, below. |
| template("_raw_config") { |
| config(target_name) { |
| forward_variables_from(invoker, "*") |
| } |
| } |
| |
| # Propagate switch settings and dependencies to dependents. |
| # |
| # This template replaces GN's built-in config() with enhanced and |
| # simplified semantics. A config() is a target that acts like a group() |
| # target in all respects. But it can also set all the switches and have |
| # `inputs` and `libs` like legacy config(). |
| |
| # By convention, a config() is usually referred to in `configs`, but in |
| # fact all the compiling target types are templates that just merge |
| # `configs` into `deps` and putting a config() label into `deps` works just |
| # the same. The main reason for the convention is just that `configs` is |
| # usually predefined with a default list via set_defaults() so `configs -= |
| # ...` can remove default elements, while `deps` is not. |
| # |
| # A config() itself never sets `configs` or `public_configs` like in legacy |
| # config(). Instead use `public_deps` for another config() that should be |
| # propagated to users of this config(). |
| # |
| # Parameters |
| # |
| # compiler_flags |
| # Optional: This is appended to all of $asmflags, $cflags, and $ldflags. |
| # It's a convenient shorthand for flags that the compiler driver treats |
| # the same way for assembling, compiling, and linking tasks. |
| # |
| # data_deps |
| # deps |
| # Optional: As for group(). This is *not* the way to refer to another |
| # config() so as to propagate its switch settings; instead use |
| # `public_deps` for that. Note that potentially each individual file |
| # compiled with the switches of this config() will also have a |
| # Ninja dependency arc to each target in `data_deps` or `deps`. Hence |
| # this should be used sparingly. |
| # |
| # public_deps |
| # Optional: As for group(). This is the way to refer to other |
| # config() targets. Every dependent of this target will also use |
| # the switches of the config() targets in `public_deps`. |
| # |
| template("config") { |
| _config_name = target_name |
| _config_label = get_label_info(":$target_name", "label_no_toolchain") |
| |
| # The old ways are now taboo. |
| assert(!defined(invoker.configs), |
| "use public_deps instead of configs in $_config_label config()") |
| assert(!defined(invoker.public_configs), |
| "use public_deps instead of public_configs in $_config_label config()") |
| |
| # The underlying config() holds everything but the deps. |
| _raw_config("_config.$_config_name") { |
| visibility = [ ":$_config_name" ] |
| asmflags = [] |
| cflags = [] |
| ldflags = [] |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "compiler_flags", |
| "data_deps", |
| "deps", |
| "metadata", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (defined(invoker.compiler_flags)) { |
| asmflags += invoker.compiler_flags |
| cflags += invoker.compiler_flags |
| ldflags += invoker.compiler_flags |
| } |
| } |
| |
| group(_config_name) { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "deps", |
| "metadata", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| public_configs = [ ":_config.$_config_name" ] |
| |
| metadata = { |
| forward_variables_from(invoker, |
| [ |
| "ldflags", |
| "lib_dirs", |
| "libs", |
| ]) |
| if (defined(invoker.compiler_flags)) { |
| if (!defined(ldflags)) { |
| ldflags = [] |
| } |
| ldflags += invoker.compiler_flags |
| } |
| } |
| } |
| } |
| |
| ### |
| ### "Non-terminal" (library and source_set) target types. |
| ### These are compiling targets that never do variant selection. |
| ### |
| |
| # Just wrap these to inject assert_no_legacy_configs() |
| # and translate configs to deps. |
| foreach(target_type, |
| [ |
| "source_set", |
| "static_library", |
| ]) { |
| template(target_type) { |
| assert_no_legacy_configs(target_name) { |
| type = target_type |
| } |
| target(target_type, target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "configs", |
| "deps", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| deps = invoker.configs |
| if (defined(invoker.deps)) { |
| deps += invoker.deps |
| } |
| } |
| } |
| } |
| |
| # 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_no_legacy_configs(target_name) { |
| type = invoker.target_type |
| } |
| 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. |
| data_deps = [] |
| foreach(label, invoker.data_deps) { |
| if (get_label_info(label, "toolchain") == current_toolchain) { |
| label += "(${toolchain.label})" |
| } |
| data_deps += [ label ] |
| } |
| } |
| } else { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "visibility", |
| ]) |
| } |
| |
| # Everything else is passed through (everything in solink toolchains). |
| # But also apply configs->deps. |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "configs", |
| "deps", |
| "data_deps", |
| "target_type", |
| "visibility", |
| ]) |
| deps = invoker.configs |
| if (defined(invoker.deps)) { |
| deps += invoker.deps |
| } |
| } |
| } |
| |
| 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" |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "metadata", |
| "target_type", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| |
| # Elaborate the toolchain's defaults to compute the output file name. |
| if (!defined(output_name)) { |
| output_name = target_name |
| } |
| output_name += toolchain.output_name_suffix |
| if (!defined(output_extension)) { |
| output_extension = "so" |
| } |
| output_file = "$target_out_dir/lib$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| |
| metadata = { |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used the data key for the output of the link, as a file name |
| # relative to $root_build_dir appropriate for command-line |
| # contexts. The second is used as a walk key to provide a |
| # dependency barrier against e.g. shared_library() deps or other |
| # executable() data_deps. |
| link_output = [ rebase_path(output_file + toolchain.link_output_suffix, |
| root_build_dir) ] |
| link_barrier = [] |
| if (current_os != "mac" && current_os != "win") { |
| elf_link_output = link_output |
| } |
| |
| # Each shared_library() that's not on the whitelist gets a poison |
| # pill to flag it if it appears in the dependency graph from a |
| # driver() target. |
| if (!defined(driver_blacklist)) { |
| driver_blacklist = [ true ] |
| } |
| } |
| } |
| } 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})", |
| ] |
| } |
| } |
| } |
| |
| 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)) { |
| sdk = invoker.sdk |
| sdk_headers = invoker.sdk_headers |
| } else { |
| sdk = false |
| sdk_headers = [] |
| } |
| _library_params = [ |
| "kernel", |
| "host", |
| "sdk", |
| "sdk_headers", |
| "shared", |
| "static", |
| ] |
| assert(host || kernel || static || shared, |
| "library(\"$target_name\") must build somewhere!") |
| |
| # Not all of these will be referenced in all toolchains. |
| not_needed(_library_params) |
| |
| # A specialized toolchain might not support shared libraries. |
| shared = shared && defined(toolchain.shlib) |
| |
| # 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 = "source_set" |
| } else { |
| static_library = "static_library" |
| } |
| not_needed([ "static_library" ]) |
| |
| targets = false |
| if (is_kernel) { |
| if (kernel) { |
| targets = true |
| source_set(_library_name) { |
| forward_variables_from(invoker, "*", _library_params) |
| if (!defined(public_deps)) { |
| public_deps = [] |
| } |
| public_deps += [ ":${_library_name}.headers" ] |
| } |
| } |
| if (sdk != false && !static && !shared && toolchain.tags == []) { |
| data_deps = [ |
| ":$_library_name-$current_cpu.pkg(${toolchain.label})", |
| ] |
| } |
| } else if (is_host) { |
| if (host) { |
| targets = true |
| target(static_library, _library_name) { |
| if (static_library == "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 (static || shared) { |
| targets = true |
| source_set("${_library_name}._sources") { |
| visibility = [ |
| ":${_library_name}.static", |
| ":${_library_name}.shared", |
| ] |
| 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, "${_library_name}.static") { |
| if (static_library == "static_library") { |
| complete_static_lib = true |
| } |
| output_name = _library_name |
| forward_variables_from(invoker, |
| [ |
| "configs", |
| "data_deps", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (!defined(public_deps)) { |
| public_deps = [] |
| } |
| public_deps += [ ":${_library_name}.headers" ] |
| deps = [ |
| ":${_library_name}._sources", |
| ] |
| } |
| } |
| if (shared) { |
| if (defined(invoker.install_path)) { |
| install_path = invoker.install_path |
| } else { |
| install_path = "lib/${toolchain.libprefix}lib${_library_name}.so" |
| } |
| shared_library("${_library_name}.shared") { |
| output_name = _library_name |
| forward_variables_from(invoker, |
| [ |
| "configs", |
| "ldflags", |
| "libs", |
| "lib_dirs", |
| "testonly", |
| "visibility", |
| ]) |
| |
| # Everything that depends on the library gets the headers. |
| # It also gets the explicit `public_deps`, which includes |
| # any header dependencies or public config()s. |
| 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 config() |
| # 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 |
| } |
| |
| # An explicit `install_path = false` means this DSO is not installed. |
| if (install_path != false) { |
| metadata = { |
| manifest_inputs = [ "$target_out_dir/lib${output_name}.so" ] |
| manifest_lines = [ "${install_path}=" + |
| rebase_path(manifest_inputs[0], root_build_dir) ] |
| } |
| } |
| } |
| } |
| group(_library_name) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| if (shared) { |
| public_deps = [ |
| ":${_library_name}.shared", |
| ] |
| } else { |
| public_deps = [ |
| ":${_library_name}.static", |
| ] |
| } |
| if (sdk != false && toolchain.environment == "user" && |
| toolchain.tags == []) { |
| if (sdk == "shared") { |
| data_deps = [ |
| ":$_library_name-$current_cpu.pkg(${toolchain.shlib})", |
| ] |
| } else { |
| data_deps = [ |
| ":$_library_name-$current_cpu.pkg(${toolchain.label})", |
| ] |
| } |
| } |
| } |
| } |
| |
| 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", |
| ] |
| } |
| } |
| |
| config("${_library_name}.headers") { |
| include_dirs = [ "include" ] |
| |
| # The public_deps here represent header dependencies. |
| forward_variables_from(invoker, |
| [ |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (defined(visibility)) { |
| visibility += [ ":$_library_name" ] |
| if (!is_kernel) { |
| visibility += [ |
| ":${_library_name}.sources", |
| ":${_library_name}.static", |
| ":${_library_name}.shared", |
| ] |
| } |
| } |
| } |
| |
| # 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", |
| ] |
| } |
| 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", |
| ] |
| } |
| } |
| } |
| } |
| |
| if (sdk != false && |
| (toolchain.environment == "user" || (!shared && !static))) { |
| if (sdk == "static") { |
| assert(static, |
| "$target_name must have static=true to have sdk=\"static\"") |
| } else if (sdk == "shared") { |
| assert(shared || !defined(toolchain.shlib), |
| "$target_name must have shared=true to have sdk=\"shared\"") |
| } else { |
| assert(sdk == "source", |
| "$target_name sdk=\"$sdk\" not static, shared, or source") |
| } |
| if (toolchain.tags != [] || |
| (defined(toolchain.shlib) && |
| ((sdk == "shared" && current_toolchain != toolchain.shlib) || |
| (sdk != "shared" && current_toolchain == toolchain.shlib)))) { |
| sdk = false |
| } |
| if (sdk != false) { |
| import("$zx/public/gn/pkg.gni") |
| pkg_export("$target_name-$current_cpu.pkg") { |
| contents = [ |
| "[package]", |
| "name=$_library_name", |
| "type=lib", |
| ] |
| if (sdk == "source") { |
| contents += [ |
| "arch=src", |
| "[src]", |
| ] |
| foreach(file, invoker.sources) { |
| contents += [ rebase_path(file, ".") + "=SOURCE/" + |
| rebase_path(file, "//") ] |
| } |
| } else { |
| contents += [ |
| "arch=$zircon_cpu", |
| "[lib]", |
| ] |
| if (sdk == "static") { |
| if (defined(invoker.output_prefix_override) && |
| invoker.output_prefix_override) { |
| contents += [ "lib/${_library_name}.a=BUILD/" + |
| rebase_path("$target_out_dir/${_library_name}.a", |
| root_build_dir) ] |
| } else { |
| contents += [ "lib/lib${_library_name}.a=BUILD/" + |
| rebase_path("$target_out_dir/lib${_library_name}.a", |
| root_build_dir) ] |
| } |
| } else { |
| soname = "lib${_library_name}.so" |
| contents += [ |
| "debug/$soname=BUILD/" + |
| rebase_path("$target_out_dir/$soname.debug", root_build_dir), |
| "lib/$soname=BUILD/" + |
| rebase_path("$target_out_dir/$soname.debug", root_build_dir), |
| ] |
| if (defined(invoker.install_path)) { |
| install_path = invoker.install_path |
| } else { |
| install_path = "lib/${toolchain.libprefix}lib${_library_name}.so" |
| } |
| if (install_path != false) { |
| contents += |
| [ "dist/$install_path=BUILD/" + |
| rebase_path("$target_out_dir/$soname", root_build_dir) ] |
| } |
| } |
| } |
| contents += [ "[includes]" ] |
| foreach(file, invoker.sdk_headers) { |
| contents += [ rebase_path(file, ".") + "=SOURCE/" + |
| rebase_path(file, "//", "include") ] |
| } |
| sdk_deps = [] |
| if (defined(invoker.public_deps)) { |
| sdk_deps += invoker.public_deps |
| } |
| if (sdk == "source" && defined(invoker.deps)) { |
| sdk_deps += invoker.deps |
| } |
| if (sdk_deps != []) { |
| banjo_deps = [] |
| fidl_deps = [] |
| lib_deps = [] |
| foreach(label, sdk_deps) { |
| if (get_path_info(get_label_info(label, "dir"), "dir") == |
| "$zx/system/banjo") { |
| banjo_deps += [ get_label_info(label, "name") ] |
| } else if (get_label_info(label, "name") == "c.headers" || |
| (get_label_info(label, "name") == "c" && |
| get_path_info(get_label_info(label, "dir"), "dir") == |
| "$zx/system/fidl")) { |
| assert(get_path_info(get_label_info(label, "dir"), "dir") == |
| "$zx/system/fidl") |
| fidl_deps += |
| [ get_path_info(get_label_info(label, "dir"), "name") ] |
| } else if (get_label_info(label, "name") != |
| "enable_driver_tracing" && |
| get_label_info(label, "name") != |
| "generated-public-headers") { |
| assert(true || get_label_info(label, "name") == "headers" || |
| get_path_info(get_label_info(label, "name"), |
| "extension") == "headers", |
| "$label is not a library() headers target") |
| lib_deps += |
| [ get_path_info(get_label_info(label, "dir"), "name") ] |
| } |
| } |
| lib_deps += [ "zxcpp" ] |
| lib_deps -= [ "zxcpp" ] |
| if (lib_deps != []) { |
| contents += [ "[deps]" ] + lib_deps |
| } |
| if (banjo_deps != []) { |
| contents += [ "[banjo-deps]" ] + banjo_deps |
| } |
| if (fidl_deps != []) { |
| contents += [ "[fidl-deps]" ] + fidl_deps |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| ### |
| ### "Terminal" (executable and loadable_module) target types. |
| ### These are the targets that do variant selection. |
| ### |
| |
| template("_variant_target") { |
| target = { |
| main_metadata = { |
| } |
| variant_metadata = { |
| } |
| forward_variables_from(invoker.target, "*") |
| if (!defined(match)) { |
| match = type |
| } |
| } |
| assert_no_legacy_configs(target_name) { |
| type = target.match |
| } |
| |
| 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 (toolchain.variant_selectors == []) { |
| # This toolchain does not participate in variant selection. |
| # Each target is just what it seems. |
| target(target.type, main_target_name) { |
| forward_variables_from(invoker, "*", [ "visibility" ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| } |
| } else { |
| # Most toolchains defined with define_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 define_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. |
| match = true |
| |
| if (selector.cpu != []) { |
| if (selector.cpu + [ current_cpu ] - [ current_cpu ] == selector.cpu) { |
| match = false |
| } |
| } |
| if (selector.dir != []) { |
| dir = get_label_info(":$target_name", "dir") |
| if (selector.dir + [ dir ] - [ dir ] == selector.dir) { |
| match = false |
| } |
| } |
| if (selector.environment != []) { |
| if (selector.environment + [ toolchain.environment ] - |
| [ toolchain.environment ] == selector.environment && |
| selector.environment + [ toolchain.base_environment ] - |
| [ toolchain.base_environment ] == selector.environment) { |
| match = false |
| } |
| } |
| if (selector.label != []) { |
| label = get_label_info(":$target_name", "label_no_toolchain") |
| if (selector.label + [ label ] - [ label ] == selector.label) { |
| match = false |
| } |
| } |
| if (selector.name != []) { |
| name = get_name_info(":$target_name", "name_no_toolchain") |
| if (selector.name + [ name ] - [ name ] == selector.name) { |
| match = false |
| } |
| } |
| if (selector.os != []) { |
| if (selector.os + [ current_os ] - [ current_os ] == selector.os) { |
| match = false |
| } |
| } |
| if (selector.output_name != []) { |
| if (selector.output_name + [ output_name ] - [ output_name ] == |
| selector.output_name) { |
| match = false |
| } |
| } |
| if (selector.target_type != []) { |
| if (selector.target_type + [ target.match ] - [ target.match ] == |
| selector.target_type) { |
| match = false |
| } |
| } |
| if (defined(selector.host) && selector.host != is_host) { |
| match = false |
| } |
| if (defined(selector.kernel) && selector.kernel != is_kernel) { |
| match = 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 (match && 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 define_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. |
| |
| 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. |
| extra_visibility = [] |
| 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. |
| |
| 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) { |
| 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 { |
| # 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", |
| "output_name", |
| "target", |
| "visibility", |
| ]) |
| output_name = output_name |
| if (defined(invoker.visibility)) { |
| # Make sure we're visible to the redirector groups. |
| visibility = invoker.visibility + extra_visibility |
| } |
| } |
| } |
| } |
| } |
| |
| template("_basic_executable") { |
| assert(current_toolchain != default_toolchain) |
| assert_no_legacy_configs(target_name) { |
| type = invoker.target_type |
| } |
| _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, |
| "*", |
| [ |
| "configs", |
| "deps", |
| "metadata", |
| "target", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| deps = invoker.configs |
| if (defined(invoker.deps)) { |
| deps += invoker.deps |
| } |
| |
| # Elaborate the toolchain's defaults to compute the output file name. |
| 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 = "$target_out_dir/$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| |
| metadata = { |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used the data key for the output of the link, as a file name |
| # relative to $root_build_dir appropriate for command-line |
| # contexts. The second is used as a walk key to provide a |
| # dependency barrier against e.g. shared_library() deps or other |
| # executable() data_deps. |
| link_output = [ rebase_path(output_file + toolchain.link_output_suffix, |
| root_build_dir) ] |
| link_barrier = [] |
| if (current_os != "mac" && current_os != "win") { |
| elf_link_output = link_output |
| } |
| } |
| |
| 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) ] |
| } |
| } |
| } |
| } |
| } |
| |
| template("loadable_module") { |
| assert(!is_kernel) |
| assert(!is_host) |
| _variant_target(target_name) { |
| # This is for _variant_target(). |
| target = { |
| shlib = true |
| match = "loadable_module" |
| type = "_shlib_toolchain_target" |
| } |
| |
| # This is for _shlib_toolchain_target(). |
| target_type = "loadable_module" |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "metadata", |
| "target_type", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| |
| # Elaborate the toolchain's defaults to compute the output file name. |
| if (!defined(output_name)) { |
| output_name = target_name |
| } |
| output_name += toolchain.output_name_suffix |
| if (!defined(output_extension)) { |
| output_extension = "so" |
| } |
| output_file = "$target_out_dir/$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| |
| metadata = { |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used the data key for the output of the link, as a file name |
| # relative to $root_build_dir appropriate for command-line |
| # contexts. The second is used as a walk key to provide a |
| # dependency barrier against e.g. shared_library() deps or other |
| # executable() data_deps. |
| link_output = [ rebase_path(output_file + toolchain.link_output_suffix, |
| root_build_dir) ] |
| link_barrier = [] |
| if (current_os != "mac" && current_os != "win") { |
| elf_link_output = link_output |
| } |
| } |
| |
| 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) ] |
| } |
| } |
| } |
| } |
| |
| template("driver") { |
| loadable_module(target_name) { |
| data_deps = [] |
| deps = [] |
| forward_variables_from(invoker, "*", [ "visibility" ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| |
| # All drivers implicitly get the driver ABI. |
| deps += [ "$zx/system/ulib/driver" ] |
| |
| # Drivers that use C++ library facilities cannot use the libc++ |
| # shared library (it's not in the whitelist). So always use the |
| # hermetic static library. This has no effect if no C++ library |
| # symbols are used. |
| configs += [ "$zx/public/gn/config:static-libc++" ] |
| |
| # It's an error to link against any shared library that's not on the |
| # whitelist. Those libraries set `driver_blackist=[]` while others (by |
| # default, see shared_library() above) set `driver_blacklist=[true]`. |
| if (false) { # TODO(get_metadata): add assert_no_metadata feature |
| assert_no_metadata([ "" ], |
| [ "driver_blacklist" ], |
| [ "driver_blacklist_barrier" ], |
| "driver() targets cannot depend on shared libraries") |
| } |
| |
| # Elaborate the defaults to compute the output file name. |
| if (!defined(output_name)) { |
| output_name = target_name |
| } |
| |
| # Set the standard install_path. |
| if (!defined(install_path)) { |
| install_path = "driver/${output_name}.so" |
| } |
| } |
| } |
| |
| template("test_driver") { |
| driver(target_name) { |
| testonly = true |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "install_path", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| |
| # Elaborate the defaults to compute the output file name. |
| if (!defined(output_name)) { |
| output_name = target_name |
| } |
| install_path = "driver/test/${output_name}.so" |
| } |
| } |
| |
| template("executable") { |
| _basic_executable(target_name) { |
| target_type = "executable" |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "target_type", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| } |
| } |
| |
| template("test") { |
| if (is_kernel) { |
| not_needed(invoker, "*") |
| not_needed([ "target_name" ]) |
| } else { |
| _basic_executable(target_name) { |
| target_type = "test" |
| testonly = true |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "target_type", |
| "test_group", |
| "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 (!is_host && !defined(install_path)) { |
| if (defined(invoker.test_group)) { |
| test_group = invoker.test_group |
| } else { |
| test_group = "sys" |
| } |
| install_path = "test/$test_group/$output_name" |
| if (output_extension != "") { |
| install_path += ".$output_extension" |
| } |
| } |
| |
| if (is_host) { |
| metadata = { # TODO: host test metadata |
| } |
| # TODO: variant_suffix_metadata = { ... } |
| } |
| } |
| } |
| } |
| |
| template("host_tool") { |
| assert(!is_kernel, "host_tool() targets don't work in kernel toolchains") |
| _basic_executable(target_name) { |
| target_type = "host_tool" |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "target_type", |
| "visibility", |
| ]) |
| forward_variables_from(invoker, [ "visibility" ]) |
| 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 = "$target_out_dir/$output_name" |
| tool_extension = "" |
| if (output_extension != "") { |
| tool_extension = ".$output_extension" |
| } |
| metadata = { |
| # List of tool binaries that could be presented to users' command line. |
| tool_executables = [ rebase_path( |
| get_path_info(tool_executable, "dir") + "/" + |
| get_path_info(tool_executable, "name") + tool_extension, |
| root_build_dir) ] |
| |
| # See host_tool_action(). |
| host_tool_barrier = [] |
| host_tool_rspfile = tool_executables + [ "--" ] |
| if (defined(toolchain.host_run_env)) { # TODO: sanitizer case |
| host_tool_rspfile += toolchain.host_run_env |
| } |
| host_tool_rspfile += tool_executables |
| } |
| variant_suffix_outputs = [ tool_executable + tool_extension ] |
| variant_suffix_metadata = { |
| tool_executables = variant_suffix_outputs |
| } |
| } |
| if (is_host && current_os == host_os && current_cpu == host_cpu) { |
| if (!defined(data_deps)) { |
| data_deps = [] |
| } |
| data_deps += [ ":$target_name.pkg" ] |
| } |
| } |
| |
| # TODO(mcgrathr): Temporary hacks for integrating with the legacy |
| # Fuchsia GN build. |
| if (is_host && current_os == host_os && current_cpu == host_cpu) { |
| import("$zx/public/gn/pkg.gni") |
| assert(!defined(invoker.output_name)) |
| tool_name = target_name |
| pkg_export("$target_name.pkg") { |
| contents = [ |
| "[package]", |
| "name=$tool_name", |
| "type=tool", |
| "arch=host", |
| "[bin]", |
| "$tool_name=BUILD/" + |
| rebase_path("$target_out_dir/$tool_name", root_build_dir), |
| ] |
| } |
| } |
| } |
| |
| # 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 -=`. |
| _compile_target_types = [ |
| "executable", |
| "host_tool", |
| "library", |
| "loadable_module", |
| "shared_library", |
| "source_set", |
| "static_library", |
| "test", |
| ] |
| if (toolchain.environment == "user") { |
| _compile_target_types += [ |
| "driver", |
| "test_driver", |
| ] |
| } |
| |
| # ${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. In the |
| # Fuchsia build, `configs` winds up being used as `deps`, so things listed |
| # in ${toolchain.configs} can actually be any target type, not just config(). |
| foreach(target_type, _compile_target_types) { |
| set_defaults(target_type) { |
| configs = [] |
| foreach(config, toolchain.configs) { |
| # Either it's an absolute label string or it's a scope with filters |
| # and mutators. See define_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 |
| } |
| } |
| } |
| } |