blob: cbb9dc3c97a12b08b23de531be32092b53a7f2c2 [file] [log] [blame]
# 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")
# NOTE: Keep in sync with //zircon/public/gn/toolchain/toolchain_runtime_deps.gni
# until build unification completes.
# ## 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 `${toolchain.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 = rebase_path("$clang_prefix/../lib", ".", root_build_dir)
_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.
# First, record a map of runtimes for Fuchsia for Asan without -static-libstdc++
_asan_runtimes_map = []
foreach(entry, _clang_runtime) {
if (entry.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 == [ "-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
}
}
}
}
},
]
}
_clang_runtime = []
_clang_runtime = _new_clang_runtime
_rustc_lib_dir = rebase_path("$rustc_prefix/../lib", ".", root_build_dir)
_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.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 + [ clang_target ] -
[ clang_target ] != config.target) {
assert(match == false,
"${toolchain_spec.runtime_file} has multiple matches for" +
" --target=${clang_target} + $invoker")
match = config
}
}
assert(match != false,
"${toolchain_spec.runtime_file} has no match for" +
" --target=${clang_target} + $invoker")
metadata = {
binaries = []
distribution_entries = []
runtime_deps_manifest_lines = []
}
_label = get_label_info(":$target_name", "label_with_toolchain")
# The installation path prefix for runtime libraries depends on the variant.
# Note that fuzzer variants use the base variant name, i.e. without the -fuzzer
# suffix for this prefix.
_libprefix = ""
if (toolchain_variant.instrumented) {
_libprefix = string_replace(toolchain_variant.name, "-fuzzer", "") + "/"
}
# In many cases, the loop below will be empty.
not_needed([
"_label",
"_libprefix",
])
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/${_libprefix}${_soname}=" +
rebase_path(lib.dist, root_build_dir, toolchain_spec.lib_dir) ]
# Used by the distribution_manifest() template.
metadata.distribution_entries += [
{
destination = "lib/${_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 ]
}
}