blob: dd2156cb849c3b745eb1624904264a72750b819f [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("//build/compiled_action.gni")
import("//build/component/component_id_index.gni")
import("//build/config.gni")
import("//build/dist/generated_resource.gni")
import("//build/drivers/driver_manifest.gni")
import("//build/images/assembly/generated_board_config.gni")
import("//build/images/assembly/generated_product_config.gni")
import("//build/images/collect_blob_manifest.gni")
import("//build/images/fvm.gni")
import("//build/images/manifest.gni")
import("//build/images/pkgfs.gni")
import("//build/images/shell_commands.gni")
import("//build/images/system_image_fuchsia_packages.gni")
import("//build/info/info.gni")
import("//build/packages/package_metadata.gni")
import("//build/security/verify_build.gni")
import("//build/unification/future/images/devmgr_config.gni")
import("//build/zbi/zbi.gni")
import("//build/zbi/zbi_input.gni")
import("//src/sys/pkg/bin/pm/pm.gni")
# Assembles a Fuchsia system.
#
# Given base, cache, and universe packages, assembles a Fuchsia system
# containing those packages.
#
# Parameters
#
# base_packages (required)
# [list of labels] The packages to include in the base package set.
#
# base_driver_packages (optional)
# [list of labels] The driver packages to include in the base package set.
#
# cache_packages (optional)
# [list of labels] The packages to cache in the images.
#
# universe_packages (optional)
# [list of labels] The packages to build in addition to the base and cache
# sets. These packages do not contribute to the produced images directly,
# however they may contribute to the config-data and shell-commands meta
# packages.
#
# bootfs_labels (required)
# [list of labels] The objects installed on the bootfs partition of the
# generated ZBI.
#
# system_image_deps (optional)
# [list of labels] The objects installed in the system image.
#
# ramdisk_fvm_in_zbi (default: false)
# [boolean] Whether the generated ZBI should contain the system's FVM as a ramdisk.
#
# devmgr_config (default: [])
# [list of strings] List of arguments to add to /boot/config/devmgr.
# These arguments come after synthesized arguments to configure blobfs and
# pkgfs.
#
# sysmgr_golden, sysmgr_golden_warn (optional, default: false)
# [path, boolean] Passed directly to the config_package template, see //build/config.gni.
#
# cmdline (optional)
# [list of strings] Kernel command line text.
#
# cmdline_inputs (optional)
# [list of files] Input files treated as kernel command line text.
#
# compress_blobs (default: true)
# [boolean] Whether the blobs added to the blobfs image should be compressed.
#
# blob_layout_format (default: "padded")
# [string] The format blobfs should store blobs in. The valid values are "padded" and
# "compact".
#
# output_dir (optional; default: target_out_dir)
# [string] The output directory into which the final system ZBI is written.
#
# output_name (optional; default: target_name.zbi)
# [string] The name of the final system ZBI file.
#
# cmdline_goldens (optional)
# [list of files] A list of possible kernel cmdline golden file to compare
# against. Verified if matches one of the goldens. At most two entries are
# supported for soft migration, if the content matches either of the entries,
# it is consider a match. Only one entry should be used for normal case.
#
# bootfs_goldens (optional)
# [list of files] A list of possible bootFS golden file to compare against.
# Verified if matches one of the goldens. At most two entries are
# supported for soft migration, if the content matches either of the entries,
# it is consider a match. Only one entry should be used for normal case.
#
# static_pkgs_goldens (optional)
# [list of files] A list of possible static pkgs golden file to compare
# against. Verified if matches one of the goldens. At most two entries are
# supported for soft migration, if the content matches either of the entries,
# it is consider a match. Only one entry should be used for normal case.
#
# include_component_id_index (default: false)
# [bool] Collect and merges a component id index from the base
# package set.
#
# check_production_tag (default: false)
# [bool] Whether to check there is no non_production_tag dependencies.
#
# include_shell_commands (default: true)
# [bool] Whether to include shell commands. Should be set to false for
# build without shell access.
#
# testonly (optional)
# [bool] Usual GN meanings.
template("assemble_system") {
image_name = target_name
# Internal labels used for Image Assembly.
labels = {
product_config = "${image_name}_product_config"
board_config = "${image_name}_board_config"
image_assembler = "${image_name}_image_assembler"
zbi_matches = "${image_name}_zbi_matches"
zbi_manifest_matches = "${image_name}_zbi_manifest_matches"
blob_matches = "${image_name}_blob_matches"
fvm_matches = "${image_name}_fvm_matches"
}
# Intermediate files produced for Image Assembly.
files = {
product_config = "$target_out_dir/${image_name}_product_config.json"
board_config = "$target_out_dir/${image_name}_board_config.json"
}
assert(defined(invoker.base_packages), "Need to define base_packages")
assert(defined(invoker.bootfs_labels), "Need to define bootfs_labels")
_base_packages = invoker.base_packages
_base_driver_packages = []
_cache_packages = []
_universe_packages = []
if (defined(invoker.base_driver_packages)) {
_base_driver_packages = invoker.base_driver_packages
}
_base_packages += _base_driver_packages
if (defined(invoker.cache_packages)) {
_cache_packages = invoker.cache_packages
}
if (defined(invoker.universe_packages)) {
_universe_packages = invoker.universe_packages
}
system_image_deps = []
if (defined(invoker.system_image_deps)) {
system_image_deps += invoker.system_image_deps
}
if (defined(invoker.output_name)) {
zbi_name = invoker.output_name
} else {
zbi_name = "${target_name}.zbi"
}
ramdisk_fvm_in_zbi = false
if (defined(invoker.ramdisk_fvm_in_zbi)) {
ramdisk_fvm_in_zbi = invoker.ramdisk_fvm_in_zbi && !bootfs_only
}
compress_blobs = true
if (defined(invoker.compress_blobs)) {
compress_blobs = invoker.compress_blobs
}
blob_layout_format = "compact"
if (defined(invoker.blob_layout_format)) {
blob_layout_format = invoker.blob_layout_format
}
devmgr_config = []
if (defined(invoker.devmgr_config)) {
devmgr_config = invoker.devmgr_config
}
include_shell_commands = true
if (defined(invoker.include_shell_commands)) {
include_shell_commands = invoker.include_shell_commands
}
# Generate the base driver manifest file that lists all available drivers
# collected from _base_driver_packages dependency tree. This manifest file
# will be included in the driver-manager-base-config package.
base_driver_manifest_label = "${target_name}_base-driver-manifest"
combined_driver_manifest(base_driver_manifest_label) {
forward_variables_from(invoker, [ "testonly" ])
manifest_name = "base-driver-manifest"
deps = _base_driver_packages
}
# The driver-manager-base-config package is read by Driver Manager to
# discover where the base drivers are located.
driver_manager_base_config_label = "${target_name}_driver-manager-base-config"
fuchsia_package(driver_manager_base_config_label) {
forward_variables_from(invoker, [ "testonly" ])
package_name = "driver-manager-base-config"
deps = [ ":${base_driver_manifest_label}" ]
}
_base_packages += [ ":${driver_manager_base_config_label}" ]
# TODO(fxbug.dev/81290): Construct the shell commands package in the Image
# Assembler.
if (include_shell_commands) {
shell_commands("${target_name}_shell-commands") {
package_name = "shell-commands"
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
deps = _base_packages + _cache_packages + _universe_packages
}
}
# For details, see //docs/development/components/component_id_index.md#system-assembly
component_id_index_config("${image_name}_component_id_index_config") {
forward_variables_from(invoker, [ "testonly" ])
# collect and merge component ID indices from the base set.
deps = _base_packages
}
component_id_index_dep = []
component_id_index_config_data_dep = []
if (defined(invoker.include_component_id_index) &&
invoker.include_component_id_index) {
component_id_index_dep = [ ":${image_name}_component_id_index_config" ]
component_id_index_config_data_dep =
[ ":${image_name}_component_id_index_config-config-data" ]
}
config_package("${image_name}_config-data") {
package_name = "config-data"
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
deps = _base_packages + _cache_packages + _universe_packages +
component_id_index_config_data_dep
if (defined(invoker.sysmgr_golden)) {
sysmgr_golden = invoker.sysmgr_golden
}
if (defined(invoker.sysmgr_golden_warn)) {
sysmgr_golden_warn = invoker.sysmgr_golden_warn
}
}
group("${image_name}_base_packages") {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
deps = _base_packages + [
":${image_name}_config-data",
pkgfs_package_label,
] + component_id_index_dep
if (include_shell_commands) {
deps += [ ":${image_name}_shell-commands" ]
}
}
meta_far_merkle_index = "$target_out_dir/${image_name}_meta_far_merkle_index"
generate_package_metadata("${image_name}_meta_far_merkle_index") {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
outputs = [ meta_far_merkle_index ]
data_keys = [ "meta_far_merkle_index_entries" ]
deps = [ ":${image_name}_base_packages" ]
}
action("${image_name}_pkgsvr_index") {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
inputs = [ meta_far_merkle_index ]
outputs = [ "${target_out_dir}/${target_name}" ]
deps = [ ":${image_name}_meta_far_merkle_index" ]
depfile = "${target_out_dir}/${target_name}.d"
script = "//build/images/manifest_content_expand.sh"
args = rebase_path(inputs, root_build_dir) +
rebase_path(outputs, root_build_dir) +
[ rebase_path(depfile, root_build_dir) ]
}
# Generate an empty cache_packages file to be consistent with other
# image assemblers used in the build.
cache_packages_file = "$target_out_dir/${image_name}_cache_packages"
cache_packages_target = "${image_name}_empty_cache_packages"
generated_file(cache_packages_target) {
outputs = [ cache_packages_file ]
contents = ""
}
system_image_fuchsia_system_packages("${image_name}_fuchsia_packages") {
forward_variables_from(invoker, [ "testonly" ])
deps = system_image_deps
filter_packages = "system_only"
}
generate_manifest("${image_name}_system_image.manifest") {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${cache_packages_target}",
":${image_name}_fuchsia_packages",
":${image_name}_pkgsvr_index",
]
json = "//build/images/system_meta_package_json"
pkgsvr_index = get_target_outputs(":${image_name}_pkgsvr_index")
system_image_fuchsia_packages_list =
get_target_outputs(":${image_name}_fuchsia_packages")
system_image_fuchsia_packages_manifest =
system_image_fuchsia_packages_list[0]
sources = [
json,
pkgsvr_index[0],
system_image_fuchsia_packages_manifest,
]
args = [
"@" + rebase_path(system_image_fuchsia_packages_manifest, root_build_dir),
"--entry=meta/package=" + rebase_path(json, root_build_dir),
"--entry=data/static_packages=" +
rebase_path(pkgsvr_index[0], root_build_dir),
"--entry=data/cache_packages=" +
rebase_path(cache_packages_file, root_build_dir),
]
}
pm_build("${image_name}_system_image.meta") {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
manifest = ":${image_name}_system_image.manifest"
package_name = "system_image"
}
blob_manifest = "$target_out_dir/${image_name}_blob.manifest"
collect_blob_manifest("${image_name}_blob.manifest") {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
outputs = [ blob_manifest ]
public_deps = [
":${image_name}_base_packages",
":${image_name}_system_image.meta",
pkgfs_package_label,
]
}
blobfs_tool = "//zircon/tools/blobfs(${host_toolchain})"
blobfs_tool_dir = get_label_info(blobfs_tool, "root_out_dir")
blobfs_binary = "$blobfs_tool_dir/blobfs"
action("${image_name}_blob.blk") {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${image_name}_blob.manifest",
blobfs_tool,
]
blob_image_path = "$target_out_dir/$target_name"
blob_size_list = "$target_out_dir/${image_name}_blobs.json"
outputs = [
blob_image_path,
blob_size_list,
]
depfile = blob_image_path + ".d"
inputs = [
blob_manifest,
blobfs_binary,
]
script = "//build/images/mkblobfs.sh"
# tell the wrapper script where to find blobfs, and the image file
args = rebase_path([
blobfs_binary,
blob_image_path,
],
root_build_dir)
# args for the blobfs tool itself
args += [
"--depfile",
"--json-output",
rebase_path(blob_size_list, root_build_dir),
]
if (compress_blobs) {
args += [ "--compress" ]
}
args += [
rebase_path(blob_image_path, root_build_dir),
"create",
"--manifest",
rebase_path(blob_manifest, root_build_dir),
"--blob_layout_format",
blob_layout_format,
]
}
compiled_action("${image_name}_data.blk") {
data_image_path = "$target_out_dir/${target_name}"
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
outputs = [ data_image_path ]
tool = "//zircon/tools/minfs"
args = [
rebase_path(data_image_path, root_build_dir),
"create",
]
}
generate_fvm("${image_name}_fvm.blk") {
# Referenced by guest_package.
forward_variables_from(invoker, [ "testonly" ])
output_name = "$target_out_dir/$target_name"
# This target is only ever used for a ram-based FVM, so the slice size chosen does not need
# to be the same as used in other places.
args = [
"create",
"--slice",
"1048576",
]
partitions = [
{
type = "blob"
dep = ":${image_name}_blob.blk"
},
{
type = "data"
dep = ":${image_name}_data.blk"
},
]
}
zbi_input("${image_name}_fvm.blk_ramdisk") {
forward_variables_from(invoker, [ "testonly" ])
type = "ramdisk"
args = [ "--compress=$zbi_compression" ]
# Use data_deps and not deps so that the contents of the fvm don't get
# included in the dependent zbi.
data_deps = [ ":${image_name}_fvm.blk" ]
sources = [ "$target_out_dir/${image_name}_fvm.blk" ]
}
action("${image_name}_devmgr_config.txt") {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
script = "//build/images/manifest.py"
inputs = [ build_info_files.minimum_utc_stamp ]
outputs = [ "$target_out_dir/$target_name" ]
args = [ "--output=" + rebase_path(outputs[0], root_build_dir) ]
sources = []
deps = [ ":${image_name}_system_image.manifest" ]
pkgfs = "bin/" + pkgfs_binary_name
pkgfs_label = pkgfs_package_label
pkgfs_pkg_out_dir = get_label_info(pkgfs_label, "target_out_dir") + "/" +
get_label_info(pkgfs_label, "name")
pkgfs_blob_manifest = "$pkgfs_pkg_out_dir/meta/contents"
system_image_merkleroot =
"$target_out_dir/${image_name}_system_image.meta/meta.far.merkle"
deps += [
":${image_name}_system_image.meta",
"//build/info:latest-commit-date",
pkgfs_label,
]
sources += [
pkgfs_blob_manifest,
system_image_merkleroot,
]
args += [
"--entry=devmgr.require-system=true",
"--contents",
"--rewrite=*=zircon.system.pkgfs.cmd={target}+{source}",
"--entry=${pkgfs}=" +
rebase_path(system_image_merkleroot, root_build_dir),
"--no-contents",
"--reset-rewrite",
"--rewrite=*=zircon.system.pkgfs.file.{target}={source}",
"--manifest=" + rebase_path(pkgfs_blob_manifest, root_build_dir),
"--reset-rewrite",
]
# Add the backstop UTC value from the integration repo latest commit
args += [
"--contents",
"--entry=clock.backstop=" +
rebase_path(build_info_files.minimum_utc_stamp, root_build_dir),
"--no-contents",
"--reset-rewrite",
]
foreach(entry, devmgr_config) {
args += [ "--entry=$entry" ]
}
}
devmgr_config("${image_name}_devmgr_config") {
label = ":${image_name}_devmgr_config.txt"
forward_variables_from(invoker, [ "testonly" ])
}
zbi_input("${image_name}_cmdline") {
type = "cmdline"
args = []
if (defined(invoker.cmdline)) {
foreach(arg, invoker.cmdline) {
args += [ "--entry=$arg" ]
}
}
if (defined(invoker.cmdline_inputs)) {
sources = invoker.cmdline_inputs
}
}
check_production_tag = false
if (defined(invoker.check_production_tag)) {
check_production_tag = invoker.check_production_tag
}
zbi("${image_name}_zbi") {
metadata = {
# We insert a package barrier because the packages inside this ZBI
# shouldn't leak to targets that depend on the ZBI. For example,
# suppose we store this ZBI inside a package() that is assembled into
# another Fuchsia system. We don't want the parent system to incorporate
# the packages from the ZBI into its own package list.
package_barrier = []
config_package_barrier = []
distribution_entries_barrier = []
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
}
if (defined(invoker.output_dir)) {
output_dir = invoker.output_dir
}
output_name = zbi_name
output_extension = ""
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = [
":${image_name}_cmdline",
":${image_name}_devmgr_config",
":${image_name}_system_image.manifest",
"//src/sys/base-resolver:bootfs",
"//zircon/kernel",
]
deps += invoker.bootfs_labels
if (ramdisk_fvm_in_zbi) {
deps += [
":${image_name}_fvm.blk_ramdisk",
"//src/storage/fshost:minfs_ramdisk",
]
}
if (check_production_tag) {
assert_no_deps = [ "//build/validate:non_production_tag" ]
}
}
if (defined(invoker.cmdline_goldens) || defined(invoker.bootfs_goldens) ||
defined(invoker.static_pkgs_golden)) {
zbi_outputs = get_target_outputs(":${image_name}_zbi")
}
if (defined(invoker.cmdline_goldens)) {
verify_kernel_cmdline("${image_name}_cmdline_verify_files") {
forward_variables_from(invoker, [ "testonly" ])
zbi = zbi_outputs[0]
goldens = invoker.cmdline_goldens
zbi_target = ":${image_name}_zbi"
}
}
if (defined(invoker.bootfs_goldens)) {
verify_bootfs_filelist("${image_name}_bootfs_verify_files") {
forward_variables_from(invoker, [ "testonly" ])
zbi = zbi_outputs[0]
goldens = invoker.bootfs_goldens
zbi_target = ":${image_name}_zbi"
}
}
if (defined(invoker.static_pkgs_goldens)) {
verify_static_pkgs("${image_name}_static_pkgs_verify_files") {
forward_variables_from(invoker, [ "testonly" ])
zbi = zbi_outputs[0]
zbi_target = ":${image_name}_zbi"
blobfs_manifest = blob_manifest
blobfs_manifest_target = ":${image_name}_blob.manifest"
goldens = invoker.static_pkgs_goldens
}
}
#######
# Build the same images using the Image Assembler.
#
generated_product_config(labels.product_config) {
forward_variables_from(invoker, [ "testonly" ])
output_path = files.product_config
kernel_image = "//zircon/kernel"
base_packages = [ ":${image_name}_base_packages" ]
cache_packages = _cache_packages
boot_args = devmgr_config
bootfs_labels = invoker.bootfs_labels
# Search for more BootFS files in these targets.
bootfs_labels += [
":${image_name}_cmdline",
":${image_name}_devmgr_config",
":${image_name}_system_image.manifest",
"//src/sys/base-resolver:bootfs",
"//zircon/kernel",
]
if (ramdisk_fvm_in_zbi) {
bootfs_labels += [ "//src/storage/fshost:minfs_ramdisk" ]
}
extra_base_deps = system_image_deps
# Search for the Kernel cmdline in the BootFS dependencies.
cmdline_deps = invoker.bootfs_labels
if (defined(invoker.cmdline)) {
cmdline = invoker.cmdline
}
}
generated_board_config(labels.board_config) {
forward_variables_from(invoker, [ "testonly" ])
board_name = "${image_name}"
output_path = files.board_config
# minfs arguments
if (!bootfs_only) {
minfs =
rebase_path("$target_out_dir/${image_name}_data.blk", root_out_dir)
deps = [ ":${image_name}_data.blk" ]
}
# blobfs arguments
blob_layout_format = blob_layout_format
compress_blobs = compress_blobs
# fvm arguments
embed_fvm_in_zbi = ramdisk_fvm_in_zbi
if (!bootfs_only) {
fvm_slice_size = 1048576
}
}
ffx_action(labels.image_assembler) {
forward_variables_from(invoker,
[
"deps",
"testonly",
])
# TODO(fxbug.dev/77290) - Add depfile support and then remove the following
hermetic_deps = false
outdir = "$target_out_dir/$image_name"
gendir = "$outdir/gen"
args = [
"--config",
"assembly_enabled=true",
"assembly",
"image",
"--product",
rebase_path(files.product_config, root_build_dir),
"--board",
rebase_path(files.board_config, root_build_dir),
"--gendir",
rebase_path(gendir, root_build_dir),
"--outdir",
rebase_path(outdir, root_build_dir),
]
deps = [
":${labels.board_config}",
":${labels.product_config}",
]
inputs = [
files.board_config,
files.product_config,
]
# zbi outputs
outputs = [
"${outdir}/fuchsia.zbi",
"${gendir}/zbi.json",
"${outdir}/update.far",
]
# Base package outputs, if this configuration uses them.
if (!bootfs_only) {
outputs += [
"${outdir}/base.far",
"${outdir}/blob.blk",
"${outdir}/fvm.blk",
"${outdir}/fvm.sparse.blk",
"${outdir}/fvm.blob.sparse.blk",
# intermediate outputs that are in gendir:
"${gendir}/data/static_packages",
"${gendir}/data/cache_packages",
"${gendir}/system_image/meta/package",
"${gendir}/update/meta/package",
]
}
}
#######
# Compare the images from both builds to ensure they are identical.
#
gn_assembler_outputs = get_target_outputs(":${image_name}_zbi")
image_assembler_outputs = get_target_outputs(":${labels.image_assembler}")
verify_files_match(labels.zbi_matches) {
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${image_name}_zbi",
":${labels.image_assembler}",
]
first = gn_assembler_outputs[0]
second = image_assembler_outputs[0]
}
verify_files_match(labels.zbi_manifest_matches) {
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${image_name}_zbi",
":${labels.image_assembler}",
]
first = gn_assembler_outputs[1]
second = image_assembler_outputs[1]
display_text_diff_on_failure = true
}
if (ramdisk_fvm_in_zbi) {
verify_files_match(labels.blob_matches) {
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${image_name}_blob.blk",
":${labels.image_assembler}",
]
first = "$target_out_dir/${image_name}_blob.blk"
second = image_assembler_outputs[4]
}
verify_files_match(labels.fvm_matches) {
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${image_name}_fvm.blk",
":${labels.image_assembler}",
]
first = "$target_out_dir/${image_name}_fvm.blk"
second = image_assembler_outputs[5]
}
}
group(image_name) {
metadata = {
# We insert these barriers to prevent the dependencies of these images
# from leaking into images "higher up" in the dependency chain.
package_barrier = []
config_package_barrier = []
distribution_entries_barrier = []
}
forward_variables_from(invoker, [ "testonly" ])
public_deps = [
":${image_name}_zbi",
":${labels.image_assembler}",
]
deps = [
":${labels.zbi_manifest_matches}",
":${labels.zbi_matches}",
]
if (ramdisk_fvm_in_zbi) {
deps += [
":${labels.blob_matches}",
":${labels.fvm_matches}",
]
}
if (defined(invoker.cmdline_goldens)) {
deps += [ ":${image_name}_cmdline_verify_files" ]
}
if (defined(invoker.bootfs_goldens)) {
deps += [ ":${image_name}_bootfs_verify_files" ]
}
if (defined(invoker.static_pkgs_goldens)) {
deps += [ ":${image_name}_static_pkgs_verify_files" ]
}
}
}