blob: 522fd87fcae5520e8cdcabe668bdee06e2b7aea5 [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/config/clang/clang_toolchain_info.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 tuples a la `${current_target_tuple}`.
# - Type: list(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}`
#
# Used internally by clang_runtime_deps and rust_runtime_deps.
# Define a group() target with the metadata matching a specific
# set of runtime libraries.
#
# Args:
# entry [scope | bool]
# Either a runtime.json input scope, or the boolean value "false"
# to indicate an empty group.
#
# libraries: [optional string list]
# An optional list of library names to embed
# in Fuchsia packages. If not provided, all runtime libraries from entry
# will be used.
#
# rebased_toolchain_lib_dir [string]
# The rebased path to the toolchain's "/lib" sub-directory.
#
# binary_common [scope]
# A scope containing common arguments for each entry in the
# final metadata.binaries list for the generated group().
#
# testonly, visibility
# Usual GN meaning.
template("_define_runtime_group") {
_rebased_lib_dir = invoker.rebased_toolchain_lib_dir
_entry = invoker.entry
if (defined(invoker.libraries)) {
_libraries = invoker.libraries
} else {
_libraries = []
}
_label = get_label_info(":$target_name", "label_with_toolchain")
# scope containing shared values for all entries in _binaries.
_binary_base = {
forward_variables_from(invoker.binary_common, "*")
cpu = current_cpu
os = current_os
label = _label
type = "shared_library"
}
_binaries = []
_distribution_entries = []
_debug_symbol_files = []
if (_entry != false) {
# Add all runtime libraries for this entry to metadata.binaries even
# though they are not build outputs, as these are only used to upload the
# corresponding symbol files to cloud storage.
# See https://fxbug.dev/379848690
foreach(lib, _entry.runtime) {
_dist_file = "${_rebased_lib_dir}/${lib.dist}"
_binaries += [
{
forward_variables_from(_binary_base, "*")
dist = _dist_file
if (defined(lib.debug)) {
debug = "${_rebased_lib_dir}/${lib.debug}"
if (defined(lib.breakpad)) {
breakpad = "${_rebased_lib_dir}/${lib.breakpad}"
}
}
},
]
if (defined(lib.debug)) {
# Compute build-id from lib.debug if possible. Look for ..../.build-id/xx/yyyyyy.debug
_build_id = ""
_lib_debug_nosuffix = string_replace(lib.debug, ".debug", "")
_has_debug_suffix = _lib_debug_nosuffix != lib.debug
if (_has_debug_suffix) {
_build_id_list = []
_build_id_list = string_split(_lib_debug_nosuffix, ".build-id/")
_is_under_build_id = _build_id_list != [ _lib_debug_nosuffix ]
if (_is_under_build_id) {
_build_id = _build_id_list[1]
_build_id = string_replace(_build_id, "/", "")
}
}
_debug_symbol_files += [
{
cpu = current_cpu
os = current_os
label = _label
debug = "${_rebased_lib_dir}/${lib.debug}"
if (_build_id != "") {
elf_build_id = _build_id
}
if (defined(lib.breakpad)) {
breakpad = "${_rebased_lib_dir}/${lib.breakpad}"
}
},
]
}
# On the other hand, only add entries to metadata.distribution_entries
# for the libraries that really need to be packaged into dependent
# Fuchsia packages.
if (_libraries == [] ||
_libraries + [ lib.name ] - [ lib.name ] != _libraries) {
_distribution_entries += [
{
destination = "lib/${toolchain_variant.libprefix}${lib.soname}"
label = _label
source = _dist_file
},
]
}
}
}
# In many cases, the loop above will be empty.
not_needed([
"_label",
"_libraries",
"_binary_base",
"_rebased_lib_dir",
])
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
metadata = {
# Used by //:binaries build API module
binaries = _binaries
# Used by distribution_manifest() template.
distribution_entries = _distribution_entries
# Used by //:debug_symbols build API module.
debug_symbol_files = _debug_symbol_files
}
}
}
# Define a group() target carrying the metadata related to the libraries
# needed at runtime when linking against a static or shared version of libc++
# for the current build variant.
#
# Args:
# static_libcxx [bool]: Set to true for runtime dependencies of
# code linked with -static-libstdc++.
#
# testonly, visibility: Usual GN meaning.
#
template("clang_runtime_deps") {
if (toolchain_variant.with_shared && current_toolchain != shlib_toolchain) {
group(target_name) {
public_deps = [ ":$target_name($shlib_toolchain)" ]
}
not_needed(invoker, [ "static_libcxx" ])
} else {
import("//build/config/clang/clang_prefix.gni")
import("//build/config/clang/clang_toolchain_info.gni")
import("//build/config/current_target_tuple.gni")
import("//build/toolchain/zircon/clang.gni")
_variant_cflags = []
_variant_tags = toolchain_variant.tags
if (_variant_tags + [ "asan" ] - [ "asan" ] != _variant_tags) {
_variant_cflags = [ "-fsanitize=address" ]
} else if (_variant_tags + [ "hwasan" ] - [ "hwasan" ] != _variant_tags) {
_variant_cflags = [ "-fsanitize=hwaddress" ]
} else if (_variant_tags + [ "ubsan" ] - [ "ubsan" ] != _variant_tags) {
_variant_cflags = [ "-fsanitize=undefined" ]
}
_variant_ldflags = []
if (invoker.static_libcxx) {
_variant_ldflags = [ "-static-libstdc++" ]
}
_target_tuple = current_target_tuple
# Find matching entry in runtime.json
_entry = false
_clang_runtimes = clang_toolchain_info.runtimes
foreach(entry, _clang_runtimes) {
if (entry.cflags == _variant_cflags &&
entry.ldflags == _variant_ldflags &&
entry.target + [ _target_tuple ] - [ _target_tuple ] !=
entry.target) {
assert(
_entry == false,
"Multiple matches for cflags=${_variant_cflags}, ldflags=${_variant_ldflags} and ${_target_tuple} in runtime.json. File is ill-formed!")
_entry = entry
}
}
_define_runtime_group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
entry = _entry
rebased_toolchain_lib_dir = "$rebased_clang_dir/lib"
binary_common = {
cflags = _variant_cflags
ldflags = _variant_ldflags
if (clang_version_string != "") {
toolchain_id = clang_version_string
}
if (clang_version_description != "") {
toolchain_version = clang_version_description
}
}
}
}
}
# Define a group() target carrying the metadata related to a Rust runtime
# library.
#
# Args:
# name [string]: Library name prefix (e.g. "libstd" or "libtest").
# shared_runtime [bool]: Set to true to specify the shared version of
# the runtime.
#
# testonly, visibility: Usual GN meaning.
#
template("rust_runtime_deps") {
if (toolchain_variant.with_shared && current_toolchain != shlib_toolchain) {
group(target_name) {
public_deps = [ ":$target_name($shlib_toolchain)" ]
}
not_needed(invoker,
[
"name",
"shared_runtime",
])
} else {
import("//build/rust/config.gni")
if (invoker.shared_runtime) {
_rustflags = [ "-Cprefer-dynamic" ]
} else {
_rustflags = []
}
_target_tuple = current_target_tuple
not_needed(invoker, [ "name" ])
# Find matching entry in Rust runtime.json
# This must use the same rustflags, target tuple and must contain
#
_entry = false
foreach(entry, rustc_runtime) {
if (entry.rustflags == _rustflags && entry.target + [ _target_tuple ] -
[ _target_tuple ] != entry.target) {
# Need to find a library in entry.runtime that matches invoker.name
foreach(runtime, entry.runtime) {
if (runtime.name == invoker.name) {
assert(
_entry == false,
"Multiple matches for rustflags=${_rustflags} and ${_target_tuple} in rust runtime.json. File is ill-formed!")
_entry = entry
}
}
}
}
_define_runtime_group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
entry = _entry
rebased_toolchain_lib_dir = rebase_path(rustc_lib_dir, root_build_dir)
libraries = [ invoker.name ]
binary_common = {
rustflags = _rustflags
if (rustc_version_string != "") {
toolchain_id = rustc_version_string
}
if (rustc_version_description != "") {
toolchain_version = rustc_version_description
}
}
}
}
}