blob: af9f609cdbdf8ce3c643019e5ee7d65e029ec6e2 [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/compiled_action.gni")
import("//build/config/fuchsia/platform_version.gni")
import("//build/packages/package_metadata.gni")
amber_repository_dir = "$root_out_dir/amber-files"
# Directory containing developer signing keys.
amber_keys_dir = "//src/sys/pkg/repositories/devhost/keys"
# Directory containing developer root metadata.
amber_metadata_dir = "//src/sys/pkg/repositories/devhost/metadata"
# Directory containing files named by their merkleroot content IDs in
# ASCII hex. The //build/image:pm_publish_blobs target populates
# this with copies of build products, but never removes old files.
amber_repository_blobs_dir = "$amber_repository_dir/repository/blobs"
template("_pm") {
compiled_action(target_name) {
tool = "//src/sys/pkg/bin/pm:pm_bin"
tool_output_name = "pm"
forward_variables_from(invoker,
[
"args",
"data_deps",
"depfile",
"deps",
"inputs",
"metadata",
"outputs",
"public_deps",
"testonly",
"visibility",
"no_output_dir_leaks",
])
}
}
# Generate a package archive in the format produced by `pm archive'.
#
# The archive is generated at $target_out_dir/$package_name where package_name
# is `get_label_info($package, "name")`
#
# Parameters
#
# manifest (required)
# [label] A generate_manifest() target defined earlier in the same file.
# This provides the contents for the package.
#
# package (required)
# [label] A pm_build() target defined earlier in the same file.
template("pm_archive") {
assert(defined(invoker.manifest), "manifest is required")
assert(defined(invoker.package), "package is required")
# This must be the same as the output directory used by pm_build.
package_name = get_label_info(invoker.package, "name")
pkg_out_dir = get_label_info(invoker.package, "target_out_dir")
pkg_out_dir = "$pkg_out_dir/$package_name"
pkg_manifest_outputs = get_target_outputs(invoker.manifest)
pkg_manifest_file = pkg_manifest_outputs[0]
_pm(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"no_output_dir_leaks",
])
deps = [
invoker.manifest,
invoker.package,
]
inputs = [
pkg_manifest_file,
# The output package manifest from pm_build(). This is a hack due to
# pm archive's lack of support for depfiles, and ensures that this
# target is rebuilt if any of the files in the dependent package change
# since that would modify package_manifest.json.
"$pkg_out_dir/package_manifest.json",
]
outputs = [ "$target_out_dir/$package_name.far" ]
args = [
"-o",
rebase_path(pkg_out_dir, root_build_dir),
"-m",
rebase_path(pkg_manifest_file, root_build_dir),
"archive",
"-output",
rebase_path("$target_out_dir/$package_name", root_build_dir),
]
}
}
# Generate a sealed package file from a manifest.
#
# Parameters
#
# manifest (required)
# [label] A generate_manifest() target defined earlier in the same file.
# This provides the contents for the package.
#
# The following two items are only required in order to produce metadata about
# the package sets, and may be removed in the future:
#
# package_name (default: the target name)
# [string] Name of the package (should match what is in meta/package)
#
# package_api_level (default: the current fuchsia api level)
# [unsigned integer] API level of the package
#
# repository (default: "fuchsia.com")
# [string] The repository host name part of the package URL.
# See https://fuchsia.dev/fuchsia-src/concepts/packages/package_url#repository
# for more details.
#
# package_out_dir (default: "$target_out_dir/$target_name")
# [path] The directory into which package artifacts should be placed.
#
# deps (optional)
# test (optional)
# visibility (optional)
# Same as for any GN `action()` target.
template("pm_build") {
forward_variables_from(invoker,
[
"package_api_level",
"package_name",
"package_out_dir",
"repository",
])
if (!defined(package_api_level)) {
package_api_level = current_fuchsia_api_level
}
if (!defined(package_name)) {
package_name = target_name
}
if (!defined(repository)) {
repository = "fuchsia.com"
}
if (!defined(package_out_dir)) {
package_out_dir = "$target_out_dir/$target_name"
}
pkg_output_manifest = "$package_out_dir/package_manifest.json"
metadata_target_name = "${target_name}_metadata"
define_package_metadata(metadata_target_name) {
package_name = package_name
snapshot_entry = "$package_name/0=" +
rebase_path("$package_out_dir/blobs.json", root_build_dir)
blob_manifest = "$package_out_dir/blobs.manifest"
meta_far_merkle_index_entry =
"$package_name/0=" +
rebase_path("$package_out_dir/meta.far.merkle", root_build_dir)
package_output_manifest = pkg_output_manifest
}
_pm(target_name) {
forward_variables_from(invoker,
[
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
])
if (!defined(deps)) {
deps = []
}
pkg_manifest_outputs = get_target_outputs(invoker.manifest)
pkg_manifest_file = pkg_manifest_outputs[0]
deps += [ invoker.manifest ]
inputs = [ pkg_manifest_file ]
depfile = "$package_out_dir/meta.far.d"
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
package_barrier = [ ":$metadata_target_name" ]
}
deps += [ ":$metadata_target_name" ]
outputs = [
# produced by seal, must be listed first because of depfile rules.
"$package_out_dir/meta.far",
# update
"$package_out_dir/meta/contents",
# seal
"$package_out_dir/meta.far.merkle",
# package blob json manifest
"$package_out_dir/blobs.json",
# package blob manifest
"$package_out_dir/blobs.manifest",
# package output manifest
pkg_output_manifest,
]
args = [
"-o",
rebase_path(package_out_dir, root_build_dir),
"-m",
rebase_path(pkg_manifest_file, root_build_dir),
"-n",
package_name,
"-r",
repository,
"-api-level",
"$package_api_level",
"build",
"-output-package-manifest",
rebase_path(pkg_output_manifest, root_build_dir),
"-depfile",
"-blobsfile",
"-blobs-manifest",
]
# Due to the content-based nature of these outputs, timestamps of these
# outputs will not be freshened when their contents do not change in
# incremental builds, which allows for early termination of downstream
# build actions.
}
}
template("pm_prepare_publish") {
# These files are copied from amber_keys_dir into $amber_repository_dir/keys.
copy("${target_name}__keys") {
sources = [
"$amber_keys_dir/snapshot.json",
"$amber_keys_dir/targets.json",
"$amber_keys_dir/timestamp.json",
]
outputs = [ "$amber_repository_dir/keys/{{source_file_part}}" ]
}
# TODO(fxbug.dev/38262) In order to be TUF-1.0 conformant, we need to have
# versioned-prefixed root metadata files. Fow now this just hard-codes
# copying the current metadata to the correct place, but long term this
# should be computed so we don't forget to copy the file when we rotate the
# root metadata.
copy("${target_name}__root_manifest") {
sources = [ "$amber_metadata_dir/7.root.json" ]
outputs = [ "$amber_repository_dir/repository/root.json" ]
}
# TODO(fxbug.dev/38262) See the comment on `root_manifest`.
copy("${target_name}__versioned_root_manifest") {
sources = [
"$amber_metadata_dir/1.root.json",
"$amber_metadata_dir/2.root.json",
"$amber_metadata_dir/3.root.json",
"$amber_metadata_dir/4.root.json",
"$amber_metadata_dir/5.root.json",
"$amber_metadata_dir/6.root.json",
"$amber_metadata_dir/7.root.json",
]
outputs = [ "$amber_repository_dir/repository/{{source_file_part}}" ]
}
group(target_name) {
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
if (!defined(deps)) {
deps = []
}
deps += [
":${target_name}__keys",
":${target_name}__root_manifest",
":${target_name}__versioned_root_manifest",
]
metadata = {
package_repository = [
{
path = rebase_path("$amber_repository_dir/repository", root_build_dir)
targets = rebase_path("$amber_repository_dir/repository/targets.json",
root_build_dir)
blobs = rebase_path("$amber_repository_dir/repository/blobs",
root_build_dir)
},
]
}
}
}
template("pm_publish") {
_pm(target_name) {
forward_variables_from(invoker,
[
"deps",
"data_deps",
"inputs",
"testonly",
"visibility",
])
depfile = "${target_out_dir}/${target_name}.d"
assert(inputs == [ inputs[0] ],
"pm_publish(\"$target_name\") requires exactly one input")
outputs = [
# Note: the first output is the one that appears in the depfile.
"$amber_repository_dir/repository/timestamp.json",
"$amber_repository_dir/repository/snapshot.json",
"$amber_repository_dir/repository/targets.json",
]
args = [
"publish",
"-depfile",
rebase_path(depfile, root_build_dir),
"-C",
"-r",
rebase_path(amber_repository_dir, root_build_dir),
"-lp",
"-f",
rebase_path(inputs[0], root_build_dir),
"-vt",
]
}
}
# Generate TUF repository metadata and data in a zip fie.
#
# Globals
#
# amber_keys_dir
# [path] Directory where TUF keys for signing metadata are stored.
#
# amber_metadata_dir
# [path] Directory where TUF root metadata is stored.
#
# Parameters
#
# inputs (required)
# [list of labels] A list of exactly one input: A list of package metadata
# from the set of packages to be published in the TUF repository. For
# example, the output from a generate_package_metadata() target.
#
# deps (optional)
# data_deps (optional)
# testonly (optional)
# visibility (optional)
# Same as for any GN `action()` target.
template("pm_publish_archive") {
action(target_name) {
forward_variables_from(invoker,
[
"deps",
"data_deps",
"inputs",
"testonly",
"visibility",
])
gendir = "${target_out_dir}/${target_name}/gen"
output = "${target_out_dir}/${target_name}.zip"
# TODO(fxbug.dev/94229): Action tracer does not observe `pm` writes because
# golang binaries are statically linked and invoke syscalls directly and
# action tracer relies on dynamically linking its own tracing library.
hermetic_action_ignored_prefixes = [ gendir ]
if (!defined(deps)) {
deps = []
}
deps += [ "//src/sys/pkg/bin/pm:pm_bin($host_toolchain)" ]
sources = [
# Injected key files read by `pm publish`.
"${amber_keys_dir}/snapshot.json",
"${amber_keys_dir}/targets.json",
"${amber_keys_dir}/timestamp.json",
# Injected root metadata for TUF repository.
"${amber_metadata_dir}/1.root.json",
"${amber_metadata_dir}/2.root.json",
"${amber_metadata_dir}/3.root.json",
"${amber_metadata_dir}/4.root.json",
"${amber_metadata_dir}/5.root.json",
"${amber_metadata_dir}/6.root.json",
"${amber_metadata_dir}/7.root.json",
]
assert(inputs == [ inputs[0] ],
"pm_publish_archive(\"$target_name\") requires exactly one input")
# inputs[0] is input to `pm publish`; the action's other input is the `pm`
# binary itself.
inputs += [ "${host_out_dir}/pm" ]
script = "//src/sys/pkg/bin/pm/pm_publish_archive.py"
args = [
"--pm",
rebase_path("${host_out_dir}/pm", root_build_dir),
"--key",
rebase_path("${amber_keys_dir}/snapshot.json", root_build_dir),
"--key",
rebase_path("${amber_keys_dir}/targets.json", root_build_dir),
"--key",
rebase_path("${amber_keys_dir}/timestamp.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/1.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/2.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/3.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/4.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/5.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/6.root.json", root_build_dir),
"--root-metadata",
rebase_path("${amber_metadata_dir}/7.root.json", root_build_dir),
"--default-root-metadata",
rebase_path("${amber_metadata_dir}/7.root.json", root_build_dir),
"--gendir",
rebase_path(gendir, root_build_dir),
"--input",
rebase_path(inputs[0], root_build_dir),
"--output",
rebase_path(output, root_build_dir),
]
outputs = [
# `outputs[0]` must be final output described by depfile.
output,
# Intermediate files copied from source locations:
# Key files read by `pm publish`.
"${gendir}/keys/snapshot.json",
"${gendir}/keys/targets.json",
"${gendir}/keys/timestamp.json",
# Root metadata for TUF repository.
"${gendir}/repository/1.root.json",
"${gendir}/repository/2.root.json",
"${gendir}/repository/3.root.json",
"${gendir}/repository/4.root.json",
"${gendir}/repository/5.root.json",
"${gendir}/repository/6.root.json",
"${gendir}/repository/7.root.json",
"${gendir}/repository/root.json",
]
}
}