blob: 0f8ee49df006bf0a178f007c3cac5f3fa222f29f [file] [log] [blame]
# Copyright 2022 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/info/info.gni")
import("//build/python/python_action.gni")
import("//build/sdk/product_bundle_transfer_manifest.gni")
import("//sdk/config.gni")
import("//src/developer/ffx/build/ffx_action.gni")
# The name of the main product bundle produced by a build.
main_product_bundle_name = "${build_info_product}.${build_info_board}"
# Generate a product bundle that can be used to flash, emulate, or update a
# product onto a Fuchsia target.
#
# Arguments
# name (required)
# [string] The name of the product bundle.
#
# partitions (required)
# [path] The path to the partitions config.
#
# system_a (optional)
# [path] The path to the images manifest that describes images intended
# for slot A.
#
# system_b (optional)
# [path] The path to the images manifest that describes images intended
# for slot B.
#
# system_r (optional)
# [path] The path to the images manifest that describes images intended
# for slot R.
#
# partitions_contents (optional)
# [list] A list of files referenced in the partitions config that will
# be copied into the outputs.
#
# system_a_contents (optional)
# [list] A list of files referenced in the system_a config that will
# be copied into the outputs.
#
# system_b_contents (optional)
# [list] A list of files referenced in the system_b config that will
# be copied into the outputs.
#
# system_r_contents (optional)
# [list] A list of files referenced in the system_r config that will
# be copied into the outputs.
#
# update (optional)
# [scope] If provided, an update package will be built. The scope has
# a couple required fields.
#
# version_file (required)
# [path] A file containing the version to put in the update package.
#
# epoch (required)
# [int] A 64-bit integer that sets an anti-rollback epoch for the
# update package. See:
# https://fuchsia.dev/fuchsia-src/concepts/packages/ota?hl=en#verify-epoch
#
# virtual_devices (optional; default=[])
# [list] A list of virtual device spec files.
#
# default_virtual_device (optional)
# [string] The name of the default virtual device
#
# delivery_blob_type (optional)
# [int] The type of delivery blob the product needs.
#
# skip_transfer_manifest (optional; default=false)
# [bool] Whether to skip creating transfer manifest
#
template("product_bundle") {
assert(defined(invoker.partitions),
"product_bundle(${target_name}) must define `partitions`")
assert(defined(invoker.name),
"product_bundle(${target_name}) must define `name`")
product_name = invoker.name
labels = {
hermetic_inputs = "${target_name}_hermetic_inputs"
product_bundle = "${target_name}_product_bundle"
transfer_manifest = "${target_name}_transfer_manifest"
}
files = {
hermetic_inputs = "${target_out_dir}/${target_name}_hermetic_inputs"
hermetic_inputs_depfile =
"${target_out_dir}/${target_name}_hermetic_inputs.d"
outdir = "$target_out_dir/$target_name"
target_out_dir = target_out_dir
product_bundle_manifest = "${outdir}/product_bundle.json"
vd_dir = "${outdir}/virtual_devices"
transfer_manifest = "$target_out_dir/${product_name}.transfer.json"
tuf_keys = "//src/sys/pkg/repositories/devhost/keys"
}
virtual_devices = []
if (defined(invoker.virtual_devices)) {
virtual_devices += invoker.virtual_devices
}
skip_transfer_manifest = false
if (defined(invoker.skip_transfer_manifest)) {
skip_transfer_manifest = invoker.skip_transfer_manifest
}
python_action(labels.hermetic_inputs) {
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
binary_label =
"//build/assembly/scripts:hermetic_inputs_from_assembly_outputs"
depfile = files.hermetic_inputs_depfile
inputs = [ invoker.partitions ]
outputs = [ files.hermetic_inputs ]
args = [
"--partitions",
rebase_path(invoker.partitions, root_build_dir),
"--include-blobs",
"--output",
rebase_path(files.hermetic_inputs, root_build_dir),
"--depfile",
rebase_path(files.hermetic_inputs_depfile, root_build_dir),
]
if (defined(invoker.system_a) || defined(invoker.system_b) ||
defined(invoker.system_r)) {
args += [ "--system" ]
if (defined(invoker.system_a)) {
args += [ rebase_path(invoker.system_a, root_build_dir) ]
inputs += [ invoker.system_a ]
}
if (defined(invoker.system_b)) {
args += [ rebase_path(invoker.system_b, root_build_dir) ]
inputs += [ invoker.system_b ]
}
if (defined(invoker.system_r)) {
args += [ rebase_path(invoker.system_r, root_build_dir) ]
inputs += [ invoker.system_r ]
}
}
}
ffx_action(labels.product_bundle) {
no_output_dir_leaks = false
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
if (!defined(deps)) {
deps = []
}
outputs = []
if (defined(invoker.partitions_contents)) {
foreach(file, invoker.partitions_contents) {
filename = get_path_info(file, "file")
outputs += [ "${files.outdir}/partitions/${filename}" ]
}
}
if (defined(invoker.system_a_contents)) {
foreach(file, invoker.system_a_contents) {
filename = get_path_info(file, "file")
outputs += [ "${files.outdir}/system_a/${filename}" ]
}
}
if (defined(invoker.system_b_contents)) {
foreach(file, invoker.system_b_contents) {
filename = get_path_info(file, "file")
outputs += [ "${files.outdir}/system_b/${filename}" ]
}
}
if (defined(invoker.system_r_contents)) {
foreach(file, invoker.system_r_contents) {
filename = get_path_info(file, "file")
outputs += [ "${files.outdir}/system_r/${filename}" ]
}
}
hermetic_inputs_target = ":${labels.hermetic_inputs}"
hermetic_inputs_file = files.hermetic_inputs
hermetic_action_ignored_prefixes = [
"${files.outdir}/blobs",
"${files.outdir}/repository",
"${files.outdir}/recovery_repository",
]
# The target below is generated as a part of the `ffx_tool` action at
# `//src/developer/ffx/plugins/product:ffx_product_tool`. See there
# for more information.
ffx_tool = "//src/developer/ffx/plugins/product:ffx_product_tool"
ffx_tool_output_name = "ffx-product"
# The sdk_id holds the sdk_version. This value can be set (e.g. for testing)
# with `--args sdk_id=1234` on the `fx set` call.
# In the future, the product bundles should be versioned independently of
# the sdk version. So far they have been the same value.
assert(sdk_id != "")
product_version = sdk_id
args = [
"--config",
"product.experimental=true",
"product",
"create",
"--product-version",
product_version,
"--product-name",
product_name,
"--partitions",
rebase_path(invoker.partitions, root_build_dir),
"--out-dir",
rebase_path(files.outdir, root_build_dir),
"--tuf-keys",
rebase_path(files.tuf_keys, root_build_dir),
]
outputs += [ files.product_bundle_manifest ]
inputs = [
invoker.partitions,
"${files.tuf_keys}/root.json",
"${files.tuf_keys}/snapshot.json",
"${files.tuf_keys}/targets.json",
"${files.tuf_keys}/timestamp.json",
]
outputs += [
"${files.outdir}/keys/root.json",
"${files.outdir}/keys/targets.json",
"${files.outdir}/keys/snapshot.json",
"${files.outdir}/keys/timestamp.json",
]
if (defined(invoker.update)) {
update = invoker.update
assert(defined(update.version_file), "Need to define update.version_file")
assert(defined(update.epoch), "Need to define update.epoch")
args += [
"--update-package-version-file",
rebase_path(update.version_file, root_build_dir),
"--update-package-epoch",
update.epoch,
]
inputs += [ update.version_file ]
}
if (defined(invoker.system_a)) {
args += [
"--system-a",
rebase_path(invoker.system_a, root_build_dir),
]
inputs += [ invoker.system_a ]
}
if (defined(invoker.system_b)) {
args += [
"--system-b",
rebase_path(invoker.system_b, root_build_dir),
]
inputs += [ invoker.system_b ]
}
if (defined(invoker.system_r)) {
args += [
"--system-r",
rebase_path(invoker.system_r, root_build_dir),
]
inputs += [ invoker.system_r ]
}
if (virtual_devices != []) {
outputs += [ "${files.vd_dir}/manifest.json" ]
}
foreach(virtual_device, virtual_devices) {
args += [
"--virtual-device",
rebase_path(virtual_device, root_build_dir),
]
inputs += [ virtual_device ]
vd_filename = get_path_info(virtual_device, "file")
outputs += [ "${files.vd_dir}/${vd_filename}" ]
}
if (defined(invoker.default_virtual_device)) {
args += [
"--recommended-device",
invoker.default_virtual_device,
]
}
if (defined(invoker.delivery_blob_type) &&
invoker.delivery_blob_type != false) {
args += [
"--delivery-blob-type",
"${invoker.delivery_blob_type}",
]
}
}
if (!skip_transfer_manifest) {
product_bundle_transfer_manifest(labels.transfer_manifest) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
product_bundle_target = ":${labels.product_bundle}"
product_bundle_dir = files.outdir
outputs = [ files.transfer_manifest ]
}
}
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${labels.product_bundle}" ]
if (!skip_transfer_manifest) {
public_deps += [ ":${labels.transfer_manifest}" ]
}
# This value can be set (e.g. for testing) with `--args sdk_id=1234` on the
# `fx set` call.
# In the future, the product bundles should be versioned independently of
# the sdk version. So far they have been the same value.
product_version = sdk_id
assert(product_version != "")
# A json file will be created by '//build/config/build_api_modules.gni'.
metadata = {
product_bundles = [
{
label = get_label_info(":$target_name", "label_with_toolchain")
path = rebase_path(files.outdir, root_build_dir)
name = product_name
cpu = current_cpu
product_version = product_version
if (!skip_transfer_manifest) {
transfer_manifest_path =
rebase_path(files.transfer_manifest, root_build_dir)
transfer_manifest_url = "file://" + transfer_manifest_path
}
json = rebase_path(files.product_bundle_manifest, root_build_dir)
},
]
}
}
}