blob: 522e492404cc3b470089961840eafcad4ebac6dc [file] [log] [blame]
# Copyright 2023 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/bazel/bazel_action.gni")
import("//build/images/args.gni")
import("//build/images/supports_fastboot_fvm.gni")
import("//build/images/vbmeta.gni")
import("//build/info/info.gni")
import("//build/sdk/product_bundle_transfer_manifest.gni")
import("//bundles/assembly/platform_aibs.gni")
import("//sdk/config.gni")
import("//src/sys/pkg/bin/package-tool/package-tool.gni")
# A GN wrapper for a Bazel product bundle target. From GN's perspective, the
# output product bundle is opaque (only a manifest file containing paths to the
# Bazel output base). This template only useful if you have a downstream target
# in GN that is capable of consuming a product bundle built by Bazel in its
# entirety.
#
# Parameters
#
# bazel_product_bundle_target (required)
# The Bazel product bundle target to build.
# Type: label (from BUILD.bazel)
#
# bazel_product_image_target (required)
# The Bazel product image target for this product bundle.
#
# When set, two extra subtargets are provided to access outputs from Bazel
# product image build steps:
#
# * ${target_name}_product_assembly: for image_assembly.json
# * ${target_name}_create_system: for images.json
#
# NOTE: This template does NOT check this is the actual product image target
# used in `bazel_product_bundle_target`.
#
# Type: label (from BUILD.bazel)
#
# bazel_product_config_target (optional)
# The Bazel product config target for the product image.
#
# When set, one extra subtarget is provided to access product config from
# Bazel assembly build steps:
#
# * ${target_name}_product_config: for product_assembly_config.json
# NOTE: This template does NOT check this is the actual product config target
# used in `bazel_product_bundle_target`.
#
# bazel_recovery_image_target (optional)
# The Bazel recovery image target for this product bundle.
#
# When set, one extra subtarget is provided to access outputs from Bazel
# recovery image build steps:
#
# * ${target_name}_create_recovery_system: for images.json
#
# NOTE: This template does NOT check this is the actual recovery image target
# used in `bazel_product_bundle_target`.
#
# Type: label (from BUILD.bazel)
#
# bazel_size_report_aggregator_target (optional)
# The Bazel fuchsia_size_report_aggregator target that includes both a budget
# and a size report for gerrit.
#
# bazel_product_size_check_target (optional)
# The Bazel fuchsia_product_size_check target that includes both a breakdown
# and a size visualization.
#
# bazel_elf_sizes_target (optional)
# The Bazel fuchsia_elf_sizes target for this product bundle's product image.
#
# When set, one extra subtarget is provided to access elf_sizes.json
# produced by Bazel.
#
# * ${target_name}_elf_sizes
#
# NOTE: This template does NOT check the provided elf sizes target matches
# the product bundle.
#
# [0] https://cs.opensource.google/fuchsia/fuchsia/+/main:tools/artifactory/cmd/up.go;l=65;drc=bf9ca43004a8073e322e9235c3b7a3e607aa7622
#
# bazel_license_review_target (optional)
# The fuchsia_licenses_review target for this product bundle.
#
# When set, one extra subtarget is included in the final group to output
# license_review.zip from Bazel.
#
# * ${target_name}_license_review
#
# NOTE: This template does NOT check the provided license review target
# matches the product bundle.
#
# bazel_inputs_from_gn (required)
# The Bazel input targets (defined by bazel_input_xxx GN templates) used to
# plumb outputs from GN/Ninja to Bazel. This list should include all GN
# targets producing outputs used in the Bazel product bundle. This is usually
# legacy images configurations, legacy AIBs, prebuilt packages from GN, etc.
#
# For example:
#
# * //build/images/fuchsia:fuchsia.legacy_aib_bazel_inputs
#
# Type: list(label)
#
# allow_eng_platform_bundle_use (optional; default=false)
# If true, allow the 'eng' platform bundles to be used by the assembled
# system. (This is only a check made by GN, assembly itself may still access
# them)
# Type: bool
# Default: false
#
# generate_product_bundle (optional; default = true)
# Whether to generate the product bundle metadata for this product bundle.
#
# product_bundle_name (optional; default = 'product.board')
# The product bundle will use this name instead of the currently active product.board name.
#
# generate_image (optional; default = use_bazel_images_only)
# Whether to generate image metadata for this product bundle.
#
# image_has_fxfs_blob (optional; default = fxfs_blob)
# Whether the image contains an fxfs blob partition.
#
# image_has_fvm (optional; default = opposite of image_fxfs_blob)
# Whether the image contains an fvm. This is exclusive to fxfs_blob, but both can be false.
#
# image_supports_fastboot_fvm (optional; default = supports_fastboot_fvm)
# Whether the image supports a fastboot fvm.
#
# image_has_recovery (optional; default = true)
# Whether the image should contain a recovery partition.
#
# image_use_vbmeta (optional; default = use_vbmeta)
# Whether the image should contain a vbmeta.
#
# deps
# metadata
# testonly
# visibility
#
template("bazel_product_bundle") {
assert(defined(invoker.bazel_product_bundle_target),
"bazel_product_bundle_target is required")
assert(defined(invoker.bazel_product_image_target),
"bazel_product_image_target is required")
assert(defined(invoker.bazel_inputs_from_gn),
"bazel_inputs_from_gn is required")
labels = {
bazel_product_bundle = "${target_name}_bazel_action"
package_manifest_list = "${target_name}_package_manifests_list"
transfer_manifest = "${target_name}_transfer_manifest"
if (defined(invoker.bazel_product_image_target)) {
bazel_product_assembly = "${target_name}_product_assembly"
bazel_create_system = "${target_name}_create_system"
}
if (defined(invoker.bazel_license_review_target)) {
license_review_bazel_action_target = "${target_name}_license_review"
}
}
files = {
out_dir = target_name
product_bundle_out_dir = "${out_dir}/product_bundle"
package_manifests_out_dir = "${out_dir}/manifests"
# NOTE: transfer.json MUST live in the parent directory of
# `product_bundle_out_dir` for `ffx product download` to work.
# See details in https://fxbug.dev/42079368.
transfer_json = "${out_dir}/transfer.json"
if (defined(invoker.bazel_product_image_target)) {
product_assembly_out_dir = labels.bazel_product_assembly
create_system_out_dir = labels.bazel_create_system
}
}
_aib_bazel_inputs = []
if (defined(invoker.allow_eng_platform_bundle_use) &&
invoker.allow_eng_platform_bundle_use) {
_aibs_to_use = eng_platform_aib_labels
not_needed(invoker, [ "allow_userdebug_platform_bundle_use" ])
} else {
if (defined(invoker.allow_userdebug_platform_bundle_use) &&
invoker.allow_userdebug_platform_bundle_use) {
_aibs_to_use = userdebug_platform_aib_labels
} else {
_aibs_to_use = user_platform_aib_labels
}
}
foreach(_aib, _aibs_to_use) {
_aib_bazel_inputs += [ "${_aib}_bazel_inputs" ]
}
_generate_image = use_bazel_images_only
if (defined(invoker.generate_image)) {
_generate_image = invoker.generate_image
}
# NOTE: Only depend on product image targets if your outputs are not included
# in product bundles.
_product_assembly_target = "${target_name}_product_assembly"
bazel_action(_product_assembly_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_product_image_target + "_product_assembly" ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_DIR}}/{{BAZEL_TARGET_NAME}}_out/image_assembly.json"
ninja = "${_product_assembly_target}/image_assembly.json"
},
]
}
_create_system_target = "${target_name}_create_system"
bazel_action(_create_system_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_product_image_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
allow_directory_in_outputs = true
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_DIR}}/{{BAZEL_TARGET_NAME}}_out"
ninja = _create_system_target
},
]
directory_timestamp_outputs = [ "${_create_system_target}/images.json" ]
if (_generate_image) {
_full_label = get_label_info(":${target_name}", "label_with_toolchain")
metadata = {
assembly_manifests = [
{
image_name = "fuchsia"
label = _full_label
assembly_manifest_path = rebase_path(
"${target_out_dir}/${_create_system_target}/images.json",
root_build_dir)
},
]
}
}
}
if (defined(invoker.bazel_product_config_target)) {
_product_config_target = "${target_name}_product_config"
bazel_action(_product_config_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_product_config_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}/{{BAZEL_TARGET_NAME}}_product_config.json"
ninja = "${_product_config_target}/product_assembly_config.json"
},
]
}
}
if (defined(invoker.bazel_recovery_image_target)) {
_create_recovery_system_target = "${target_name}_create_recovery_system"
bazel_action(_create_recovery_system_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_recovery_image_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
allow_directory_in_outputs = true
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_DIR}}/{{BAZEL_TARGET_NAME}}_out"
ninja = _create_recovery_system_target
},
]
directory_timestamp_outputs =
[ "${_create_recovery_system_target}/images.json" ]
}
}
_size_targets = []
if (defined(invoker.bazel_size_report_aggregator_target)) {
_size_report_target = "${target_name}_size_report"
_size_targets += [ ":${_size_report_target}" ]
bazel_action(_size_report_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_size_report_aggregator_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
# Size checker prints out size information when limits are exceeded on
# INFO level, so it is necessary to include Bazel INFO logs here.
filter_bazel_info_logs = false
_size_budgets_path = "${_size_report_target}/size_budgets.json"
_size_report_path = "${_size_report_target}/size_report.json"
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_size_budgets.json"
ninja = _size_budgets_path
},
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_size_report.json"
ninja = _size_report_path
},
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_verbose_output.json"
ninja = "${_size_report_target}/verbose_output.json"
},
]
metadata = {
gerrit_size_reports =
[ rebase_path("${target_out_dir}/${_size_report_path}",
root_build_dir) ]
detailed_size_checker_data = [
{
name = "size_budgets"
path = rebase_path("${target_out_dir}/${_size_budgets_path}",
root_build_dir)
},
]
}
}
}
if (defined(invoker.bazel_product_size_check_target)) {
_product_size_checker_target = "${target_name}_product_size_checker"
_size_targets += [ ":${_product_size_checker_target}" ]
bazel_action(_product_size_checker_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_product_size_check_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
allow_directory_in_outputs = true
_visualization_path = "${_product_size_checker_target}/visualization"
_size_breakdown_path =
"${_product_size_checker_target}/size_breakdown.txt"
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_visualization"
ninja = _visualization_path
},
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_size_breakdown.txt"
ninja = _size_breakdown_path
},
]
metadata = {
product_size_checker_output = [
{
visualization =
rebase_path("${target_out_dir}/${_visualization_path}",
root_build_dir)
size_breakdown =
rebase_path("${target_out_dir}/${_size_breakdown_path}",
root_build_dir)
},
]
}
}
}
group("${target_name}_size_targets") {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = _size_targets
}
_elf_sizes_bazel_action_target = "${target_name}_elf_sizes"
if (defined(invoker.bazel_elf_sizes_target)) {
_elf_sizes_ninja_out_path =
"${_elf_sizes_bazel_action_target}/elf_sizes.json"
bazel_action(_elf_sizes_bazel_action_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
command = "build"
bazel_targets = [ invoker.bazel_elf_sizes_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_PATH}}_elf_sizes.json"
ninja = _elf_sizes_ninja_out_path
},
]
metadata = {
images = [
{
name = "elf_sizes.json"
label = get_label_info(":${_elf_sizes_bazel_action_target}",
"label_with_toolchain")
type = "manifest"
path = rebase_path("${target_out_dir}/${_elf_sizes_ninja_out_path}",
root_build_dir)
},
]
}
}
} else {
group(_elf_sizes_bazel_action_target) {
}
}
if (defined(invoker.bazel_license_review_target)) {
bazel_action(labels.license_review_bazel_action_target) {
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
command = "build"
bazel_targets = [ invoker.bazel_license_review_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_DIR}}/license_review.zip"
ninja = "license_review.zip"
},
]
# This metadata is added to infra's build output
metadata = {
licenses = [
{
license_review_archive =
rebase_path("${target_out_dir}/license_review.zip",
root_build_dir)
},
]
}
}
}
bazel_action(labels.bazel_product_bundle) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"deps",
])
no_output_dir_leaks = false
command = "build"
bazel_targets = [ invoker.bazel_product_bundle_target ]
bazel_inputs = _aib_bazel_inputs + invoker.bazel_inputs_from_gn
# Directory outputs are OK because `product_bundle.json` correctly
# represents the freshness of all outputs.
allow_directory_in_outputs = true
copy_outputs = [
{
bazel = "{{BAZEL_TARGET_OUT_DIR}}/{{BAZEL_TARGET_NAME}}_out"
ninja = files.product_bundle_out_dir
},
]
directory_timestamp_outputs = [
"${files.product_bundle_out_dir}/product_bundle.json",
"${files.product_bundle_out_dir}/system_a/fuchsia.zbi",
]
_generate_product_bundle = true
if (defined(invoker.generate_product_bundle)) {
_generate_product_bundle = invoker.generate_product_bundle
}
metadata = {
}
if (_generate_product_bundle) {
# 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 != "")
_product_bundle_name = "${build_info_product}.${build_info_board}"
if (defined(invoker.product_bundle_name)) {
_product_bundle_name = invoker.product_bundle_name
}
metadata.product_bundles = [
{
label = get_label_info(":$target_name", "label_with_toolchain")
path =
rebase_path("${target_out_dir}/${files.product_bundle_out_dir}",
root_build_dir)
name = _product_bundle_name
product_version = product_version
transfer_manifest_path =
rebase_path("${target_out_dir}/${files.transfer_json}",
root_build_dir)
transfer_manifest_url = "file://" + transfer_manifest_path
},
]
} else {
not_needed(invoker, "product_bundle_name")
}
# Populate metadata when Bazel image is not used will pollute GN assembly.
if (_generate_image) {
_full_label = get_label_info(":${target_name}", "label_with_toolchain")
_system_a_root = rebase_path(target_out_dir, root_out_dir) +
"/${files.product_bundle_out_dir}/system_a"
_image_has_fxfs_blob = fxfs_blob
if (defined(invoker.image_has_fxfs_blob)) {
_image_has_fxfs_blob = invoker.image_has_fxfs_blob
}
_image_has_fvm = !_image_has_fxfs_blob
if (defined(invoker.image_has_fvm)) {
_image_has_fvm = invoker.image_has_fvm
}
if (_image_has_fxfs_blob) {
directory_timestamp_outputs +=
[ "${files.product_bundle_out_dir}/system_a/fxfs.blk" ]
assert(!_image_has_fvm, "Cannot have both fxfs and fvm.")
} else if (_image_has_fvm) {
directory_timestamp_outputs +=
[ "${files.product_bundle_out_dir}/system_a/fvm.blk" ]
}
_image_use_vbmeta = use_vbmeta
if (defined(invoker.image_use_vbmeta)) {
_image_use_vbmeta = invoker.image_use_vbmeta
}
if (_image_use_vbmeta) {
directory_timestamp_outputs +=
[ "${files.product_bundle_out_dir}/system_a/fuchsia.vbmeta" ]
}
_image_has_recovery = true
if (defined(invoker.image_has_recovery)) {
_image_has_recovery = invoker.image_has_recovery
}
if (_image_has_recovery) {
_system_r_root = rebase_path(target_out_dir, root_out_dir) +
"/${files.product_bundle_out_dir}/system_r"
_recovery_name = get_label_info(recovery_label, "name")
}
# TODO(https://fxbug.dev/42078046): Add metadata for recovery image as well.
metadata.images = [
{
name = "zircon-a"
type = "zbi"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/fuchsia.zbi"
archive = true
bootserver_pave = []
if (!sign_zbi) {
bootserver_pave += [ "--zircona" ]
fastboot_flash = []
if (zircon_a_partition != "") {
fastboot_flash += [ zircon_a_partition ]
}
if (zircon_b_partition != "") {
fastboot_flash += [ zircon_b_partition ]
}
}
},
]
if (_image_has_recovery) {
metadata.images += [
{
name = "zircon-r"
cpu = current_cpu
type = "zbi"
label = _full_label
path = "${_system_r_root}/${_recovery_name}.zbi"
archive = true
bootserver_pave = [ "--zirconr" ]
fastboot_flash = []
if (zircon_r_partition != "") {
fastboot_flash = [ zircon_r_partition ]
}
if (recovery_is_zedboot) {
bootserver_pave_zedboot = [ "--zircona" ]
}
},
]
}
if (_image_has_fxfs_blob) {
metadata.images += [
{
name = "storage-full"
type = "fxfs-blk"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/fxfs.blk"
archive = add_qemu_to_build_archives
},
{
name = "fuchsia.fxfs_sparse"
type = "blk"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/fxfs.sparse.blk"
archive = true
bootserver_pave = [ "--fxfs" ]
},
]
} else if (_image_has_fvm) {
metadata.images += [
{
name = "blob"
type = "blk"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/blob.blk"
},
{
name = "storage-full"
type = "blk"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/fvm.blk"
archive = add_qemu_to_build_archives
},
{
name = "storage-sparse"
type = "blk"
cpu = current_cpu
label = _full_label
path = "${_system_a_root}/fvm.sparse.blk"
archive = true
bootserver_pave = [ "--fvm" ]
},
]
_image_supports_fastboot_fvm = supports_fastboot_fvm
if (defined(invoker.image_supports_fastboot_fvm)) {
_image_supports_fastboot_fvm = invoker.image_supports_fastboot_fvm
}
if (_image_supports_fastboot_fvm) {
metadata.images += [
{
name = "fvm.fastboot"
cpu = current_cpu
type = "blk"
label = _full_label
path = "${_system_a_root}/fvm.fastboot.blk"
archive = true
fastboot_flash = [ fvm_partition ]
},
]
}
}
if (_image_use_vbmeta) {
metadata.images += [
{
name = "zircon-a"
cpu = current_cpu
type = "vbmeta"
label = _full_label
path = "${_system_a_root}/fuchsia.vbmeta"
archive = true
bootserver_pave = [ "--vbmetaa" ]
fastboot_flash = []
if (vbmeta_a_partition != "") {
fastboot_flash += [ vbmeta_a_partition ]
}
if (vbmeta_b_partition != "") {
fastboot_flash += [ vbmeta_b_partition ]
}
},
]
if (_image_has_recovery) {
metadata.images += [
{
name = "zircon-r"
cpu = current_cpu
type = "vbmeta"
label = _full_label
path = "${_system_r_root}/${_recovery_name}.vbmeta"
archive = true
bootserver_pave = [ "--vbmetar" ]
fastboot_flash = []
if (vbmeta_r_partition != "") {
fastboot_flash = [ vbmeta_r_partition ]
}
if (recovery_is_zedboot) {
bootserver_pave_zedboot = [ "--vbmetaa" ]
}
},
]
}
}
} else {
not_needed(invoker,
[
"image_has_fvm",
"image_has_fxfs_blob",
"image_has_recovery",
"image_supports_fastboot_fvm",
"image_use_vbmeta",
])
}
}
package_tool_package_manifest_list_create(labels.package_manifest_list) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
product_bundle = "${target_out_dir}/${files.product_bundle_out_dir}"
manifests_dir = "${target_out_dir}/${files.package_manifests_out_dir}"
deps = [ ":${labels.bazel_product_bundle}" ]
}
product_bundle_transfer_manifest(labels.transfer_manifest) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
product_bundle_target = ":${labels.bazel_product_bundle}"
product_bundle_dir = "${target_out_dir}/${files.product_bundle_out_dir}"
outputs = [ "${target_out_dir}/${files.out_dir}/transfer.json" ]
}
# A convenience group for easy `fx build` invocation.
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = [
":${labels.package_manifest_list}",
":${labels.transfer_manifest}",
]
if (defined(invoker.bazel_license_review_target)) {
deps += [ ":${labels.license_review_bazel_action_target}" ]
}
# Some dependents want to claim outputs from these targets as inputs.
public_deps = [ ":${labels.bazel_product_bundle}" ]
}
}