| # Copyright 2021 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import("//build/config/current_target_tuple.gni") |
| import("//build/config/zircon/standard.gni") |
| import("//build/toolchain/zircon/clang.gni") |
| |
| _llvm_ifs = "$clang_tool_dir/llvm-ifs" |
| |
| # Putting this in lib_dirs early in the configs resolution order preempts the |
| # lib_dirs in each ifs_shared_library() `.as-needed` target's implied config(). |
| # Thus each libs reference in the source_set() underlying the `.as-needed` |
| # target resolves to the no-link-inputs file in ifs-as-needed instead. A later |
| # link step should use an ifs_shared_library_libs() target to get the same |
| # shared library dependencies into the final link. These files are just empty |
| # files with comments, so they are reused across all toolchains. |
| _common_gen_dir = get_label_info(".($default_toolchain)", "root_gen_dir") |
| ifs_shared_library_as_needed_dir = "$_common_gen_dir/ifs-as-needed" |
| |
| # The metadata-generated linker scripts from ifs_shared_library_libs() all go |
| # into this directory instead of $target_gen_dir. See comments at the setting |
| # of ifs_shared_library_ldscript metadata in ifs_shared_library(). |
| _gen_stub_ldscript_dir = "$root_gen_dir/ifs_shared_library_libs" |
| |
| # Define a linkable shared library built from a text ABI (.ifs) specification. |
| # |
| # This uses a `.ifs` file kept in the source tree to create a linkable ELF |
| # shared library stub file. This can be used at link time to build executable |
| # and (non-stub) shared library binaries without reference to the actual |
| # runtime shared library. The real (non-stub) shared library to be used at |
| # runtime must be compiled separately and included at runtime as necessary. |
| # This target only takes care of satisfying any link-time dependencies. |
| # Its label is used in `deps` like a shared_library() target. |
| # |
| # This defines three public targets and a config. |
| # |
| # * "$target_name" |
| # - The main target acts like shared_library() for linking purposes, but is |
| # not a GN target with outputs. |
| # |
| # * "$target_name.stub" |
| # - This is the target whose direct outputs include the linking stub ELF |
| # file itself, e.g. for use as input to a copy() target or the like. This |
| # does not act as a link input in GN deps; use the main target for that. |
| # |
| # * "$target_name.config" |
| # - This config() is defined to make `-l$output_name` implicit link |
| # arguments work by supplying a `lib_dirs` that will find the stub and |
| # nothing else. This config is propagated from the main target via |
| # public_configs, but this alone may not be sufficient for it to reach the |
| # linking targets unless all the intervening deps are via public_deps. |
| # **Note:** It's always better to use GN deps on the main target to get the |
| # stub into the link. The `libs` method is provided only to handle cases |
| # like compiler driver, .deplibs, or other forms of implicit link-time |
| # dependencies that can't be replaced with normal explicit link inputs. |
| # |
| # * "$target_name.as-needed" |
| # - This can be used *instead of* (not _in addition to_!) the main target |
| # to satisfy link-time dependencies for symbols defined in the `.ifs` file. |
| # Unlike the main target, this uses `--as-needed` semantics, meaning that |
| # no DT_NEEDED entry will be generated if none of these symbols is actually |
| # used in a given link. This is appropriate for use in the deps of a |
| # static library where some, but not all, of the source files going into |
| # that static library depend on symbols in this shared library ABI. |
| # **Note:** This is the **only** kind of shared library target that can |
| # appear in the linking deps graph of an hermetic_source_set() target. |
| # ifs_shared_library_libs() targets provide the mechanism for propagating a |
| # dependency on "$target_name.as-needed" through a relocatable link step |
| # such as hermetic_source_set(). |
| # |
| # Parameters |
| # |
| # * abi |
| # - Required: Source path to a `.ifs` file defining the library's ELF ABI. |
| # **NOTE:** This file must be consistent across $current_cpu values! |
| # - Type: file |
| # |
| # * output_name |
| # - Optional: The plain name of the linkable library file to write, |
| # without the `lib` prefix or the `.so` extension. |
| # - Type: string |
| # - Default: $target_name |
| # |
| # * data_deps, public, public_configs, public_deps |
| # - Optional: As for shared_library(). |
| # |
| # * deps |
| # - Optional: Any dependencies that went into the creation of the supplied |
| # `.ifs` file. |
| # - Type: list(label) |
| # |
| template("ifs_shared_library") { |
| main_target = target_name |
| main_label = get_label_info(":$main_target", "label_with_toolchain") |
| stub_target = "$target_name.stub" |
| config_target = "$target_name.config" |
| |
| gen_target_base = "_ifs_shared_library.$target_name." |
| gen_target = "$gen_target_base$clang_cpu" |
| gen_label = ":$gen_target($default_toolchain)" |
| gen_dir_base = get_label_info(gen_label, "target_gen_dir") + "/$target_name." |
| |
| if (defined(invoker.output_name)) { |
| output_name = invoker.output_name |
| } else { |
| output_name = target_name |
| } |
| |
| gen_dir = "$gen_dir_base$clang_cpu" |
| gen_stub_file = "$gen_dir/lib$output_name.so" |
| public_stub_file = "$target_out_dir/lib$output_name.so" |
| |
| as_needed_target = "$main_target.as-needed" |
| gen_stub_as_needed_file = "$gen_dir.as-needed/lib$output_name.so" |
| gen_as_needed_skip_target = gen_target_base + "as-needed.skip" |
| |
| # This has the actual stub ELF file directly in libs, so lib_dirs searching. |
| source_set(main_target) { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "public", |
| "public_configs", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| configs += [ ":$config_target" ] |
| libs = [ gen_stub_file ] |
| deps = [ gen_label ] |
| metadata = { |
| # This is a barrier like shared_library() would be. |
| link_output_barrier = [] |
| } |
| } |
| |
| # This provides lib_dirs such that `libs = [ output_name ]` (or a generated |
| # `-l$output_name` outside GN's knowledge, perhaps built into the compiler) |
| # would find the actual stub ELF file. |
| config(config_target) { |
| forward_variables_from(invoker, [ "visibility" ]) |
| if (defined(visibility)) { |
| visibility += [ ":$main_target" ] |
| } |
| lib_dirs = [ gen_dir ] |
| } |
| |
| # This provides `libs = [ output_name ]` and a lib_dirs such that it will be |
| # resolved to the $gen_stub_as_needed_file input linker script, getting the |
| # link stub wrapped in AS_NEEDED. |
| # |
| # However, this also generates an alternate lib$output_name.so input linker |
| # script in the shared directory that //build/config:ifs-as-needed injects |
| # into lib_dirs. Thus any dependent link target using that config will use |
| # the alternate file instead. It provides no link input, so |
| # ifs_shared_library_libs() must be used to collect the link input this |
| # target would have produced if not preempted by the |
| # //build/config:ifs-as-needed config. |
| source_set(as_needed_target) { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "public", |
| "public_configs", |
| "public_deps", |
| "testonly", |
| "visibility", |
| ]) |
| libs = [ output_name ] |
| deps = [ |
| ":$gen_as_needed_skip_target.a($default_toolchain)", |
| ":$gen_as_needed_skip_target.so($default_toolchain)", |
| ":$gen_target.as-needed($default_toolchain)", |
| |
| # gen_stub_as_needed_file points to the file this generates. |
| gen_label, |
| ] |
| lib_dirs = [ |
| # This holds the "lib$output_name.so" that will be found if |
| # //build/config:ifs-as-needed is not preempting it. |
| get_path_info(gen_stub_as_needed_file, "dir"), |
| ] |
| |
| # The generated_file() in the ifs_shared_library_libs() target collects the |
| # ifs_shared_library_ldscript metadata to produce an input linker script. |
| # For relative paths used in input linker scripts, the linker will try |
| # resolving that relative to directory containing that script file. |
| # |
| # Ordinarily that generated script would go in the $target_gen_dir of the |
| # ifs_shared_library_libs() target. But that isn't known here, and can't |
| # be known: each ifs_shared_library_libs() will have the $target_gen_dir |
| # for its BUILD.gn file's directory; there is no correlation between the |
| # $target_gen_dir of that target and anything this ifs_shared_library() |
| # target can know. So instead every ifs_shared_library_libs() target puts |
| # its metadata-collected linker script into $_gen_stub_ldscript_dir, a |
| # single directory for all targets (in the toolchain). (To preserve |
| # uniqueness, the script's file name is generated so as to encode the full |
| # label without producing any slashes.) Thus, the fragment here refers to |
| # the link stub's path relative to $_gen_stub_ldscript_dir. |
| ldscript_relative_gen_stub_file = |
| rebase_path(gen_stub_file, _gen_stub_ldscript_dir) |
| |
| metadata = { |
| # This is a barrier like shared_library() would be. |
| link_output_barrier = [] |
| |
| # This will be collected by ifs_shared_library_libs(). |
| ifs_shared_library_ldscript = [ |
| "/* $main_label", |
| " * uses this for indirect deps via //build/config:ifs-as-needed.", |
| " * See ifs_shared_library_libs() documentation for details.", |
| " */", |
| "INPUT(AS_NEEDED($ldscript_relative_gen_stub_file))", |
| ] |
| } |
| } |
| |
| copy(stub_target) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| deps = [ gen_label ] |
| sources = [ gen_stub_file ] |
| outputs = [ public_stub_file ] |
| } |
| |
| if (current_toolchain == default_toolchain) { |
| as_needed_label = |
| get_label_info(":$main_target.as-needed", "label_no_toolchain") |
| |
| # Define a target to generate a stub for each CPU type, but all generated |
| # in the default toolchain since nothing but CPU varies among them. Note |
| # that because of GN's special treatment of the default toolchain, these |
| # will all get Ninja targets even if they're not in any GN deps graph. |
| # But since these targets depend only on the source files and don't cause |
| # anything else new to get built, that should not add any build overhead. |
| foreach(cpu, standard_fuchsia_cpus) { |
| foreach(translation, clang_cpu_translations) { |
| if (cpu == translation.gn) { |
| cpu = translation.clang |
| } |
| } |
| |
| gen_target = "$gen_target_base$cpu" |
| gen_dir = "$gen_dir_base$cpu" |
| gen_stub_file = "$gen_dir/lib$output_name.so" |
| gen_stub_as_needed_file = "$gen_dir.as-needed/lib$output_name.so" |
| |
| action(gen_target) { |
| forward_variables_from(invoker, |
| [ |
| "deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (defined(visibility)) { |
| visibility += [ |
| ":$main_target", |
| ":$stub_target", |
| ] |
| } |
| |
| # Output timestamps are not freshened if contents do not change. |
| all_outputs_fresh = false |
| |
| script = _llvm_ifs |
| sources = [ invoker.abi ] |
| outputs = [ gen_stub_file ] |
| args = [ |
| "--input-format=IFS", |
| "--target=$cpu-fuchsia", |
| "--write-if-changed", |
| "--output-elf=" + rebase_path(gen_stub_file, root_build_dir), |
| rebase_path(invoker.abi, root_build_dir), |
| ] |
| } |
| |
| # This is an input linker script that just wraps that file in AS_NEEDED. |
| generated_file("$gen_target.as-needed") { |
| visibility = [ ":$as_needed_target" ] |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| relative_stub_path = |
| rebase_path(gen_stub_file, |
| get_path_info(gen_stub_as_needed_file, "dir")) |
| outputs = [ gen_stub_as_needed_file ] |
| output_conversion = "list lines" |
| contents = [ |
| "/* $as_needed_label", |
| " * uses this to link in the shared library only if referenced.", |
| " * This file is used for a deps path that reaches that", |
| " * without an intervening ifs_shared_library_libs() target.", |
| " * See ifs_shared_library_libs() documentation for details.", |
| " */", |
| "INPUT(AS_NEEDED($relative_stub_path))", |
| ] |
| } |
| } |
| |
| # This is an input linker script that just does nothing. It exists to |
| # preempt the file from the as-needed stub target when its output dir comes |
| # first in the search order via //build/config:ifs-as-needed. It provides |
| # no input to the link. For a relocatable link, the as-needed dependency |
| # will be found via metadata collection. Both lib$output_name.a and |
| # lib$output_name.so are generated to ensure this preempts any other |
| # resolution for -l$output_name in the lib_dirs search path, even if that's |
| # left to the linker's -L path and whichever file name it searches for. |
| foreach(extension, |
| [ |
| "a", |
| "so", |
| ]) { |
| generated_file("$gen_as_needed_skip_target.$extension") { |
| visibility = [ ":$as_needed_target" ] |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| outputs = |
| [ "$ifs_shared_library_as_needed_dir/lib$output_name.$extension" ] |
| output_conversion = "list lines" |
| contents = [ |
| "/* $as_needed_label", |
| " * links this in when //build/config:ifs-as-needed is used.", |
| " * See ifs_shared_library_libs() documentation for details.", |
| " */", |
| ] |
| } |
| } |
| } else { |
| not_needed(invoker, |
| [ |
| "abi", |
| "deps", |
| ]) |
| } |
| } |
| |
| # Define a link input for using ifs_shared_library() `.as-needed` subtargets. |
| # |
| # This defines a target usable in deps of a linking target. Its effect is to |
| # make sure linking stubs go into that link for any `.as-needed` subtargets in |
| # the deps of this input target. This is needed in an outer link that includes |
| # the output of another relocatable link. The deps for this target should |
| # include the deps of the relocatable link, whose configs should include |
| # //build/config:ifs-as-needed. This template is used implicitly by |
| # hermetic_source_set(), which uses that config automatically. |
| # |
| # Parameters |
| # |
| # * deps |
| # - Required: Should reach ifs_shared_library() `.as-needed` subtargets. |
| # - Type: list(label) |
| # |
| # * visibility, testonly |
| # - Optional: Usual GN meanings. |
| # |
| template("ifs_shared_library_libs") { |
| libs_target = target_name |
| gen_target = "_ifs_shared_library_libs.$target_name.gen" |
| prologue_target = "_ifs_shared_library_libs.$target_name.prologue" |
| |
| # See comment in metadata above. All these scripts must go into a single |
| # common directory (for the toolchain), rather than $target_gen_dir. This |
| # allows each input linker script fragment to use a script-relative path to |
| # its link stub file ($gen_stub_file in ifs_shared_library() implementation). |
| # To make sure the same $target_name in two ifs_shared_library_libs() targets |
| # in different directories doesn't result in a collision, each generated |
| # script's file name encodes its full label path rather than its path under |
| # $root_gen_dir to $target_gen_dir doing that implicitly. |
| gen_prefix = string_replace(rebase_path(".", "//"), "/", ".") |
| gen_file = "$_gen_stub_ldscript_dir/$gen_prefix$libs_target.ld" |
| |
| source_set(libs_target) { |
| forward_variables_from(invoker, |
| [ |
| "visibility", |
| "testonly", |
| ]) |
| libs = [ gen_file ] |
| deps = [ ":$gen_target" ] |
| } |
| |
| generated_file(gen_target) { |
| visibility = [ ":$libs_target" ] |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| deps = [ ":$prologue_target" ] + invoker.deps |
| |
| outputs = [ gen_file ] |
| output_conversion = "list lines" |
| data_keys = [ "ifs_shared_library_ldscript" ] |
| walk_keys = [ "link_output_barrier" ] |
| } |
| |
| # This comes first in the metadata walk so its contribution will be the first |
| # thing in $gen_file. |
| group(prologue_target) { |
| visibility = [ ":$gen_target" ] |
| forward_variables_from(invoker, [ "testonly" ]) |
| metadata = { |
| ifs_shared_library_ldscript = [ |
| "/* This file is generated by the ifs_shared_library_libs() target", |
| " * " + get_label_info(":$libs_target", "label_with_toolchain"), |
| " */", |
| ] |
| } |
| } |
| } |