blob: c19e718084d4f1499018bdd097f0b579c37af740 [file] [log] [blame]
# Copyright 2017 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/compiled_action.gni")
import("//build/json/validate_json.gni")
# Defines an SDK element.
#
# Outputs
#
# $target_gen_dir/$target_name.sdk
# A manifest describing what files pertain to the atom and which other atoms
# are required by this atom.
# TODO(DX-340): mention that this file is now purely internal to the build.
#
# $target_gen_dir/$target_name.meta.json
# A metadata file describing the atom.
# This file is included in the final SDK and used to e.g. drive the
# inclusion of the atom in a different build system.
#
# Parameters
#
# domain
# Name of the domain this element too.
# Domains regroup elements of similar nature. Language-based domains are
# most common.
# TODO(DX-340): remove me.
#
# name (optional)
# Name of the element.
# Must be unique within the given domain.
# TODO(DX-340): remove me.
#
# name_file (optional)
# Path to a file containing the name of the element.
# This should be used when the element's name requires some computation in
# its own build target.
# TODO(DX-340): remove me.
#
# NOTE: Exactly one of `name` or `name_file` must be set.
#
# id
# Identifier of this element within SDKs.
# The identifier should represent the canonical base path of the element
# within SDKs according to the standard layout (https://fuchsia.googlesource.com/docs/+/master/development/sdk/layout.md).
# For an element at $ROOT/pkg/foo, the id should be "sdk://pkg/foo".
#
# category
# Describes the availability of the element.
# Possible values, from most restrictive to least restrictive:
# - excluded : the atom may not be included in SDKs;
# - experimental : the atom is available with no quality guarantee;
# - internal : the atom is exposed within the Fuchsia tree;
# - partner : the atom may be used by select partners;
# - public : the atom may be included in published SDKs.
#
# meta
# Scope describing the element's metadata file.
# See the "Metadata scope" section for how to populate this attribute.
#
# files
# List of scopes describing the contents of this element.
# See the "File scopes" section for how to describe files - note that for
# this attribute, "dest" must be specified relatively to the element's root
# in the SDK, not to the SDK root.
# This is a legacy attribute which will soon be replaced with new_files.
#
# new_files (optional)
# List of scopes describing the contents of this element.
# See the "File scopes" section for how to describe files.
# TODO(DX-340): rename to "files"
#
# 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.
#
# deps (optional)
# List of GN labels for other SDK elements this element depends on at build
# time.
# These labels must point to "sdk_atom" targets.
#
# package_deps (optional)
# List of GN labels for other SDK elements this element depends on at
# runtime.
# These labels must point to "sdk_atom" targets.
#
# non_sdk_deps (optional)
# List of GN labels which this target needs built.
#
# tags (optional)
# List of strings characterizing this element.
# Examples: "service:networking", "language:rust", "layer:garnet"
#
# tags_file (optional)
# File containing a set of tags.
# This file must be using the JSON format; the top-level object must be a
# map whose values are strings.
#
# Metadata scope
#
# This scope describes a metadata file to be added to the SDK element. Its
# supported attributes are:
#
# source (optional)
# Path to the metadata file.
#
# value (optional)
# Scope representing the metadata contents.
#
# NOTE: Exactly one of `source` or `value` must be set.
#
# dest (required)
# Path to the metadata file in the SDK, relatively to the SDK root
#
# schema (required)
# Name of the schema for this file, ignoring the extension.
# Metadata files are hosted under //build/sdk/meta.
# If the metadata file conforms to //build/sdk/meta/foo.json, the
# present attribute should have a value of "foo".
#
# File scopes
#
# Each scope describes a file to be added to the SDK element. The supported
# attributes are:
#
# source (required)
# Path to the original file.
# This path may be absolute or relative to the target's directory.
#
# dest (optional)
# Destination path of the file relative to the SDK root.
# the SDK.
# This attribute is required unless "source" points to a file located under
# the target's source directory, in which case the relative path of that
# file to the target's directory will be used as the default value.
#
# packaged (optional)
# Whether this file should be added to Fuchsia packages.
template("sdk_atom") {
assert(defined(invoker.domain), "Must define a domain name")
domain_name = invoker.domain
assert(defined(invoker.category), "Must define an SDK category")
category = invoker.category
assert(defined(invoker.id), "Must define an SDK ID")
assert(defined(invoker.meta), "Must specify some metadata")
meta = invoker.meta
if (defined(invoker.name)) {
name_args = [
"--name",
invoker.name,
]
} else if (defined(invoker.name_file)) {
name_args = [
"--name-file",
rebase_path(invoker.name_file),
]
} else {
assert(false, "Must specify either name or name_file")
}
gn_deps = []
if (defined(invoker.non_sdk_deps)) {
gn_deps = invoker.non_sdk_deps
}
dep_manifests = []
if (defined(invoker.deps)) {
gn_deps += invoker.deps
foreach(dep, invoker.deps) {
gen_dir = get_label_info(dep, "target_gen_dir")
name = get_label_info(dep, "name")
dep_manifests += [ rebase_path("$gen_dir/$name.sdk") ]
}
}
package_dep_manifests = []
if (defined(invoker.package_deps)) {
gn_deps += invoker.package_deps
foreach(dep, invoker.package_deps) {
gen_dir = get_label_info(dep, "target_gen_dir")
name = get_label_info(dep, "name")
dep_manifests += [ rebase_path("$gen_dir/$name.sdk") ]
}
}
assert(defined(invoker.files), "An atom must specify some files")
assert(defined(invoker.new_files), "An atom must specify some files")
file_mappings = []
file_inputs = []
all_files = invoker.files
foreach(file, all_files) {
assert(defined(file.source), "File $file does not specify a source.")
file_inputs += [ file.source ]
source = rebase_path(file.source)
destination = ""
if (defined(file.dest)) {
destination = file.dest
}
prefix = "internal"
if (defined(file.packaged)) {
prefix = "packaged"
}
file_mappings += [ "$prefix:$destination=$source" ]
}
new_file_mappings = []
foreach(file, invoker.new_files) {
assert(defined(file.source), "File $file does not specify a source.")
assert(defined(file.dest), "File $file does not specify a destination.")
file_inputs += [ file.source ]
source = rebase_path(file.source)
new_file_mappings += [ "${file.dest}=${source}" ]
}
source_dir = "."
if (defined(invoker.source_dir)) {
source_dir = invoker.source_dir
}
tags = []
if (defined(invoker.tags)) {
tags = invoker.tags
}
meta_target_name = "${target_name}_meta"
meta_content = invoker.meta
meta_file = "$target_gen_dir/$target_name.meta.json"
if (defined(meta_content.value)) {
# Directly write the value into a file in the output directory.
write_file(meta_file, meta_content.value, "json")
meta_deps = gn_deps
} else {
meta_copy_target_name = "${target_name}_meta_copy"
assert(defined(meta_content.source), "Meta scope needs a source or value")
# Copy the file to a canonical location for access by other rules.
# TODO(DX-340): instead, make sure that all atoms generate their metadata
# file in the right location.
copy(meta_copy_target_name) {
sources = [
meta_content.source,
]
outputs = [
meta_file,
]
deps = gn_deps
}
meta_deps = [ ":$meta_copy_target_name" ]
}
# Verify that the metadata complies with the specified schema.
validate_json(meta_target_name) {
forward_variables_from(invoker, [ "testonly" ])
data = meta_file
schema = "//build/sdk/meta/${meta_content.schema}.json"
sources = [
# This file is imported by all schemas.
"//build/sdk/meta/common.json",
]
public_deps = meta_deps
}
# Add the metadata file to the set of files to include in SDKs.
meta_source = rebase_path(meta_file)
new_file_mappings += [ "${meta.dest}=${meta_source}" ]
# Builds a manifest representing this element.
action(target_name) {
forward_variables_from(invoker, [ "testonly" ])
manifest = "$target_gen_dir/$target_name.sdk"
script = "//build/sdk/create_atom_manifest.py"
public_deps = gn_deps + [
":$meta_target_name",
]
inputs = dep_manifests + file_inputs + [
# Imported by the action's script.
"//build/sdk/sdk_common.py",
]
outputs = [
manifest,
]
args = name_args + [
"--id",
invoker.id,
"--domain",
domain_name,
"--out",
rebase_path(manifest),
"--base",
rebase_path(source_dir),
"--gn-label",
get_label_info(":$target_name", "label_with_toolchain"),
"--category",
category,
"--deps",
] + dep_manifests + [ "--package-deps" ] + package_dep_manifests +
[ "--files" ] + file_mappings + [ "--tags" ] + tags +
[ "--new-files" ] + new_file_mappings
if (defined(invoker.tags_file)) {
args += [
"--tags-file",
rebase_path(invoker.tags_file),
]
}
args += [
"--meta",
meta.dest,
]
}
}