blob: 2a084bb9beec85ac300503e6a253ddd91152b4d9 [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/verify_pragma_once.gni")
import("//build/cpp/verify_public_headers.gni")
import("//build/sdk/plasa/config.gni")
import("//build/sdk/plasa/plasa_fragment_cc.gni")
import("//build/sdk/sdk_atom.gni")
# A source set that can be exported to an SDK.
#
# An equivalent to the built-in source_set which adds an SDK atom declaration to
# allow the set to be included in an SDK as sources.
#
# Parameters
#
# category (required)
# Publication level of the library in SDKs.
# See //build/sdk/sdk_atom.gni.
#
# sdk_area (optional)
# [string] The API area responsible for maintaining this library.
# 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".
#
# sdk_name (required)
# Name of the library in the SDK.
#
# source_dir (optional)
# If set, path to the base directory of the sources.
# This is useful if the sources are generated and therefore not hosted
# directly under the directory where the GN rules are declared.
#
# include_base (optional)
# Path to the root directory for includes.
# Defaults to "include".
#
# build_as_static (optional)
# Whether the sources should be exposed as a static library.
# This is mostly used in the transition of Zircon libraries to the GN build.
# Defaults to false.
#
# non_sdk_deps (optional)
# List of dependencies that should not be reflected in SDKs.
# Mostly useful for code generation.
#
# non_sdk_public_deps (optional)
# List of dependencies that will go into `public_deps` field of `source_set`
# target.
#
# non_sdk_local_deps (optional)
# List of dependencies that will go into `deps` field of `source_set`
# target.
#
# sdk_headers_for_internal_use (optional)
# Out of the headers specified in `public` or `sources`, some headers are
# part of the SDK but not meant for direct inclusion by users, i.e. they are
# only transitively included by other public headers. They usually contain
# implementation details. Re-specify those headers here.
#
# When enumerating the platform surface area (PlaSA), these headers will
# be excluded. See /build/sdk/plasa/plasa_fragment_cc.gni.
#
# See https://fxbug.dev/42068255 for more details about this field.
template("sdk_source_set") {
assert(defined(invoker.category), "Must define an SDK category")
is_internal = invoker.category == "internal"
assert(is_internal || defined(invoker.sdk_name),
"Non-internal sdk_source_set must define an SDK name")
if (defined(invoker.sdk_name)) {
sdk_name = invoker.sdk_name
sdk_root_path = "pkg/${sdk_name}"
} else {
# if not defined by the invoker, the sdk_name is the fully qualified target name with some tweaks. For example:
# //src/lib/fxl:fxl sdk_name = //src/lib/fxl_fxl sdk_root_path= src/lib/fxl_fxl
# //src/lib/fxl:functional sdk_name = //src/lib/fxl_functional sdk_root_path= src/lib/fxl_functional
# //src/lib/fxl/files:files sdk_name = //src/lib/fxl/files_files sdk_root_path= src/lib/fxl/files_files
full_label = get_label_info(":${invoker.target_name}", "label_no_toolchain")
path = get_label_info(full_label, "dir")
path = "${path}_${invoker.target_name}"
sdk_name = path
sdk_root_path = string_replace(path, "//", "")
}
sdk_id = "sdk://${sdk_root_path}"
# Only partner and public categories have API validation
if (invoker.category == "partner" || invoker.category == "public") {
api_reference = "${sdk_name}.api"
if (defined(invoker.api)) {
api_reference = invoker.api
}
}
# Create the native target (source_set or static_library) for the GN build
main_target_name = target_name
sdk_target_name = "${target_name}_sdk"
target_type = "source_set"
if (defined(invoker.build_as_static) && invoker.build_as_static) {
target_type = "static_library"
}
target(target_type, main_target_name) {
deps = []
public_deps = []
forward_variables_from(invoker,
"*",
[
"api",
"category",
"sdk_area",
"include_base",
"metadata",
"non_sdk_deps",
"non_sdk_public_deps",
"non_sdk_local_deps",
"sdk_headers_for_internal_use",
"source_dir",
"testonly",
])
if (defined(visibility)) {
visibility += [ ":${sdk_target_name}" ]
}
if (defined(invoker.non_sdk_deps)) {
deps += invoker.non_sdk_deps
public_deps += invoker.non_sdk_deps
}
if (defined(invoker.non_sdk_public_deps)) {
public_deps += invoker.non_sdk_public_deps
}
if (defined(invoker.non_sdk_local_deps)) {
deps += invoker.non_sdk_local_deps
}
# Ensure that 'sdk_source_set()' targets in the 'partner' or 'public' category that are not
# testonly are included in the SDK source_sets allowlist. The allowlist target's `visibility`
# list ensures that the target using this template is in the allowlist.
if ((!defined(invoker.testonly) || !invoker.testonly) &&
(invoker.category == "partner" || invoker.category == "public")) {
deps += [ "//build/sdk:sdk_source_sets_allowlist" ]
}
# TODO(https://fxbug.dev/42079584): All SDK libraries should compile with `sdk_extra_warnings`.
if (invoker.category == "partner" || invoker.category == "public") {
configs += [ "//build/config:sdk_extra_warnings" ]
}
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
}
}
# Walk over dependencies (deps and public_deps) and collect their names (dep_names), labels (all_deps), labels of corresponding
# sdk elements (sdk_deps) and location of their metadata files (sdk_metas)
sdk_metas = []
sdk_deps = []
all_deps = []
dep_names = []
if (defined(invoker.deps)) {
all_deps += invoker.deps
}
if (defined(invoker.public_deps)) {
all_deps += invoker.public_deps
}
foreach(dep, all_deps) {
dep_full_label = get_label_info(dep, "label_no_toolchain")
sdk_dep = "${dep_full_label}_sdk"
sdk_deps += [ sdk_dep ]
gen_dir = get_label_info(sdk_dep, "target_gen_dir")
name = get_label_info(sdk_dep, "name")
sdk_metas += [ "${gen_dir}/${name}.meta.json" ]
dep_names += [ get_label_info(dep, "name") ]
}
# Split invoker.sources into sources and headers.
# If invoker.public is defined, use that as the list of headers. Otherwise, *.h are headers, and the others are sources
all_headers = []
all_sources = []
if (defined(invoker.public)) {
all_headers += invoker.public
}
if (defined(invoker.sources)) {
# If public headers are not defined, pick them from `sources`.
#
# NOTE: If this is an internal SDK library, headers from `sources` are
# always made available so Bazel can find them.
if (is_internal || !defined(invoker.public)) {
foreach(source_file, invoker.sources) {
extension = get_path_info(source_file, "extension")
if (extension == "h") {
all_headers += [ source_file ]
} else {
all_sources += [ source_file ]
}
}
} else {
all_sources += invoker.sources
}
}
# Determine destinations in the SDK for headers and sources.
sdk_metadata_headers = []
sdk_metadata_sources = []
sdk_header_files = []
sdk_files = []
if (defined(invoker.include_base)) {
include_base = invoker.include_base
} else {
if (is_internal) {
# by default, we want internal SDK elements to have their include path similar when used through
# an SDK (eg Bazel SDk) or directly (GN/Ninja), so that source code doesn't need to be changed
# when switching from one to the other.
include_base = "//"
} else {
include_base = "include"
}
}
assert(include_base != "") # Work around for unused variable warning when
# all_headers is empty
include_dest = "${sdk_root_path}/include"
foreach(header, all_headers) {
relative_destination = rebase_path(header, include_base)
destination = "${include_dest}/${relative_destination}"
sdk_metadata_headers += [ destination ]
sdk_header_files += [
{
source = header
dest = destination
},
]
}
sdk_files += sdk_header_files
foreach(source, all_sources) {
sdk_metadata_sources += [ "${sdk_root_path}/${source}" ]
sdk_files += [
{
source = source
dest = "${sdk_root_path}/${source}"
},
]
}
should_verify_pragma = !is_internal
if (should_verify_pragma) {
verify_pragma_target_name = "${target_name}_sdk_pragma"
verify_pragma_once(verify_pragma_target_name) {
headers = all_headers
}
}
verify_public_headers_target = "${target_name}_verify_public_headers"
verify_public_headers(verify_public_headers_target) {
forward_variables_from(invoker, [ "testonly" ])
target_label = ":${target_name}"
headers = all_headers
}
if (generate_plasa_artifacts) {
_plasa_artifacts_target_name = "${main_target_name}_plasa"
plasa_fragment_cc(_plasa_artifacts_target_name) {
forward_variables_from(invoker,
[
"source_dir",
"testonly",
"all_headers",
"all_deps",
"sdk_headers_for_internal_use",
])
file_base = sdk_root_path
}
} else {
not_needed(invoker, [ "sdk_headers_for_internal_use" ])
}
metadata_target_name = "${target_name}_sdk_metadata"
metadata_file = "$target_gen_dir/$target_name.sdk_meta.json"
action(metadata_target_name) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/cpp/gen_sdk_sources_meta_file.py"
inputs = sdk_metas
outputs = [ metadata_file ]
args = [
"--out",
rebase_path(metadata_file, root_build_dir),
"--name",
sdk_name,
"--root",
sdk_root_path,
"--include-dir",
include_dest,
]
args += [ "--deps" ] + rebase_path(sdk_metas, root_build_dir)
args += [ "--dep_names" ] + dep_names
args += [ "--sources" ] + sdk_metadata_sources
args += [ "--headers" ] + sdk_metadata_headers
deps = sdk_deps
}
sdk_atom(sdk_target_name) {
forward_variables_from(invoker,
[
"source_dir",
"testonly",
"category",
"sdk_area",
])
id = sdk_id
if (defined(api_reference)) {
api = api_reference
api_contents = sdk_header_files
}
meta = {
source = metadata_file
dest = "$sdk_root_path/meta.json"
schema = "cc_source_library"
}
files = sdk_files
deps = sdk_deps
non_sdk_deps = [ ":$metadata_target_name" ]
non_sdk_deps += [ ":${verify_public_headers_target}" ]
if (should_verify_pragma) {
non_sdk_deps += [ ":$verify_pragma_target_name" ]
}
if (generate_plasa_artifacts) {
non_sdk_deps += [ ":$_plasa_artifacts_target_name" ]
}
# Explicitly add non-public dependencies, in case some of the source files
# are generated.
if (defined(invoker.non_sdk_deps)) {
non_sdk_deps += invoker.non_sdk_deps
}
if (defined(invoker.non_sdk_public_deps)) {
non_sdk_deps += invoker.non_sdk_public_deps
}
if (defined(invoker.non_sdk_local_deps)) {
non_sdk_deps += invoker.non_sdk_local_deps
}
metadata = {
# Used by the //sdk:sdk_source_set_list build API module.
sdk_source_set_sources = rebase_path(all_sources + all_headers, "//")
}
}
}
set_defaults("sdk_source_set") {
configs = default_common_binary_configs
}