| # 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() { |
| # *These 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 = "/" |
| |
| # "$zx_build/" is the prefix for GN "source-absolute" paths in the Zircon |
| # build for build infrastructure. |
| zx_build = "/" |
| } |
| |
| 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 = "$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 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 |
| } |
| |
| ### |
| ### "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 (defined(dep.unless_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 |
| } |
| } |
| } |
| } |
| } |
| |
| # 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", |
| "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 = "$output_dir/lib$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| link_output_file = output_file + toolchain.link_output_suffix |
| |
| metadata = { |
| binaries = [] |
| link_barrier = [] |
| link_output = [] |
| |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used as 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. |
| # The last is provided only on ELF platforms, to indicate all the link |
| # outputs that should be checked for a build ID if collecting an |
| # exhaustive list via metadata. |
| link_output += [ rebase_path(link_output_file, root_build_dir) ] |
| |
| # For build_api_module("binaries") in $zx/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.build-id.stamp", root_build_dir) |
| } |
| if (toolchain.breakpad_syms != false && current_os == "fuchsia") { |
| breakpad = rebase_path("$output_file.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) |
| # |
| # 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(BLD-353): 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_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\"") |
| } |
| } else { |
| sdk = false |
| sdk_headers = [] |
| sdk_publishable = false |
| } |
| _library_params = [ |
| "kernel", |
| "host", |
| "sdk", |
| "sdk_headers", |
| "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(BLD-353): temporary hacks |
| if (sdk != false && toolchain.environment == "user" && |
| current_toolchain == toolchain.shlib) { |
| 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") == |
| "$zx/system/banjo") { |
| 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 = _library_name |
| } else { |
| libfile = "lib$_library_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(TC-446): 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") |
| }, |
| ] |
| } |
| |
| legacy_metadata = { |
| legacy_barrier = [] |
| legacy_dirs = [ "lib/$_library_name" ] |
| legacy_targets = [ |
| { |
| _label = get_label_info(":$_library_name", "label_with_toolchain") |
| _zircon_public = "lib" |
| forward_variables_from(invoker, [ "testonly" ]) |
| import = "//build/zircon/zircon_library.gni" |
| target_type = "zircon_library" |
| target_name = _library_name |
| dir = "//zircon/" + rebase_path(".", "//") |
| include_dirs = [ "//zircon/" + rebase_path("include", "$zx/") ] |
| headers = sdk_headers |
| publishable = sdk_publishable |
| if (sdk == "source") { |
| source_dir = "//zircon/" + rebase_path(".", "$zx/") |
| sources = [] |
| foreach(file, rebase_path(invoker.sources, "$zx/")) { |
| 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") == "$zx/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") == "$zx/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") == "$zx/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") == "$zx/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_path_info(get_label_info(dep, "dir"), "dir") == |
| "$zx/system/banjo") { |
| dep = get_path_info(get_label_info(dep, "dir"), "file") |
| deps += [ "//zircon/system/banjo/$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 if (!is_host && !is_kernel && (static || shared)) { |
| legacy_metadata = { |
| legacy_barrier = [] |
| } |
| } |
| |
| # 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" ] |
| } |
| } |
| } 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_configs", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (!defined(public_deps)) { |
| public_deps = [] |
| } |
| public_deps += [ ":${_library_name}.headers" ] |
| deps = [ |
| ":${_library_name}._sources", |
| ] |
| |
| # TODO(BLD-353): 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 |
| } |
| |
| toolchain_label = get_label_info(":$target_name", "toolchain") |
| |
| 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(BLD-353): temporary hacks |
| forward_variables_from(legacy_metadata, "*") |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, [ "legacy_sysroot" ]) |
| } |
| |
| # Make the manifest describing this library available to the build |
| # API module for unification. |
| # The manifest is not published for fuzzer variants. |
| if (string_replace(toolchain_label, "fuzzer", "") == |
| toolchain_label) { |
| zx_manifest = [ |
| { |
| label = get_label_info(":$target_name", "label_with_toolchain") |
| name = "lib.$_library_name" |
| if (toolchain.tags + [ "instrumented" ] - [ "instrumented" ] != toolchain.tags) { |
| name += toolchain.variant_suffix |
| } |
| toolchain = toolchain_label |
| path = rebase_path(zx_manifest_file, root_build_dir) |
| cpu = current_cpu |
| os = current_os |
| }, |
| ] |
| } |
| } |
| } |
| } |
| 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}.sources", |
| ":${_library_name}.static", |
| ":${_library_name}.shared", |
| ] |
| } |
| } |
| } |
| |
| 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 $zx/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.type ] - |
| [ target.type ] != dep.types) { |
| 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", |
| "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 = "$output_dir/$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| link_output_file = output_file + toolchain.link_output_suffix |
| |
| metadata = { |
| binaries = [] |
| link_barrier = [] |
| link_output = [] |
| |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used as 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. |
| # The last is provided only on ELF platforms, to indicate all the link |
| # outputs that should be checked for a build ID if collecting an |
| # exhaustive list via metadata. |
| link_output += [ rebase_path(link_output_file, root_build_dir) ] |
| |
| # For build_api_module("binaries") in $zx/BUILD.gn. |
| 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.build-id.stamp", root_build_dir) |
| } |
| if (toolchain.breakpad_syms != false && current_os == "fuchsia") { |
| breakpad = rebase_path("$output_file.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/$test_group/$output_name" |
| # Type: string or false |
| # |
| # test_group |
| # Optional: Used to set the default $install_path. |
| # Type: string |
| # Default: "sys" |
| # |
| # 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", |
| "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 (!defined(output_dir)) { |
| output_dir = target_out_dir |
| } |
| |
| 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 (!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 $zx/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(). |
| 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", |
| "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" |
| } |
| |
| # 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, "name") + 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(). |
| 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)) { # TODO: sanitizer case |
| host_tool_rspfile += toolchain.host_run_env |
| } |
| host_tool_rspfile += [ run_file ] |
| |
| # TODO(BLD-353): Temporary hacks for integrating with the legacy |
| # Fuchsia GN build. |
| 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", |
| "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 = "$output_dir/$output_name" |
| if (output_extension != "") { |
| output_file += ".$output_extension" |
| } |
| link_output_file = output_file + toolchain.link_output_suffix |
| |
| metadata = { |
| binaries = [] |
| link_barrier = [] |
| link_output = [] |
| |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Every terminal target provides these metadata keys. The first is |
| # used as 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. |
| # The last is provided only on ELF platforms, to indicate all the link |
| # outputs that should be checked for a build ID if collecting an |
| # exhaustive list via metadata. |
| link_output += [ rebase_path(link_output_file, root_build_dir) ] |
| |
| # For build_api_module("binaries") in $zx/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.build-id.stamp", root_build_dir) |
| } |
| if (toolchain.breakpad_syms != false && current_os == "fuchsia") { |
| breakpad = rebase_path("$output_file.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(ZX-2863): 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(ZX-2863): Remove this ASAP! |
| "deprecated_inhibit_driver_shlib_allowlist", |
| ]) |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| |
| # All drivers implicitly get the driver ABI. |
| deps += [ "$zx/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(ZX-2863): 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("$zx/public/gn/prebuilt.gni") |
| forward_variables_from(invoker, [ "testonly" ]) |
| visibility = [ ":${driver_target}._build($current_toolchain)" ] |
| script = "$zx/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", |
| "$zx/", |
| ], |
| 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" |
| } |
| } |
| |
| ### |
| ### 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 |
| # $zx/public/gn/${target_type}.gni file. |
| _compile_target_types = [ |
| "executable", |
| "host_tool", |
| "library", |
| "loadable_module", |
| "shared_library", |
| "source_set", |
| "static_library", |
| "test", |
| "zx_executable", |
| "zx_host_tool", |
| "zx_library", |
| "zx_test", |
| ] |
| if (toolchain.environment == "user") { |
| _compile_target_types += [ |
| "driver", |
| "test_driver", |
| "zx_driver", |
| "zx_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) { |
| 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 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 |
| } |
| } |
| } |
| } |
| |
| # Make wrapper templates used for build unification universally available. |
| import("//public/gn/unification/wrappers.gni") |