| # Copyright 2020 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/clang/clang.gni") |
| import("//build/rust/config.gni") |
| import("//build/toolchain/zircon/clang.gni") |
| |
| # ## runtime.json |
| # |
| # Each toolchain (i.e. Clang or rustc) provides a "runtime.json" file |
| # in its top-level lib/ sub-directory. |
| # |
| # This file is provided by the toolchain to describe the runtime |
| # dependencies implied by linking a binary based on --target and other |
| # compiler switches. The file contains a JSON array of objects that map to |
| # the following GN schema. Each entry matches a single compilation mode |
| # and yields all the runtime dependencies implied by that mode. |
| # |
| # Type: list(scope) |
| # |
| # * target |
| # - Required: --target tuple a la `${current_target_tuple}`. |
| # - Type: string |
| # |
| # * cflags |
| # - Optional: List of compilation flags that select this mode, |
| # e.g. `"-fsanitizer=..."` and the like. |
| # If not specified, cflags are ignored during selection. |
| # - Type: list(string) |
| # |
| # * ldflags |
| # - Optional: Link-time flags that select this mode. |
| # This is usually either `[ "-static-libstdc++" ]` or `[]`. |
| # If not specified, ldflags are ignored during selection. |
| # - Type: list(string) |
| # |
| # * runtime |
| # - Required: List of runtime files needed by binaries in this mode. |
| # - Type: list(scope) |
| # |
| # * name |
| # - Optional: A stable name for the library to use when publishing a |
| # zx_manifest. If omitted, soname is used. |
| # - Type: string |
| # |
| # * soname |
| # - Required: `DT_SONAME` string in the ELF shared library. |
| # - Type: string |
| # |
| # * dist |
| # - Required: File to load to satisfy $soname `DT_NEEDED` entries. |
| # - Type: path relative to `${toolchain_spec.lib_dir}` |
| # |
| # * debug |
| # - Optional: Unstripped or separate debug file matching $dist. |
| # - Type: path relative to `${toolchain_spec.lib_dir}` |
| # |
| # * breakpad |
| # - Required if `debug` is present and `toolchain.use_breakpad` is true: |
| # Path to breakpad .sym file. |
| # - Type: path relative to `${toolchain_spec.lib_dir}` |
| # |
| _clang_lib_dir = "$clang_prefix/../lib" |
| _clang_runtime_file = "$_clang_lib_dir/runtime.json" |
| _clang_runtime = read_file(_clang_runtime_file, "json") |
| |
| # As a special case, on Fuchsia and for ASan builds, libclang_rt.asan.so depends |
| # on libc++abi.so even if the generated machine code does not (i.e. when |
| # -static-libstdc++ is also used). libc++abi.so depends on libwundind.so itself. |
| # Linking issues appear because these dependencies are not listed properly, so |
| # work-around this by patching _clang_runtime for Asan. |
| |
| _cflags = [] |
| |
| # First, record a map of runtimes for Fuchsia for Asan without -static-libstdc++ |
| _asan_runtimes_map = [] |
| foreach(entry, _clang_runtime) { |
| if (entry.cflags == _cflags + [ "-fsanitize=address" ] && |
| entry.ldflags == []) { |
| _targets = [] |
| _targets = entry.target |
| _target = _targets[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| _asan_runtimes_map += [ |
| { |
| target = _target |
| runtime = entry.runtime |
| }, |
| ] |
| } |
| } |
| } |
| |
| # Then patch up the entry of Asan with -static-libstdc++ to be the same. |
| _new_clang_runtime = [] |
| foreach(entry, _clang_runtime) { |
| _new_clang_runtime += [ |
| { |
| forward_variables_from(entry, "*") |
| if (cflags == _cflags + [ "-fsanitize=address" ] && |
| ldflags == [ "-static-libstdc++" ]) { |
| _target = target[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| foreach(_asan_entry, _asan_runtimes_map) { |
| if (_asan_entry.target == _target) { |
| runtime = [] |
| runtime = _asan_entry.runtime |
| } |
| } |
| } |
| } |
| }, |
| ] |
| } |
| |
| # Do the same for the 'libclang_rt.hwasan.so' which also depends |
| # on libc++abi.so. |
| _clang_runtime = [] |
| _clang_runtime = _new_clang_runtime |
| |
| _hwasan_runtimes_map = [] |
| foreach(entry, _clang_runtime) { |
| if (entry.cflags == [ "-fsanitize=hwaddress" ] && entry.ldflags == []) { |
| _targets = [] |
| _targets = entry.target |
| _target = _targets[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| _hwasan_runtimes_map += [ |
| { |
| target = _target |
| runtime = entry.runtime |
| }, |
| ] |
| } |
| } |
| } |
| |
| _new_clang_runtime = [] |
| foreach(entry, _clang_runtime) { |
| _new_clang_runtime += [ |
| { |
| forward_variables_from(entry, "*") |
| if (cflags == [ "-fsanitize=hwaddress" ] && |
| ldflags == [ "-static-libstdc++" ]) { |
| _target = target[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| foreach(_hwasan_entry, _hwasan_runtimes_map) { |
| if (_hwasan_entry.target == _target) { |
| runtime = [] |
| runtime = _hwasan_entry.runtime |
| } |
| } |
| } |
| } |
| }, |
| ] |
| } |
| |
| # Do the same for the 'libclang_rt.ubsan_standalone.so' which also depends |
| # on libc++abi.so. |
| _clang_runtime = [] |
| _clang_runtime = _new_clang_runtime |
| |
| _ubsan_runtimes_map = [] |
| foreach(entry, _clang_runtime) { |
| if (entry.cflags == _cflags + [ "-fsanitize=undefined" ] && |
| entry.ldflags == []) { |
| _targets = [] |
| _targets = entry.target |
| _target = _targets[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| _ubsan_runtimes_map += [ |
| { |
| target = _target |
| runtime = entry.runtime |
| }, |
| ] |
| } |
| } |
| } |
| |
| _new_clang_runtime = [] |
| foreach(entry, _clang_runtime) { |
| _new_clang_runtime += [ |
| { |
| forward_variables_from(entry, "*") |
| if (cflags == _cflags + [ "-fsanitize=undefined" ] && |
| ldflags == [ "-static-libstdc++" ]) { |
| _target = target[0] |
| if (string_replace(_target, "-fuchsia", "") != _target) { |
| foreach(_ubsan_entry, _ubsan_runtimes_map) { |
| if (_ubsan_entry.target == _target) { |
| runtime = [] |
| runtime = _ubsan_entry.runtime |
| } |
| } |
| } |
| } |
| }, |
| ] |
| } |
| |
| _clang_runtime = [] |
| _clang_runtime = _new_clang_runtime |
| |
| _rustc_lib_dir = "$rustc_prefix/lib" |
| _rustc_runtime_file = "$_rustc_lib_dir/runtime.json" |
| _rustc_runtime = read_file(_rustc_runtime_file, "json") |
| |
| # ## Toolchain specifications |
| # |
| # Each <runtime>_toolchain_spec below is a scope with the following keys: |
| # |
| # * runtime |
| # - Required: The contents of the runtimes manifest (runtime.json). |
| # See above for the schema. |
| # - Type: scope |
| # |
| # * runtime_file |
| # - Required: The path to runtime.json from which `runtime` was read. |
| # - Type: file |
| # |
| # * flag_vars |
| # - Required: The set of flags to match on in the runtimes manifest. |
| # - Example: `[ "cflags", "ldflags" ]` |
| # - Type: list(string) |
| # |
| # * lib_dir |
| # - Required: The base path for all libraries in runtime.json. |
| # - Type: string |
| # |
| # * version_string |
| # - Required: A string that changes every time the toolchain is updated, |
| # so we know when to force a recompile. |
| # - Type: string |
| # |
| # * version_description |
| # - Required: Something that can lead a human to find the specific toolchain, |
| # such as a source repository URL and revision identifier. If not available, |
| # supply an empty string. |
| # - Type: string |
| # |
| # Clang toolchain spec for use with toolchain_runtime_deps(). |
| clang_toolchain_spec = { |
| runtime = _clang_runtime |
| runtime_file = _clang_runtime_file |
| lib_dir = _clang_lib_dir |
| version_string = clang_version_string |
| version_description = clang_version_description |
| flag_vars = [ |
| "cflags", |
| "ldflags", |
| ] |
| } |
| |
| # Rust toolchain spec for use with toolchain_runtime_deps(). |
| rustc_toolchain_spec = { |
| runtime = _rustc_runtime |
| runtime_file = _rustc_runtime_file |
| lib_dir = _rustc_lib_dir |
| version_string = rustc_version_string |
| version_description = rustc_version_description |
| flag_vars = [ "rustflags" ] |
| } |
| |
| # Provide deps required by toolchain-provided runtime libraries. |
| # |
| # Every linking target, such as executable(), shared_library(), or |
| # loadable_module(), needs this in deps to represent the link-time and |
| # runtime dependencies of support code the compiler links in implicitly. |
| # The parameters indicate the compilation mode in terms of the link-time |
| # and compile-time flags used. These must exactly match lists supplied by |
| # the toolchain in $clang_runtime to select for things like instrumentation |
| # and shared vs static linking of the standard C++ library. |
| # |
| # ## Parameters |
| # |
| # * toolchain_spec |
| # - Required: Path information about the compiler runtimes. |
| # - Type: scope with the following: |
| # |
| # * <flag variables> |
| # - Required: Flags to match in the runtimes manifest. Should have a value for |
| # every flag in toolchain_spec.flag_vars. |
| # - Example: `cflags = [] ldflags = [ "-static-libstdc++" ]` |
| # - Type: list(string) |
| # |
| # * libraries |
| # - Optional: List of library names to include in this target. If not specific |
| # all libraries matching the flag variables in the toolchain spec for the |
| # current variant will be added. |
| # - Type: list(string) |
| # |
| template("toolchain_runtime_deps") { |
| not_needed(invoker, [ "libraries" ]) |
| group(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| |
| toolchain_spec = invoker.toolchain_spec |
| |
| # This information comes out the same in the main and the shlib |
| # toolchains. But we don't want two copies to appear in the metadata |
| # collection, so we always redirect to the shlib toolchain (when there |
| # is one). Note that multiple toolchains (variants that aren't that |
| # different, e.g. uninstrumented variants) may produce identical |
| # manifest entries because they match the same entries in the |
| # clang_runtime and use the same ${toolchain_variant.libprefix} string. That |
| # is less than ideal but it does no harm since the tools like zbi that |
| # consume manifests accept redundant entries if they are identical. |
| if (toolchain_variant.with_shared && current_toolchain != shlib_toolchain) { |
| public_deps = [ ":$target_name($shlib_toolchain)" ] |
| not_needed(invoker, toolchain_spec.flag_vars) |
| } else { |
| if (toolchain_variant.with_shared && defined(visibility)) { |
| visibility += [ ":$target_name" ] |
| } |
| |
| match = false |
| match_flags = { |
| forward_variables_from(invoker, toolchain_spec.flag_vars) |
| } |
| foreach(config, toolchain_spec.runtime) { |
| # Clear value from last iteration. |
| config_flags = { |
| } |
| config_flags = { |
| forward_variables_from(config, toolchain_spec.flag_vars) |
| } |
| if (config_flags == match_flags && |
| config.target + [ current_target_tuple ] - |
| [ current_target_tuple ] != config.target) { |
| assert(match == false, |
| "${toolchain_spec.runtime_file} has multiple matches for" + |
| " --target=${current_target_tuple} + $invoker") |
| match = config |
| } |
| } |
| assert(match != false, |
| "${toolchain_spec.runtime_file} has no match for" + |
| " --target=${current_target_tuple} + $invoker") |
| |
| metadata = { |
| binaries = [] |
| distribution_entries = [] |
| runtime_deps_manifest_lines = [] |
| } |
| |
| _label = get_label_info(":$target_name", "label_with_toolchain") |
| |
| # In many cases, the loop below will be empty. |
| not_needed([ "_label" ]) |
| |
| foreach(lib, match.runtime) { |
| # For build_api_module("binaries") in //BUILD.gn. |
| metadata.binaries += [ |
| { |
| cpu = current_cpu |
| os = current_os |
| label = get_label_info(":$target_name", "label_with_toolchain") |
| type = "shared_library" |
| dist = rebase_path(lib.dist, root_build_dir, toolchain_spec.lib_dir) |
| if (defined(lib.debug)) { |
| debug = |
| rebase_path(lib.debug, root_build_dir, toolchain_spec.lib_dir) |
| if (output_breakpad_syms) { |
| breakpad = rebase_path(lib.breakpad, |
| root_build_dir, |
| toolchain_spec.lib_dir) |
| } |
| } |
| |
| target_tuple = match.target |
| forward_variables_from(match, toolchain_spec.flag_vars) |
| |
| if (toolchain_spec.version_string != "") { |
| toolchain_id = toolchain_spec.version_string |
| } |
| if (toolchain_spec.version_description != "") { |
| toolchain_version = toolchain_spec.version_description |
| } |
| }, |
| ] |
| |
| if (!defined(invoker.libraries) || invoker.libraries + [ lib.name ] - |
| [ lib.name ] != invoker.libraries) { |
| _dist_file = |
| rebase_path(lib.dist, root_build_dir, toolchain_spec.lib_dir) |
| |
| if (defined(lib.soname)) { |
| _soname = lib.soname |
| } else { |
| _soname = get_path_info(lib.dist, "file") |
| } |
| |
| # Used by runtime_deps_manifest(). |
| metadata.runtime_deps_manifest_lines += |
| [ "lib/${toolchain_variant.libprefix}${_soname}=" + |
| rebase_path(lib.dist, root_build_dir, toolchain_spec.lib_dir) ] |
| |
| # Used by the distribution_manifest() template. |
| metadata.distribution_entries += [ |
| { |
| destination = "lib/${toolchain_variant.libprefix}${_soname}" |
| label = _label |
| source = _dist_file |
| }, |
| ] |
| } |
| } |
| } |
| } |
| } |
| |
| # Generate a fini manifest that lists all toolchain runtime dependencies and their |
| # installation location. |
| # |
| # deps, data_deps, public_deps (optional) |
| # [list of labels] The targets to generate a manifest for. |
| # |
| # testonly, visibility (optional) |
| # See `gn help`. |
| # |
| # outputs (optional) |
| # Singleton list containing the path to the manifest file. |
| # Defaults to `[ "$target_gen_dir/$target_name.runtime_deps" ]`. |
| template("toolchain_runtime_deps_manifest") { |
| # Build the name of the output file. |
| if (defined(invoker.outputs)) { |
| _outputs = invoker.outputs |
| assert(_outputs != [] && _outputs == [ _outputs[0] ], |
| "Outputs list must have exactly one element.") |
| manifest_file = _outputs[0] |
| } else { |
| manifest_file = "$target_gen_dir/$target_name.runtime_deps" |
| } |
| |
| generated_file(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "data_deps", |
| "deps", |
| "public_deps", |
| "testonly", |
| ]) |
| data_keys = [ "runtime_deps_manifest_lines" ] |
| walk_keys = [ "runtime_deps_manifest_barrier" ] |
| outputs = [ manifest_file ] |
| } |
| } |