blob: d444d4441b5f7c7ed61a1f297f6cb3643d46760e [file] [log] [blame]
# Copyright 2019 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("//zircon/public/gn/host_tool_action.gni")
import("//zircon/public/gn/manifest.gni")
import("//zircon/public/gn/resource.gni")
declare_args() {
# This can be "zstd", optionally followed by ".LEVEL" where `LEVEL` can be an
# integer or "max". It can also be just "LEVEL" to to use the default
# algorithm with a non-default setting.
#
# The default level for each algorithm is tuned to balance compression
# speed with compression ratio. Higher levels make image builds slower.
# So using the default during rapid development (quick builds, pretty
# good compression) and "max' for production builds (slow builds, best
# compression available) probably makes sense.
zbi_compression = "zstd"
}
# The compression algorithm part of the $zbi_compression setting.
zbi_compression_algorithm = get_path_info(zbi_compression, "name")
if (get_path_info(zbi_compression, "extension") == "" &&
zbi_compression_algorithm != "none" &&
zbi_compression_algorithm != "zstd") {
# This must match what //zircon/tools/zbi/zbi.cc uses by default.
zbi_compression_algorithm = "zstd"
}
# Define inputs to a dependent zbi() target.
#
# The target generates a manifest that collects any manifest_lines from its
# dependencies' metadata, e.g. contributed by resource() targets. It
# contributes metadata that will drive zbi() to use this manifest, as well
# as any non-BOOTFS inputs given via $sources and $args.
#
# resource() targets in the dependencies explicitly contribute to the
# manifest. Many other target types such as executable() and
# shared_library() that have a $install_path parameter (even if defaulted)
# implicitly contribute to the manifest.
#
# It's not necessary to use an explicit zbi_input() target just to collect
# files into the BOOTFS of a zbi(). Use zbi_input() explicitly to get the
# manifest file as an explicit build artifact, to apply a target directory
# prefix to the manfiest file, or to include other types.
#
# Parameters
#
# data_deps, deps
# Optional: Dependencies examined for metadata. Any other zbi_input()
# or zbi() targets in this dependency graph will flow into any zbi()
# target that depends on this one. Any manifest metadata will be collected
# here into a manifest file; see manifest_file().
# Type: list(label)
#
# prefix
# Optional: Directory prefix prepended to the target path in each
# manifest entry. This is normalized when nonempty so it need not end
# with a `/`.
# Type: string
# Default: ""
#
# type
# Optional: The input type for any $sources or $args, as in the
# `--type` switch to the `zbi` tool. See `zbi --help` for the set of
# available types. A value of "" uses `zbi --files`.
# Type: string
# Default: ""
#
# sources
# Optional: Files to put into the ZBI. $type determines what kinds of
# files these can be and how their contents are used. If $type is ""
# (the default), then these can be manifest files or directories.
# Type: list(file)
#
# args
# Optional: Additional arguments to the `zbi` tool. These are switches
# that will precede the $sources on the `zbi` command line, so they
# can include e.g. `--prefix=...` to affect contents of manifest file.
# The most common use is "--entry=..." to provide literal contents right
# in the GN file (e.g. for `type = "cmdline"`).
#
# See manifest_file() for additional parameters. The manifest file
# produced from those parameters contributes to the BOOTFS (as modified
# by $prefix).
#
template("zbi_input") {
manifest_file(target_name) {
prefix = ""
forward_variables_from(invoker,
"*",
[
"args",
"metadata",
"type",
"sources",
])
# Elaborate the defaults identically to manifest_file() so that we know
# the file name to pass to `zbi`.
if (!defined(output_dir)) {
output_dir = target_gen_dir
}
if (!defined(output_extension)) {
output_extension = "manifest"
}
if (!defined(output_name)) {
output_name = target_name
}
manifest_file = "$output_dir/$output_name"
if (output_extension != "") {
manifest_file += ".$output_extension"
}
metadata = {
# TODO(mcgrathr): seems to tickle a gn bug, see below
#zbi_barrier = []
zbi_bootfs_manifest = []
zbi_input_args = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
# When a zbi() target depends on this zbi_input() target, it includes
# other zbi_input() targets in this target's deps, but not data_deps.
if (defined(invoker.deps)) {
# TODO(mcgrathr): seems to tickle a gn bug
#zbi_barrier += invoker.deps
}
# An explicit type applies only to $sources and $args.
if (defined(invoker.type) && invoker.type != "") {
zbi_input_args += [ "--type=${invoker.type}" ]
} else if (defined(invoker.args) || defined(invoker.sources)) {
zbi_input_args += [
# The implicit type is always --files, and needs to be reset in
# case the previous `zbi_input_args` list collected left a
# different `--type=...` as the last state.
"--files",
# Following $args and $sources entries shouldn't get a
# previously-set prefix.
"--prefix=",
]
}
# Explicit $args come first, for e.g. "-u" or "-g" to affect inputs.
# $args may also contain "--entry=..." payloads directly.
if (defined(invoker.args)) {
zbi_input_args += invoker.args
}
if (defined(invoker.sources)) {
zbi_input_args += rebase_path(invoker.sources, root_build_dir)
}
# Always include the manifest generated from the deps here. The
# `manifest_barrier` in manifest_file() will prevent zbi() from
# collecting `manifest_lines` that are redundant with this manifest.
zbi_input_args += [
"--prefix=$prefix",
"--files",
rebase_path(manifest_file, root_build_dir),
]
zbi_bootfs_manifest += [
{
label = get_label_info(":$target_name", "label_with_toolchain")
prefix = prefix
path = rebase_path(manifest_file, root_build_dir)
},
]
}
}
}
# Build a ZBI file rolling up the contents from dependencies.
#
# This automatically collects a BOOTFS manifest from the dependencies like
# zbi_input() does. It also takes any zbi_input() or equivalent targets
# from the dependencies. (The kernel acts as a zbi_input(), for example.)
#
# This template has two outputs: the first is the ZBI in question, whose path
# is a function of the `output_dir`, `output_name`, and `output_extension`
# parameters in the usual way, and the second is a JSON representation of its
# contents whose path is formed by adding a further ".json" to the ZBI's path.
#
# Parameters
#
# cpu
# Optional: CPU architecture for a complete ZBI.
# If this is "", then this target may produce an incomplete ZBI.
# Otherwise, it's a CPU name ("arm64" or "x64") and the target will
# fail if the ZBI is not complete so it can be booted on that CPU.
# Type: string
# Default: current_cpu
#
# compress
# Optional: Whether to compress the BOOTFS and other `ZBI_TYPE_STORAGE`
# items in the output. See the `--compressed` switch in `zbi --help`.
# If this is a string rather than a bool, it's the argument for the
# `--compressed` switch to `zbi`. A value of `true` is replaced with
# $zbi_compression.
# Type: bool or string
# Default: true
#
# output_dir
# Optional: Directory where the output file is written.
# Type: dir
# Default: target_out_dir
#
# output_extension
# Optional: Extension added to $output_name.
# Type: string
# Default: "zbi"
#
# output_name
# Optional: Name of the output file.
# Type: string
# Default: target_name
#
# tags
# Optional: List of tags to associate with the image.
# This is reflected in build_api_module("images") metadata.
# Type: list(string)
#
template("zbi") {
zbi_target = target_name
input_target = "_zbi.input.$target_name"
rspfile_target = "_zbi.rsp.$target_name"
rspfile = "$target_gen_dir/$target_name.zbi.rsp"
json_target = "_zbi.manifest.$target_name"
json_file = "$target_gen_dir/$target_name.manifest.json"
zbi_input(input_target) {
visibility = [
":$json_target",
":$rspfile_target",
]
forward_variables_from(invoker,
[
"deps",
"output_dir",
"output_name",
"testonly",
])
assert(defined(deps), "zbi(\"$zbi_target\") must have `deps`")
if (!defined(output_name)) {
output_name = zbi_target
}
}
if (defined(invoker.output_name)) {
output_name = invoker.output_name
} else {
output_name = target_name
}
output_file = output_name
if (defined(invoker.output_extension)) {
if (invoker.output_extension != "") {
output_file += ".${invoker.output_extension}"
}
} else {
output_file += ".zbi"
}
if (defined(invoker.output_dir)) {
output_dir = invoker.output_dir
} else {
output_dir = target_out_dir
}
output_file = "$output_dir/$output_file"
json_output_file = "$output_file.json"
# Generate a response file of input arguments collected from metadata.
generated_file(rspfile_target) {
visibility = [ ":$zbi_target" ]
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":$input_target" ]
outputs = [ rspfile ]
data_keys = [ "zbi_input_args" ]
walk_keys = [ "zbi_barrier" ]
output_conversion = "list lines"
}
# Generate a JSON file that describes all the separate manifest files
# that feed into the BOOTFS construction. This could be used to produce
# a single flat manifest, though we don't need one to produce the ZBI.
# The same information is in the zbi_input_args metadata that feeds into
# the response file (above) in the form of --prefix=... switch and
# manifest file arguments.
generated_file(json_target) {
visibility = [ ":$zbi_target" ]
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":$input_target" ]
outputs = [ json_file ]
data_keys = [ "zbi_bootfs_manifest" ]
walk_keys = [ "zbi_barrier" ]
output_conversion = "json"
metadata = {
images = [
{
cpu = current_cpu
name = "$zbi_target.bootfs"
label = get_label_info(":$zbi_target", "label_with_toolchain")
type = "json"
path = rebase_path(json_file, root_build_dir)
forward_variables_from(invoker, [ "tags" ])
if (!defined(tags)) {
tags = []
}
tags += [ "manifest" ]
},
]
}
}
host_tool_action(zbi_target) {
forward_variables_from(invoker,
[
"assert_no_deps",
"compress",
"data_deps",
"visibility",
"testonly",
])
deps = [
":$json_target",
":$rspfile_target",
]
outputs = [
output_file,
json_output_file,
]
depfile = "${output_file}.d"
sources = [ rspfile ]
tool = "//zircon/tools/zbi"
args = [
"--output=" + rebase_path(output_file, root_build_dir),
"--json-output=" + rebase_path(json_output_file, root_build_dir),
"--depfile=" + rebase_path(depfile, root_build_dir),
"@" + rebase_path(rspfile, root_build_dir),
]
# Require a complete ZBI for the specified $cpu (or $current_cpu).
# A value of "" means it need not be a complete ZBI.
if (defined(invoker.cpu)) {
cpu = invoker.cpu
} else {
cpu = current_cpu
}
if (cpu != "") {
args += [ "--complete=$cpu" ]
}
# This comes last to affect the output despite any earlier
# "-c" or "-u" from metadata.zbi_input_args meant to affect
# a particular input (e.g. for "--type=ramdisk").
if (!defined(compress) || compress == true) {
compress = zbi_compression
}
if (compress == false) {
args += [ "--uncompressed" ]
} else {
args += [ "--compressed=$compress" ]
}
metadata = {
images = []
zbi_input_args = []
# Another zbi() target that depends on this one will include this ZBI as
# input, but not this ZBI's inputs.
zbi_barrier = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
# For the //:images build_api_module().
images += [
{
label = get_label_info(":$target_name", "label_with_toolchain")
name = output_name
path = rebase_path(output_file, root_build_dir)
type = "zbi"
cpu = cpu
compressed = !defined(invoker.compress) ||
(invoker.compress != false && invoker.compress != "none")
if (defined(testonly) && testonly) {
testonly = true
}
forward_variables_from(invoker, [ "tags" ])
},
]
# Provide metadata so that a zbi() target can also act as if it were a
# zbi_input() with `type = "zbi"` and $sources of this target's $outputs.
# Thus a zbi() target can be a dependency of another zbi() target to
# combine them without requiring an intervening zbi_input() target.
zbi_input_args +=
[ "--type=container" ] + rebase_path(outputs, root_build_dir)
}
}
}