blob: 5779a8a820e6ae90bcb145698a03e0ae755ee002 [file] [log] [blame] [edit]
# 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/compiled_action.gni")
import("//build/json/validate_json.gni")
import("config.gni")
import("meta/version.gni")
import("sdk_molecule.gni")
# Prepare a collection of elements to be published in an IDK.
#
# This creates the $BUILD_DIR/sdk/exported/{sdk_name}/ directory, which will
# follow the standard IDK layout, including a top-level meta/manifest.json
# file describing all atoms in the collection using the standard IDK schema.
#
# It also writes the $BUILD_DIR/sdk/manifest/{sdk_name} file, as an internal
# SDK manifest describing all atoms as well. Note that this format is internal
# to the build system, and should not be used outside of it (though it is
# ok for `ffx` to use that from a Fuchsia checkout).
#
# If `generate_archive` is true, this will also create a compressed tarball
# under $BUILD_DIR/sdk/archive/{sdk_name}.tar.gz, which will store the same
# files as the exported directory described above.
#
# This last feature is only provided for legacy reasons and will be removed
# soon. IDK archives should be generated with generate_final_idk()
# instead.
#
# Parameters
#
# name (optional)
# Name of the SDK.
# Defaults to the target's name.
#
# id (optional)
# An opaque identifier for the SDK.
# Defaults to the empty string.
#
# api (required)
# Path to the file representing the canonical contents of this SDK.
# This file is used to ensure modifications to the SDK contents are
# explicitly acknowledged.
# Mandatory for "public" or "partner" SDKs.
#
# category (required)
# Describes the minimum category that atoms in this SDK must have.
# See //build/sdk/sdk_atom.gni for possible values.
#
# filter_api_host_tools (optional)
# Boolean. Set to true to ignore host tools declarations in the api file
# that do not belong to the current host toolchain. Useful when
# host tools are not cross-compiled for several host architectures
# (see sdk_cross_compile_host_tools in //sdk/config.gni for details).
#
# generate_archive (optional)
# Boolean. Set to true to generate a compressed archive for this collection.
# This feature is deprecated and will be removed when all clients
# will rely on IDK archives instead. Defaults to false.
#
# deps
# A list of sdk_atom(), sdk_molecule() targets that will be walked to
# collect all SDK atom dependencies for this collection.
#
# testonly
# Usual GN meaning.
#
template("sdk_collection") {
assert(defined(invoker.category), "Must define an SDK category")
if (invoker.category == "public" || invoker.category == "partner") {
assert(defined(invoker.api), "API file required for public/partner SDKs")
}
# Compute target and host triples, which will be written to the
# top-level meta/manifest.json file for this collection.
target_triple = target_cpu
if (host_cpu == "x64") {
host_triple_cpu = "x86_64"
} else if (host_cpu == "arm64") {
host_triple_cpu = "aarch64"
} else {
assert(false, "Unrecognized host CPU: $host_cpu")
}
if (host_os == "linux") {
host_triple_os = "linux-gnu"
} else if (host_os == "mac") {
host_triple_os = "apple-darwin"
} else if (host_os == "fuchsia") {
host_triple_os = "fuchsia"
} else {
assert(false, "Unrecognized host OS: $host_os")
}
host_triple = "$host_triple_cpu-$host_triple_os"
# The name used for this collection's output directory and archives.
sdk_name = target_name
if (defined(invoker.name)) {
sdk_name = invoker.name
}
# Labels and file paths for all targets generated by this template.
labels = {
export = "${target_name}_export"
final_archive = "${target_name}_archive"
final_manifest = "${target_name}_final_manifest"
local_archive = "${target_name}_local_archive"
local_manifest = "${target_name}_molecule"
main = target_name
sdk_meta = "${target_name}_sdk_meta"
tarmaker_manifest = "${target_name}_tarmaker_manifest"
verify_api = "${target_name}_verify_api"
verify_manifest = "${target_name}_verify_manifest"
verify_sdk_meta = "${target_name}_verify_sdk_meta"
}
files = {
final_archive = "${root_out_dir}/sdk/archive/${sdk_name}.tar.gz"
final_manifest = "$root_out_dir/sdk/manifest/$sdk_name"
local_archive = "$target_gen_dir/$target_name.tar.gz"
local_manifest = "$target_gen_dir/${labels.main}.sdk"
sdk_meta = "$target_gen_dir/$sdk_name.sdk_meta.json"
tarmaker_manifest = "$target_gen_dir/${sdk_name}.tarmaker_manifest"
verify_api = "$root_out_dir/sdk/api/$sdk_name"
}
# Generates an internal molecule target, which creates an internal SDK
# manifest file describing all atoms in it.
sdk_molecule(labels.local_manifest) {
# Do not expose this molecule to other targets. Depending directly on the
# contents of an SDK foregoes API verification, which is not desirable.
visibility = [ ":*" ]
# Ensure the internal manifest is named foo.sdk instead of foo_molecule.sdk
output_name = labels.main
forward_variables_from(invoker,
[
"assert_no_deps",
"category",
"deps",
"testonly",
])
if (!defined(deps)) {
deps = []
}
deps += [ "//build/sdk/meta" ]
}
_verify_api = defined(invoker.api)
if (_verify_api) {
base_reference_manifest = ""
api_reference = invoker.api
api_deps = []
if (defined(invoker.filter_api_host_tools) &&
invoker.filter_api_host_tools) {
# Remove any host tools that does not belong to the current
# host_cpu architecture from the manifest, and use the
# result for verification.
host_api =
"$target_gen_dir/${labels.local_manifest}.host_tools_only.manifest"
host_api_target = "${labels.local_manifest}.host_tools_only.api"
action(host_api_target) {
script = "//build/sdk/filter_host_tools_from_sdk_api.py"
inputs = [ invoker.api ]
outputs = [ host_api ]
args = [
"--host_cpu=${host_cpu}",
"--input",
rebase_path(inputs[0], root_build_dir),
"--output",
rebase_path(outputs[0], root_build_dir),
"--host-tool-prefix",
"sdk://tools/",
]
}
base_reference_manifest = api_reference
api_reference = host_api
api_deps = [ ":$host_api_target" ]
}
# Verify that the contents of the SDK have not changed.
action(labels.verify_api) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/sdk/verify_sdk_api.py"
inputs = [ files.local_manifest ]
outputs = [ files.verify_api ]
args = [
"--manifest",
rebase_path(inputs[0], root_build_dir),
"--updated",
rebase_path(outputs[0], root_build_dir),
]
if (sdk_no_host_tools) {
args += [ "--ensure-no-host-tools" ]
}
if (defined(invoker.api)) {
inputs += [ api_reference ]
args += [
"--reference",
rebase_path(api_reference, root_build_dir),
]
deps = api_deps
}
if (warn_on_sdk_changes) {
args += [ "--warn" ]
}
if (base_reference_manifest != "") {
args += [
"--base_reference",
rebase_path(base_reference_manifest, root_build_dir),
]
}
public_deps = [ ":${labels.local_manifest}" ]
}
}
# Copies the manifest to a central location + verify the collection API if
# needed.
copy(labels.final_manifest) {
forward_variables_from(invoker, [ "testonly" ])
sources = [ files.local_manifest ]
outputs = [ files.final_manifest ]
deps = [ ":${labels.local_manifest}" ]
metadata = {
# Used by distribution_manifest() template. Ensure that dependencies are not
# installed into Fuchsia packages that depend on the current target.
distribution_entries_barrier = []
}
}
# Verifies that the manifest is valid.
validate_json(labels.verify_manifest) {
forward_variables_from(invoker, [ "testonly" ])
data = files.final_manifest
schema = "//build/sdk/manifest_schema.json"
deps = [ ":${labels.final_manifest}" ]
}
# Generates a metadata file (meta/manifest.json) describing the various
# parts of the SDK collection.
action(labels.sdk_meta) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/sdk/generate_meta.py"
inputs = [
files.local_manifest,
"//build/sdk/sdk_common.py",
]
outputs = [ files.sdk_meta ]
args = [
"--manifest",
rebase_path(files.local_manifest, root_build_dir),
"--meta",
rebase_path(files.sdk_meta, root_build_dir),
"--target-arch",
target_triple,
"--host-arch",
host_triple,
"--schema-version",
sdk_metadata_schema_version,
]
if (defined(invoker.id) && invoker.id != "") {
args += [
"--id",
invoker.id,
]
}
public_deps = [ ":${labels.local_manifest}" ]
}
# Verifies that the metadata manifest is valid.
validate_json(labels.verify_sdk_meta) {
forward_variables_from(invoker, [ "testonly" ])
data = files.sdk_meta
schema = "//build/sdk/meta/manifest.json"
sources = [
# This file is imported by all schemas.
"//build/sdk/meta/common.json",
]
public_deps = [ ":${labels.sdk_meta}" ]
}
additional_archive_files = [
{
source = files.sdk_meta
dest = "meta/manifest.json"
deps = [ ":${labels.sdk_meta}" ]
},
]
# Generate an input tarmaker manifest that will be used to create
# a tarball archive from the content listed in final_manifest.
action(labels.tarmaker_manifest) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/sdk/generate_archive_manifest.py"
sources = [ "//build/sdk/sdk_common.py" ]
inputs = [ files.final_manifest ]
outputs = [ files.tarmaker_manifest ]
args = [
"--manifest",
rebase_path(inputs[0], root_build_dir),
"--output",
rebase_path(outputs[0], root_build_dir),
]
deps = [ ":${labels.final_manifest}" ]
foreach(file, additional_archive_files) {
inputs += [ file.source ]
args += [
"--mapping",
file.dest,
rebase_path(file.source, root_build_dir),
]
deps += file.deps
}
}
# Populate the content of the $BUILD_DIR/sdk/exported/{name}/
# directory from the content of the archive manifest.
action(labels.export) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/sdk/export_sdk.py"
inputs = [ files.tarmaker_manifest ]
_out_dir = "$root_out_dir/sdk/exported/$sdk_name"
_stamp_file = "$target_gen_dir/$target_name.exported"
outputs = [
_stamp_file,
"$_out_dir/meta/manifest.json",
]
depfile = "${_stamp_file}.d"
args = [
"--out-dir",
rebase_path(_out_dir, root_build_dir),
"--stamp-file",
rebase_path(_stamp_file, root_build_dir),
"--manifest",
rebase_path(files.tarmaker_manifest, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
]
deps = [ ":${labels.tarmaker_manifest}" ]
}
_generate_archive =
defined(invoker.generate_archive) && invoker.generate_archive
# Generates the local archive, then copy it to its final location.
compiled_action(labels.local_archive) {
forward_variables_from(invoker, [ "testonly" ])
tool = "//build/tools/tarmaker"
inputs = [ files.tarmaker_manifest ]
outputs = [ files.local_archive ]
args = [
"-manifest",
rebase_path(inputs[0], root_build_dir),
"-output",
rebase_path(outputs[0], root_build_dir),
]
deps = [ ":${labels.tarmaker_manifest}" ]
}
copy(labels.final_archive) {
forward_variables_from(invoker, [ "testonly" ])
sources = [ files.local_archive ]
outputs = [ files.final_archive ]
public_deps = [ ":${labels.local_archive}" ]
metadata = {
sdk_archives = [
{
name = sdk_name
os = current_os
cpu = current_cpu
label = get_label_info(":$target_name", "label_with_toolchain")
path = rebase_path(files.final_archive, root_build_dir)
},
]
}
}
group(labels.main) {
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${labels.verify_manifest}",
":${labels.verify_sdk_meta}",
]
if (_verify_api) {
deps += [ ":${labels.verify_api}" ]
}
public_deps = [
":${labels.export}",
":${labels.final_manifest}",
]
if (_generate_archive) {
public_deps += [ ":${labels.final_archive}" ]
}
# TODO(fxbug.dev/93205): Block all metadata traversals such as the following:
# metadata = {
# expect_includes_barrier = []
# }
}
}