blob: 70ce42da290a095597611acd4048eb1c16f2acfe [file]
# 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/cpp/sdk_shared_library.gni")
import("//build/cpp/sdk_source_set.gni")
import("//build/cpp/sdk_static_library.gni")
import("//build/toolchain/toolchain_environment.gni")
# LINT.IfChange
# Build a zircon-specific library, possibly in several ways.
#
# This template should be used by Zircon build rules exclusively, and follows
# most of the conventions of the Zircon build `library()` template, which
# differ significantly from those of `library()` in the Fuchsia build.
#
# IMPORTANT NOTE: Due to historical reasons, its behaviour is very different,
# depending on the value of the `is_kernel` global variable.
#
# * When building with a Zircon-specific toolchain, it will create a source_set()
# if the global `is_kernel` is true (meaning building part of the kernel), or
# a static_library() target otherwise. The values of `public`, `sdk`,
# `sdk_area`, `sdk_headers`, `sdk_headers_for_internal_use`, `sdk_name`,
# `sdk_publishable`, and `stable` will be ignored entirely.
#
# This will also create a :headers sub-target, a group used to give access
# to the library's include directory from any dependent. Note that this
# does *not* make the library's headers, listed in `sdk_headers` public.
#
# This behaviour is needed to replicate the Zircon build's behaviour when
# compiling Zircon artefacts.
#
# * When not using a Zircon-specific toolchain, which means the library is built
# either as a Fuchsia user binary or a host binary, `sdk` must be set to determine
# the shape of the library, as must `sdk_headers to list the library's public
# headers, relative to its `include` sub-directory.
#
# If `sdk_publishable` is present, an sdk_*() template will be used, enabling
# the library to be part of the Fuchsia SDK. The value of `sdk_publishable`
# must be a valid SDK category.
# The only allowed value for new instances of this template is `"partner"`.
# This is enforced by the allowlists defined in //build/zircon/BUILD.gn.
# New libraries likely probably need to be added to the partner SDK rather
# than specifying one of the other SDK categories. See
# https://fxbug.dev/333125197 for details.
#
# Any ":<library>.as-needed" sub-targets that appear in `deps` will be
# rewritten into a dependency to the library itself, e.g.:
#
# deps = [ "//zircon/system/ulib/foo:foo.as-needed" ]
#
# will be replaced by:
#
# deps = [ "//zircon/system/ulib/foo:foo" ]
#
# Any ":headers" or ":<library>.headers" sub-targets that appear in
# `public_deps` will be rewritten into a dependency to the library itself,
# e.g.:
#
# public_deps = [ "//zircon/system/ulib/foo:headers" ]
#
# will be replaced by:
#
# public_deps = [ "//zircon/system/ulib/foo" ]
#
# Because the Fuchsia build doesn't support ":headers" sub-targets in
# non-kernel libraries for several technical reasons.
#
# Parameters
#
# sdk
# Optional: A value that indicates whether to build this library as a
# source set, static library or shared library with the Fuchsia build.
# This is required, except when building with Zircon-specific toolchains.
# Note that this name is confusing for historical reasons, because using
# this parameter does not alone make this library exported to the Fuchsia
# SDK (see `sdk_publishable` below for this).
# TODO(https://fxbug.dev/333125197): Rename this parameter.
# Values: "static" or "shared" or "source"
#
# sdk_publishable
# Optional: Indicates that this library can be added to the Fuchsia SDK
# and in which SDK category.
# TODO(https://fxbug.dev/333125197): Remove this parameter.
# Type: string indicating an SDK category.
#
# sdk_area (optional)
# [string] The API area responsible for maintaining this library.
# See //build/sdk/sdk_atom.gni.
# TODO(https://fxbug.dev/333125197): Remove this parameter.
#
# stable (optional)
# Whether this sdk_atom is stabilized. If false, an `.api` file will not be
# generated, and the atom will be marked as unstable in the final IDK.
# Required if `sdk == "source"` and `sdk_publishable` is specified and not
# "internal". Not allowed otherwise.
# TODO(https://fxbug.dev/333125197): Remove this parameter.
#
# public
# Optional: It or `sdk_headers` is required, except if the global
# `is_kernel` is true. Mutually exclusive with `sdk_headers`.
# This must list all the public header files for the library.
# Unlike `sdk_headers`, paths are not relative to `include/`.
# This is primarily used by bazel2gn-generated targets.
#
# sdk_headers
# Optional: It or `public` is required, except if the global
# `is_kernel` is true. Mutually exclusive with `public`.
# This must list all the public header files in the library's `include/`
# directory; names should be relative to `include/`. Despite the name,
# this parameter is not specific to SDK targets (those specifying
# `sdk_publishable`).
# TODO(https://fxbug.dev/333125197): Rename this parameter.
# Type: list(string)
#
# sdk_name
# Optional: Override the name of the library in the SDK. This can be used
# when `target_name` is already used by another SDK atom.
# Only allowed when `sdk_publishable` and `sdk == "source"` are specified.
# (For bazel2gn purposes, it is also allowed when `sdk_publishable` and
# (`sdk == "shared"` or `sdk == "static"`) are specified and its value is
# the same as `target_name`.)
# TODO(https://fxbug.dev/333125197): Remove this parameter.
# Type: string
# Default: `target_name`
#
# sdk_headers_for_internal_use
# Optional: This is always forwarded to the corresponding `sdk_*` template.
# See `sdk_source_set` for example about its significance.
# Unlike `sdk_headers`, names are not relative to `include/` so "include/"
# must be included in the path.
# TODO(https://fxbug.dev/333125197): Remove this parameter.
#
# api
# Path for the file representing the API of this library.
# This can be set when this template is used by a bazel2gn-generated target.
# It should only be set when the library is outside `//sdk/lib` and must
# match the default value for such libraries.
#
# See source_set() for other parameters.
#
template("zx_library") {
# Parameters that are not forwarded to the underlying target by default.
# They must be used or explicitly forwarded when appropriate.
# TODO(https://fxbug.dev/333125197): Remove when removing the parameters.
unforwarded_params = [
"api",
"public",
"sdk",
"sdk_area",
"sdk_headers",
"sdk_headers_for_internal_use",
"sdk_name",
"sdk_publishable",
"stable",
]
assert(defined(invoker.public) != defined(invoker.sdk_headers),
"Exactly one of `public` and `sdk_headers` must be specified.")
# Ensure SDK-specific parameters are only specified for libraries in the SDK.
# The following "sdk" parameters are always allowed:
# * `sdk` is required for the `zircon_toolchain=false` case.
# * `sdk_headers` is often used instead of "public".
assert(defined(invoker.sdk_publishable) ||
(!defined(invoker.api) && !defined(invoker.sdk_area) &&
!defined(invoker.sdk_headers_for_internal_use) &&
!defined(invoker.sdk_name) && !defined(invoker.stable)))
if (zircon_toolchain != false) {
_library_name = target_name
if (toolchain_environment == "kernel") {
# In the kernel proper, zx_library() is always a source_set().
# Everything goes into the kernel and anything unused gets linker GC.
kernel_library_target_type = "source_set"
} else {
kernel_library_target_type = "static_library"
}
# If the library has the same name as its directory, just create
# :headers and :headers.config sub-targets. Otherwise, create
# :foo.headers and :foo.headers.config
if (get_label_info(":$_library_name", "name") ==
get_path_info(get_label_info(":$_library_name", "dir"), "file")) {
_headers_target = "headers"
_headers_config_target = "headers.config"
} else {
_headers_target = "$_library_name.headers"
_headers_config_target = "$_library_name.headers.config"
}
target(kernel_library_target_type, _library_name) {
if (kernel_library_target_type == "static_library") {
complete_static_lib = true
}
forward_variables_from(invoker, "*", unforwarded_params)
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":$_headers_target" ]
}
group(_headers_target) {
# Used to represent header dependencies.
# Direct use of public_configs should be rare but is sometimes needed.
forward_variables_from(invoker,
[
"public_configs",
"public_deps",
"testonly",
"visibility",
])
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [ ":${_headers_config_target}" ]
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ "//zircon/system/public" ]
}
config(_headers_config_target) {
include_dirs = [ "include" ]
}
} else {
assert(
defined(invoker.sdk),
"The `sdk` argument is needed to build a zx_library() with a Fuchsia or host toolchain ($current_toolchain)")
shape = invoker.sdk
sdkable = defined(invoker.sdk_publishable)
if (sdkable) {
assert(
invoker.sdk_publishable == "${invoker.sdk_publishable}",
"Must be an SDK category name if specified - received: ${invoker.sdk_publishable}")
sdk_category = invoker.sdk_publishable
}
assert((sdkable && shape == "source") || !defined(invoker.stable))
extra_target_args = {
}
if (shape == "source") {
if (sdkable) {
assert(
defined(invoker.stable) !=
# TODO(https://fxbug.dev/331991540): Remove "firmware_sdk" once no longer used.
(sdk_category == "internal" || sdk_category == "firmware_sdk"),
"`stable` must be specified except for categories that are always unstable.")
if (defined(invoker.sdk_name)) {
name = invoker.sdk_name
} else {
name = target_name
}
target_type = "sdk_source_set"
extra_target_args = {
build_as_static = true
category = sdk_category
sdk_name = name
forward_variables_from(invoker,
[
"sdk_headers_for_internal_use",
"sdk_area",
"stable",
])
}
} else {
target_type = "static_library"
assert(!defined(invoker.sdk_name))
}
} else if (shape == "static") {
if (sdkable) {
assert(!defined(invoker.sdk_name) || invoker.sdk_name == target_name)
target_type = "sdk_static_library"
extra_target_args = {
category = sdk_category
sdk_name = target_name
libcxx_linkage = "static"
forward_variables_from(invoker,
[
"sdk_headers_for_internal_use",
"sdk_area",
])
}
} else {
assert(!defined(invoker.sdk_name))
target_type = "static_library"
}
} else if (shape == "shared") {
if (sdkable) {
assert(!defined(invoker.sdk_name) || invoker.sdk_name == target_name)
assert(sdk_category == "partner")
target_type = "sdk_shared_library"
extra_target_args = {
category = sdk_category
sdk_name = target_name
libcxx_linkage = "static"
forward_variables_from(invoker,
[
"sdk_headers_for_internal_use",
"sdk_area",
])
}
} else {
assert(!defined(invoker.sdk_name))
target_type = "shared_library"
}
} else {
assert(false, "Unknown library type: $shape")
}
main_target_name = target_name
config_target_name = "$target_name.config"
config(config_target_name) {
visibility = [ ":$main_target_name" ]
include_dirs = [ "include" ]
}
# Ignore an "includes" parameter if it contains exactly the value above.
# This can happen when this template is used by a bazel2gn-generated target.
if (defined(invoker.includes) && invoker.includes == [ "include" ]) {
not_needed(invoker, [ "includes" ])
}
# IMPORTANT: Rewrite dependencies
# ** Rewrite as-needed dependencies in `deps` **
#
# Rewrite private dependencies on ".as-needed" targets into regular
# dependencies into the libraries themselves. For example:
#
# deps = [ "//src/zircon/lib/zircon:zircon.as-needed" ]
#
# will be replaced by:
#
# deps = [ "//src/zircon/lib/zircon:zircon(<same-toolchain>)" ]
#
#
# ** Rewrite header dependencies in `public_deps` **
#
# For each zx_library(), the Zircon build used to produce a 'headers'
# group that adds a 'headers.config' public config. This is done to allow
# other targets to depend on the library's headers, but not the library
# itself.
#
# In practice, this means it is common for targets to list these headers
# groups in their public_deps, as in:
#
# public_deps = [ "//sdk/lib/fit:headers" ]
#
# However, these groups do not mix well with the Fuchsia build, and
# especially with sdk_source_set() or sdk_static_library().
#
# To work around this, rewrite the headers public dependencies into
# regular dependencies into the libraries themselves. For example:
#
# public_deps = [ "//sdk/lib/fit:headers", "//sdk/lib/fit-promise:fit-promise.headers" ]
#
# will be replaced by:
#
# public_deps = [ "//sdk/lib/fit", "//sdk/lib/fit-promise:fit-promise" ]
#
lib_deps = []
if (defined(invoker.deps)) {
# Rewrite any foo.as-needed deps to just foo.
foreach(label, invoker.deps) {
if (get_path_info(get_label_info(label, "name"), "extension") ==
"as-needed") {
label = get_label_info(label, "dir") + ":" +
get_path_info(get_label_info(label, "name"), "name") + "(" +
get_label_info(label, "toolchain") + ")"
}
lib_deps += [ label ]
}
}
lib_public_deps = []
if (defined(invoker.public_deps)) {
foreach(dep, invoker.public_deps) {
if (get_label_info(dep, "name") == "headers") {
# Format //zircon/.../foo:headers -> //zircon/.../foo
dep = get_label_info(dep, "dir")
} else if (get_path_info(get_label_info(dep, "name"), "extension") ==
"headers") {
# Format //zircon/.../foo:bar.headers -> //zircon/.../foo:bar
dep = get_label_info(dep, "dir") + ":" +
get_path_info(get_label_info(dep, "name"), "name")
}
lib_public_deps += [ dep ]
}
}
if (!is_fuchsia) {
lib_public_deps += [ "//zircon/system/public" ]
}
_has_public_headers =
(defined(invoker.public) && invoker.public != []) ||
(defined(invoker.sdk_headers) && invoker.sdk_headers != [])
target(target_type, main_target_name) {
forward_variables_from(invoker,
"*",
unforwarded_params + [
"deps",
"public_deps",
])
forward_variables_from(extra_target_args, "*")
deps = lib_deps
public_deps = lib_public_deps
if (sdkable && sdk_category == "partner") {
target_path = get_label_info(target_name, "label_no_toolchain")
is_in_sdk_dir = target_path ==
"//sdk/" + string_replace(target_path, "//sdk/", "", 1)
if (!is_in_sdk_dir) {
# For targets outside `//sdk` that require modification
# acknowledgement, override the default `.api` file location with
# one under `//sdk/lib`.
api = "//sdk/lib/$target_name/$target_name.api"
# bazel2gn may generate "api". Ignore it after confirming it matches.
if (defined(invoker.api)) {
assert(api == invoker.api, api)
}
}
}
if (_has_public_headers) {
if (defined(invoker.public)) {
public = invoker.public
} else {
public = []
foreach(header, invoker.sdk_headers) {
public += [ "include/$header" ]
}
}
}
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [ ":$config_target_name" ]
if (!defined(defines)) {
defines = []
}
defines += [ "_ALL_SOURCE" ]
if (!defined(data_deps)) {
data_deps = []
}
# Add allowlist to data_deps rather than deps to not trigger SDK deps
# logic
data_deps += [ "//build:deprecated_zx_wrapper_allowlist" ]
if (sdkable) {
# Prevent additional confusing uses of `sdk_publishable` per its
# documentation.
if (sdk_category == "partner") {
data_deps += [ "//build/zircon:sdk_category_partner_allowlist" ]
} else if (sdk_category == "internal") {
data_deps += [ "//build/zircon:sdk_category_internal_allowlist" ]
} else if (sdk_category == "firmware_sdk") {
data_deps += [ "//build/zircon:sdk_category_firmware_allowlist" ]
} else {
assert(
false,
"Unsupported SDK category '${sdk_category}' - see https://fxbug.dev/333125197.")
}
}
}
}
# Uncomment the below to generate the allowlist
#print("\"" + get_label_info(":$target_name", "dir") + "/*\",")
}
set_defaults("zx_library") {
configs = default_common_binary_configs
}
# LINT.ThenChange(//build/bazel/rules/idk/private/idk_cc_prebuilt_library.bzl,
# //build/bazel/rules/idk/private/idk_cc_source_library.bzl,
# //build/bazel/rules/zx_library.bzl)