blob: 5dec1341f208fb26c9b70649f4c77fa656755051 [file] [log] [blame]
# Copyright 2021 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/dist/resource.gni")
import("//build/json/validate_json.gni")
# Defines an SDK element.
#
# Direct instantiation of `sdk_element` is not supported. Element authors should
# use specialized templates, e.g. `host_tool_sdk_element`, instead.
#
# Outputs
#
# $target_gen_dir/${target_name}_sdk_metadata.json
# A metadata file describing the element.
# * This file is included in the final SDK and may be used by tools that
# interact with the SDK.
#
# Parameters
#
# schema (required)
# Scope describing the element metadata JSON schema. It contains the
# following fields:
# * source (required)
# A GN label for the JSON schema file constraining this element's
# metadata. The schemata can be found under //sdk/schema.
# * deps (optional)
# A list of GN labels for schemas included by the source. Defaults
# to //sdk/schema/common-00000000.json.
#
# meta (required)
# Scope describing the element metadata that is packaged together with
# the element contents. See `files`. It contains the following fields:
# * contents (required unless `source` is set)
# This field structure is dictated by the JSON schema.
# * source (required if `contents` is not set)
# A source label of the file containing the metadata.
# * dest (optional)
# A path relative to the root of the SDK element in the package. By
# default, this is "${target_name}_sdk_metadata.json".
#
# files (optional)
# List of scopes describing the contents of this element.
# * A file scope has the following attributes:
# source (required)
# A GN label representing the source. Must be absolute,
# beginning with "//".
# dest (required)
# Destination path in the SDK relative to the element root.
#
# deps (optional)
# A list of labels of targets contributing the content to the SDK.
#
# api (optional) [TODO(b/204903307): implement the api parameter]
# Scope describing the API surface of the SDK element. If the contents of
# the precomputed digest differ from the generated one, the build will
# fail and the change will have to be explicitly acknowledged. The scope
# api contains the following fields:
# * digest (required)
# A GN label for a JSON file containing a map of file paths to MD5
# hashes. The file paths are source files in the files scope unless the
# sources field below is set. The digest can be customized by specifying
# a digester tool.
# * sources (required if `files` scope is not set)
# A list of GN sources labels to use for digest computation. In case of a C++
# library, this will be a list of headers, which constitute the element's
# API surface. If absent, the `files` scope is used to generate the
# digest.
# * digester (optional)
# A GN label for a custom digester tool.
#
# Metadata
# sdk_adapter
# The metadata is intended to be consumed only by the
# sdk_element_adapter() template. It will re removed once all SDK build
# targets have been migrated.
# * files
# A list of file scope specifying the mapping between the source and
# destination.
# * source
# The absolute label of the source that is mapped into the SDK>
# * dest
# A path relative to the SDK package root.
# * meta
# A scope specifying the element metadata mapping. The contents are
# identical to the file scope.
template("sdk_element") {
if (defined(invoker.deps)) {
gn_deps = invoker.deps
} else {
gn_deps = []
}
# Validate the schema scope.
assert(defined(invoker.schema), "Must define the schema scope.")
schema = invoker.schema
assert(defined(schema.source), "The schema scope must define a source.")
schema_source = schema.source
if (defined(schema.deps)) {
schema_deps = schema.deps
} else {
schema_deps = [
"//sdk/schema/common-00000000.json",
"//sdk/schema/sdk/sdk_common-00000000.json",
]
}
# Validate the file scope.
if (defined(invoker.files)) {
sdk_files = invoker.files
foreach(file, sdk_files) {
assert(defined(file.source),
"A file mapping '$file' must specify a source.")
assert(defined(file.dest),
"A file mapping '$file must specify a destination.")
}
} else {
sdk_files = []
}
# Validate the metadata scope.
assert(defined(invoker.meta), "Must define the metadata scope.")
element_meta = invoker.meta
assert(defined(element_meta.contents) || defined(element_meta.source),
"Must define either a metadata contents or a source")
assert(!(defined(element_meta.contents) && defined(element_meta.source)),
"Metadata contents and the source are mutually exclusive")
# Prepare and validate the element metadata against the schema.
meta_target = "${target_name}.metadata_generator"
meta_file = "${target_gen_dir}/${target_name}_sdk_metadata.json"
if (defined(element_meta.contents)) {
generated_file(meta_target) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
outputs = [ meta_file ]
contents = element_meta.contents
output_conversion = "json"
}
} else {
copy(meta_target) {
sources = [ element_meta.source ]
outputs = [ meta_file ]
}
}
# Validate the metadata against the schema.
meta_validator_target = "${target_name}.meta_validator"
validate_json(meta_validator_target) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
use_valico = true # Use rust-based validator
data = meta_file
schema = schema_source
sources = schema_deps
deps = [ ":${meta_target}" ]
}
# Map the metadata file so it is packaged together with other files.
element_files = sdk_files
if (defined(element_meta.dest)) {
meta_dest = element_meta.dest
} else {
meta_dest = "${target_name}_sdk_metadata.json"
}
element_files += [
{
source = meta_file
dest = meta_dest
},
]
# TODO(b/216398199): Decide whether to include the schemata
# with element_files.
# Create a distribution manifest for the sdk_package() template.
manifest_target = "${target_name}.manifest_generator"
resource_group(manifest_target) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
files = element_files
deps = [ ":${meta_target}" ] + gn_deps
}
# TODO(b/204903307): .api generation/validation
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = [ ":${meta_validator_target}" ]
public_deps = [
":${manifest_target}",
":${meta_target}",
]
# The metadata is exported for the sole benefit of the sdk_adapter().
# No other template should take a dependency on it.
metadata = {
sdk_adapter = [
{
meta = {
source = meta_file
dest = meta_dest
}
files = sdk_files
},
]
}
}
}
# Produces sdk_molecule-compatible metadata from an sdk_element.
#
# This template converts SDK element metadata into SDK atom metadata, to allow
# SDK atoms to be gradually migrated to sdk_element without breaking the
# monolithic SDK build.
#
# Outputs
#
# $target_gen_dir/${target_name}.sdk
# A manifest describing which files are included in the element.
#
# $target_gen_dir/${target_name}_adapter.meta.json
# A metadata file describing the element.
#
# Parameters
#
# deps (required)
# A single element list containing an sdk_element GN label.
#
# category (optional)
# The SDK atom category. Defaults to `partner`.
template("sdk_element_adapter") {
assert(defined(invoker.deps), "Must define deps.")
if (defined(invoker.category)) {
category = invoker.category
} else {
category = "partner"
}
# Collect GN metadata corresponding to the sdk_element.
gn_meta_collector_target = "${target_name}.gn_meta_collector"
gn_meta_file = "${target_gen_dir}/${target_name}_gn_meta.json"
# .sdk format manifest
atom_manifest_file = "${target_gen_dir}/${target_name}.sdk"
# .meta.json format metadata
atom_meta_file = "${target_gen_dir}/${target_name}_adapter.meta.json"
generated_file(gn_meta_collector_target) {
outputs = [ gn_meta_file ]
data_keys = [ "sdk_adapter" ]
output_conversion = "json"
deps = invoker.deps
}
action(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
depfile = "$target_gen_dir/$target_name.d"
deps = [ ":${gn_meta_collector_target}" ]
public_deps = invoker.deps
outputs = [
atom_meta_file,
atom_manifest_file,
]
script = "//build/sdk/sdk_element_adapter.py"
sources = [ gn_meta_file ]
args = [
"--atom-manifest-path",
rebase_path(atom_manifest_file, root_build_dir),
"--atom-meta-path",
rebase_path(atom_meta_file, root_build_dir),
"--depfile-path",
rebase_path(depfile, root_build_dir),
"--gn-label",
get_label_info(":${target_name}", "label_with_toolchain"),
"--gn-meta-path",
rebase_path(gn_meta_file, root_build_dir),
"--root-build-dir",
root_build_dir,
"--sdk-category",
category,
]
# --root-build-dir exposes the output directory name
no_output_dir_leaks = false
}
}