blob: b77d9add9b0419710b9a9145629d4ca229ead339 [file] [log] [blame]
# Copyright 2018 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/cpp/extract_public_symbols.gni")
import("//build/cpp/verify_pragma_once.gni")
import("//build/cpp/verify_public_symbols.gni")
import("//build/sdk/sdk_atom.gni")
import("//build/sdk/sdk_atom_alias.gni")
# A shared library that can be exported to an SDK in binary form.
#
# Parameters
#
# category (required)
# Publication level of the library in SDKs.
# See //build/sdk/sdk_atom.gni.
#
# api (optional)
# Path to the file representing the API of this library.
# This file is used to ensure modifications to the library's API are
# explicitly acknowledged. It is mandatory for publication categories of
# "partner" or "public".
# Defaults to "<SDK name>.api".
#
# symbols_api (optional)
# Path to the file containing the public symbols of this library.
# This file is used to ensure modifications to the library's API are
# explicitly acknowledged. It is mandatory for publication categories of
# "partner" or "public".
# Default to "<SDK name>.symbols.api".
#
# no_headers (optional)
# Whether to include the library's headers in the SDK.
# Defaults to false.
#
# libcxx_linkage (optional)
# Whether or how to link libc++. SDK shared libraries cannot link libc++.so
# dynamically because libc++.so does not have a stable ABI. Can be either
# "none" or "static".
# Defaults to "none".
#
# sdk_name (optional)
# Name of the library in the SDK.
# Defaults to the library's output name.
#
# include_base (optional)
# Path to the root directory for includes.
# Defaults to "include".
#
# runtime_deps (optional)
# List of labels representing the library's runtime dependencies. This is
# only needed for runtime dependencies inherited from private dependencies.
# Note that these labels should represent SDK targets.
# The defaults for a sdk_shared_library should match that of a shared_library.
set_defaults("sdk_shared_library") {
configs = default_shared_library_configs
}
template("sdk_shared_library") {
assert(defined(invoker.category), "Must define an SDK category")
output_name = target_name
if (defined(invoker.output_name)) {
output_name = invoker.output_name
}
if (defined(invoker.sdk_name)) {
atom_name = invoker.sdk_name
} else {
atom_name = output_name
}
no_headers = defined(invoker.no_headers) && invoker.no_headers
if ((invoker.category == "partner" || invoker.category == "public") &&
!no_headers) {
api_reference = "${atom_name}.api"
if (defined(invoker.api)) {
api_reference = invoker.api
}
}
main_target_name = target_name
metadata_target_name = "${target_name}_sdk_metadata"
manifest_target_name = "${target_name}_sdk_manifest"
verify_pragma_target_name = "${target_name}_sdk_pragma"
extract_public_symbols_target_name =
"${target_name}_sdk_extract_public_symbols"
verify_public_symbols_target_name = "${target_name}_sdk_verify_public_symbols"
sdk_target_name = "${target_name}_sdk"
shared_library(main_target_name) {
forward_variables_from(invoker,
"*",
[
"api",
"category",
"include_base",
"no_headers",
"runtime_deps",
"sdk_name",
"symbols_api",
])
if (defined(visibility)) {
visibility += [ ":$manifest_target_name" ]
}
if (!defined(libcxx_linkage)) {
libcxx_linkage = "none"
}
assert(libcxx_linkage == "none" || libcxx_linkage == "static")
# Prebuilt shared libraries are eligible for inclusion in the SDK. We do not
# want to dynamically link against libc++.so because we let clients bring
# their own toolchain, which might have a different C++ Standard Library or
# a different C++ ABI entirely.
if (!defined(configs)) {
configs = []
}
if (libcxx_linkage == "static") {
configs += [ "//build/config/fuchsia:static_cpp_standard_library" ]
} else {
# Adding this linker flag keeps us honest about not commiting to a
# specific C++ ABI. If this flag is causing your library to not
# compile, consider whether your library really ought to be in the SDK.
# If so, consider including your library in the SDK as source rather than
# precompiled. If you do require precompilation, you probably need to
# find a way not to depend on dynamically linking C++ symbols because C++
# does not have a sufficiently stable ABI for the purposes of our SDK.
configs += [ "//build/config/fuchsia:no_cpp_standard_library" ]
}
# Request that the runtime deps be written out to a file. This file will be
# used later to verify that all runtime deps are available in the SDK.
write_runtime_deps = "$target_out_dir/$target_name.runtime_deps"
}
# Base path for source files of this library in SDKs.
file_base = "pkg/$atom_name"
# Base path for binaries of this library in SDKs.
prebuilt_base = "arch/$target_cpu"
# Identify dependencies and their metadata files.
sdk_deps = []
sdk_metas = []
# If a prebuilt library is only provided for packaging purposes (by not
# exposing headers) then its dependencies need not be included in an SDK.
if (defined(invoker.public_deps) && !no_headers) {
foreach(dep, invoker.public_deps) {
full_label = get_label_info(dep, "label_no_toolchain")
sdk_dep = "${full_label}_sdk"
sdk_deps += [ sdk_dep ]
}
}
# Runtime deps are already SDK targets.
if (defined(invoker.runtime_deps)) {
sdk_deps += invoker.runtime_deps
}
foreach(sdk_dep, sdk_deps) {
gen_dir = get_label_info(sdk_dep, "target_gen_dir")
name = get_label_info(sdk_dep, "name")
sdk_metas += [ rebase_path("$gen_dir/$name.meta.json") ]
}
# Process headers.
all_headers = []
if ((defined(invoker.public) || defined(invoker.sources)) && !no_headers) {
if (defined(invoker.public)) {
all_headers += invoker.public
} else {
foreach(source_file, invoker.sources) {
extension = get_path_info(source_file, "extension")
if (extension == "h") {
all_headers += [ source_file ]
}
}
}
}
sdk_metadata_headers = []
sdk_header_files = []
foreach(header, all_headers) {
include_base = "include"
if (defined(invoker.include_base)) {
include_base = invoker.include_base
}
destination = rebase_path(header, include_base)
header_dest = "$file_base/include/$destination"
sdk_metadata_headers += [ header_dest ]
sdk_header_files += [
{
source = header
dest = header_dest
},
]
}
# Add binaries.
shared_out_dir = get_label_info(":bogus($shlib_toolchain)", "root_out_dir")
lib_name = "lib$output_name.so"
link_lib = "$prebuilt_base/lib/$lib_name"
dist_lib = "$prebuilt_base/dist/$lib_name"
sdk_files =
sdk_header_files + [
{
# TODO(fxbug.dev/2875): put ABI stubs under lib/, not the full thing.
source = "$shared_out_dir/$lib_name"
dest = link_lib
},
{
source = "$shared_out_dir/$lib_name"
dest = dist_lib
},
]
metadata_file = "$target_gen_dir/$metadata_target_name.sdk_meta.json"
debug_mapping_file = "$target_gen_dir/$metadata_target_name.mappings.txt"
debug_lib_file = "$shared_out_dir/lib.unstripped/$lib_name"
action(metadata_target_name) {
script = "//build/cpp/gen_sdk_prebuilt_meta_file.py"
inputs = sdk_metas + [ debug_lib_file ]
outputs = [
debug_mapping_file,
metadata_file,
]
args = [
"--out",
rebase_path(metadata_file),
"--name",
atom_name,
"--format",
"shared",
"--root",
file_base,
"--include-dir",
"$file_base/include",
"--dist-path",
"lib/$lib_name",
"--deps",
] + sdk_metas + [ "--headers" ] + sdk_metadata_headers +
[
"--arch",
target_cpu,
"--lib-link",
link_lib,
"--lib-dist",
dist_lib,
"--lib-debug-file",
rebase_path(debug_lib_file),
"--debug-mapping",
rebase_path(debug_mapping_file),
]
deps = sdk_deps + [ ":$main_target_name" ]
}
verify_pragma_once(verify_pragma_target_name) {
headers = all_headers
}
main_target_dir = get_label_info(":$main_target_name", "dir")
reference_symbols_file = "$main_target_dir/${output_name}.symbols.api"
if (defined(invoker.symbols_api)) {
reference_symbols_file = invoker.symbols_api
}
generated_symbols_file = "$target_gen_dir/${output_name}.symbols.api"
extract_public_symbols(extract_public_symbols_target_name) {
library = "$shared_out_dir/$lib_name"
deps = [ ":$main_target_name" ]
symbols = generated_symbols_file
}
verify_public_symbols(verify_public_symbols_target_name) {
current = generated_symbols_file
reference = reference_symbols_file
library_name = main_target_dir
deps = [ ":$extract_public_symbols_target_name" ]
}
sdk_atom(manifest_target_name) {
forward_variables_from(invoker, [ "testonly" ])
id = "sdk://pkg/$atom_name"
category = invoker.category
if (defined(api_reference) && !no_headers) {
api = api_reference
api_contents = sdk_header_files
}
meta = {
source = metadata_file
dest = "$file_base/meta.json"
schema = "cc_prebuilt_library"
}
files = sdk_files
file_list = debug_mapping_file
deps = sdk_deps
non_sdk_deps = [
":$main_target_name",
":$metadata_target_name",
":$verify_pragma_target_name",
":$verify_public_symbols_target_name",
]
# Explicitly add non-public dependencies, in case some of the source files
# are generated.
if (defined(invoker.deps)) {
non_sdk_deps += invoker.deps
}
}
shared_gen_dir = get_label_info(":bogus($shlib_toolchain)", "target_out_dir")
runtime_deps_file = "$shared_gen_dir/$target_name.runtime_deps"
sdk_manifest_file = "$target_gen_dir/$manifest_target_name.sdk"
verify_target_name = "${target_name}_verify"
# Verify that the SDK manifest for this target includes all of the expected
# runtime dependencies.
# TODO(fxbug.dev/5365): also check that everything in there is either prebuilt or
# headers only.
action(verify_target_name) {
script = "//build/cpp/verify_runtime_deps.py"
inputs = [
sdk_manifest_file,
runtime_deps_file,
]
stamp_file = "$target_gen_dir/$target_name.gn_stamp"
outputs = [ stamp_file ]
args = [
"--stamp",
rebase_path(stamp_file),
"--manifest",
rebase_path(sdk_manifest_file),
"--runtime-deps-file",
rebase_path(runtime_deps_file),
"--root-build-dir",
rebase_path(root_build_dir),
]
deps = [
":$main_target_name",
":$manifest_target_name",
]
}
sdk_atom_alias(sdk_target_name) {
atom = ":$manifest_target_name"
non_sdk_deps = [ ":$verify_target_name" ]
}
}