blob: b854cf0107d9a31d112d76b9c18311c93e2f4734 [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/assembly/product_assembly_config_file_impl.gni")
import("//build/group_with_inputs.gni")
# Create a product assembly config file from the lists of packages and config
# passed into the template.
#
# This template specifically converts lists of labels for fuchsia_package() and
# prebuilt_package() into the lists of output paths needed. This keeps the
# contract about where those two templates place the package manifest internal
# to fuchsia.git.
#
# As outputs, this creates:
#
# outputs = [
# "${target_out_dir}/${target_name}/product_assembly_config.json"
# ]
#
#
# Arguments:
#
# Product-specified Package Sets:
# These are optional lists of targets that produce Fuchsia Packages. These
# are NOT walked for metadata, but must be the exact desired package-creating
# targets.
#
# base_packages [optional]
# [list, GN scopes] A list of GN scopes that hold the information for a
# product-provided package to place into the base set. The scope must have a
# 'package_target' field pointing to the GN target of the fuchsia_package.
#
# base_driver_packages (optional)
# [list, GN scopes] A list of GN scopes that hold the driver packages to
# include in the base package set. Packages listed here should not be
# listed in the base_packages and will be included automatically in
# the base package set as driver packages.
#
# Each scope added to this list needs to be in the following form:
# {
# # This is the label that creates the package, this can not be a group
# package_target = "//gn/label/that/to/the/driver:package"
#
# # These are paths to the driver components within the above package.
# driver_components = [
# "meta/driver_1.cm",
# "meta/driver_2.cm",
# ]
# }
#
# cache_packages [optional]
# [list, GN scopes] A list of GN scopes that hold the information for a
# product-provided package to place into the cache set. The scope must have a
# 'package_target' field pointing to the GN target of the fuchsia_package.
#
# font_asset_provider_packages [optional]
# [list, GN labels] A list of GN labels pointing at product-provided
# packages containing font assets. These fonts end up as base packages,
# since that is the only font distribution method we support at the moment.
#
# platform [optional]
# [scope] This is the platform configuration scope
#
# product [optional]
# [scope] This is the product configuration scope
#
# GN Usual:
# deps
# testonly
# visibility
template("product_assembly_configuration") {
labels = {
# So it can be reused.
target_name = target_name
assembly_config = "${target_name}.product_assembly_config.json"
# This is a second target created by the product_assembly_config_file()
# template that wraps up all the input file labels found in the product and
# platform config with the deps that are passed to this template.
assembly_config_inputs = "${assembly_config}.inputs"
# This is a publicly visible, test-only target, that allows the assembly
# config to be used without needing the deps used create it.
assembly_config_for_validation =
"${target_name}.product_assembly_config.json.for_validation"
base_package_set = "${target_name}.base_packages"
base_package_config_data = "${target_name}.base_packages.config_data"
base_driver_package_set = "${target_name}.base_driver_packages"
cache_package_set = "${target_name}.cache_packages"
cache_package_config_data = "${target_name}.cache_packages.config_data"
board_driver_package_set = "${target_name}.board_driver_packages"
# Base packages specified by the invoker, used to create the list of
# manifests and as the deps for the package set targets.
base_package_labels = []
base_package_config_data_deps = []
if (defined(invoker.base_packages)) {
foreach(package, invoker.base_packages) {
assert(defined(package.package_target),
"package_target must be supplied")
base_package_labels += [ package.package_target ]
if (defined(package.config_data)) {
foreach(_config_data, package.config_data) {
if (defined(_config_data.label)) {
base_package_config_data_deps += [ _config_data.label ]
}
}
}
}
}
# Font packages to be included in base_packages. These contain no
# config data.
if (defined(invoker.font_asset_provider_packages)) {
foreach(font_package, invoker.font_asset_provider_packages) {
base_package_labels += [ font_package ]
}
}
# Cache packages specified by the invoker, used to create the list of
# manifests and as the deps for the package set targets.
cache_package_labels = []
cache_package_config_data_deps = []
if (defined(invoker.cache_packages)) {
foreach(package, invoker.cache_packages) {
assert(defined(package.package_target),
"package_target must be supplied")
cache_package_labels += [ package.package_target ]
if (defined(package.config_data)) {
foreach(_config_data, package.config_data) {
if (defined(_config_data.label)) {
cache_package_config_data_deps += [ _config_data.label ]
}
}
}
}
}
# Driver packages specified by the invoker, used to create the list of
# manifests and as the deps for the package set targets.
base_driver_package_labels = []
if (defined(invoker.base_driver_packages)) {
foreach(package, invoker.base_driver_packages) {
base_driver_package_labels += [ package.package_target ]
}
}
}
files = {
outdir = "$target_out_dir/$target_name"
assembly_config_file = "$outdir/product_assembly_config.json"
# Compute the paths for the package manifests (as files). This is
# closely coupled with how fuchsia_package() and prebuilt_package() both
# create a package manifest from their label.
base_packages = []
base_package_manifests = []
base_package_config_data_sources = []
if (defined(invoker.base_packages)) {
foreach(package, invoker.base_packages) {
assert(defined(package.package_target),
"package_target must be supplied")
_package_out_dir =
get_label_info(package.package_target, "target_out_dir")
_package_name = get_label_info(package.package_target, "name")
_manifest_path =
"${_package_out_dir}/${_package_name}/package_manifest.json"
_manifest_path_rebased = rebase_path(_manifest_path, root_build_dir)
_config_data = []
if (defined(package.config_data)) {
foreach(c, package.config_data) {
base_package_config_data_sources += [ c.source ]
_config_data += [
{
source = rebase_path(c.source, root_build_dir)
destination = c.destination
},
]
}
}
base_package_manifests += [ _manifest_path ]
base_packages += [
{
manifest = _manifest_path_rebased
if (defined(package.config_data)) {
config_data = _config_data
}
},
]
}
}
# Similar to above, except for font packages, and a bit simpler since
# invoker.font_asset_provider_packages is a list of labels, not a list
# of scopes.
if (defined(invoker.font_asset_provider_packages)) {
foreach(package, invoker.font_asset_provider_packages) {
_package_out_dir = get_label_info(package, "target_out_dir")
_package_name = get_label_info(package, "name")
_manifest_path =
"${_package_out_dir}/${_package_name}/package_manifest.json"
_manifest_path_rebased = rebase_path(_manifest_path, root_build_dir)
base_package_manifests += [ _manifest_path ]
base_packages += [
{
manifest = _manifest_path_rebased
},
]
}
}
cache_packages = []
cache_package_manifests = []
cache_package_config_data_sources = []
if (defined(invoker.cache_packages)) {
foreach(package, invoker.cache_packages) {
assert(defined(package.package_target),
"package_target must be supplied")
_package_out_dir =
get_label_info(package.package_target, "target_out_dir")
_package_name = get_label_info(package.package_target, "name")
_manifest_path =
"${_package_out_dir}/${_package_name}/package_manifest.json"
_manifest_path_rebased = rebase_path(_manifest_path, root_build_dir)
_config_data = []
if (defined(package.config_data)) {
foreach(c, package.config_data) {
cache_package_config_data_sources += [ c.source ]
_config_data += [
{
source = rebase_path(c.source, root_build_dir)
destination = c.destination
},
]
}
}
cache_package_manifests += [ _manifest_path ]
cache_packages += [
{
manifest = _manifest_path_rebased
if (defined(package.config_data)) {
config_data = _config_data
}
},
]
}
}
base_driver_packages = []
driver_package_manifests = []
if (defined(invoker.base_driver_packages)) {
foreach(driver_package, invoker.base_driver_packages) {
assert(defined(driver_package.package_target),
"package target must be supplied")
_package_out_dir =
get_label_info(driver_package.package_target, "target_out_dir")
_package_name = get_label_info(driver_package.package_target, "name")
_manifest_path =
"${_package_out_dir}/${_package_name}/package_manifest.json"
_manifest_path_rebased = rebase_path(_manifest_path, root_build_dir)
driver_package_manifests += [ _manifest_path ]
base_driver_packages += [
# This scope needs to serialize to json and deserialize
# to a DriverDetails Assembly config struct
{
package = _manifest_path_rebased
components = driver_package.driver_components
},
]
}
}
}
_assembly_config = {
# Create the platform configuration section from the caller's argument
platform = invoker.platform
assert(defined(platform.build_type),
"The platform build-type must be specified.")
# Create the product configuration section from the caller's arguments.
product = {
if (defined(invoker.product)) {
forward_variables_from(invoker.product, "*")
}
assert(!defined(packages),
"Packages cannot be directly supplied under product")
packages = {
base = files.base_packages
cache = files.cache_packages
}
base_drivers = files.base_driver_packages
}
}
# Generate the Product Assembly configuration file itself.
#
# While deps are passed to this, the target created with the target_name does
# depend on those deps, but creates a separate target (suffixed with
# `.inputs`) that wraps up the input labels and the dependencies. This needs
# to be depended-upon by this template's `target_name`, with the validator
# target that's created depending on this file here, _without_ depending on
# the inputs and deps, so that the validation target can use the json file for
# textual comparison but without having the burden of all the compilation deps
# of this template.
#
product_assembly_config_file(labels.assembly_config) {
forward_variables_from(invoker,
[
"deps",
"public_deps",
"testonly",
])
visibility = [
":${labels.assembly_config_for_validation}",
":${labels.target_name}",
]
outputs = [ files.assembly_config_file ]
product_assembly_config = _assembly_config
}
# These are used to detect if the deps don't correspond to a set of input
# files (the deps can be larger than the set of files, but not the other way
# around). Since we're computing the manifest paths from the labels, if the
# label to something other than a package is added, we'll compute a manifest
# path that doesn't exist. This catches it here, instead of inside a build
# action which can't explain why it can't find a file.
# Create a target for the base packages, so they appear in the dep graph
# as distinct from the cache packages, and validate that they produce all of
# the manifests whose paths were computed from the labels.
group_with_inputs(labels.base_package_set) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.base_package_manifests
deps = labels.base_package_labels
}
group_with_inputs(labels.base_package_config_data) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.base_package_config_data_sources
deps = labels.base_package_config_data_deps
}
# Create a target for the base driver packages, so they appear in the dep
# graph as distinct from the cache packages, and validate that they produce
# all of the manifests whose paths were computed from the labels.
group_with_inputs(labels.base_driver_package_set) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.driver_package_manifests
deps = labels.base_driver_package_labels
}
# Create a target for the cache packages, so they appear in the dep graph
# as distinct from the base packages, and validate that they produce all of
# the manifests whose paths were computed from the labels.
group_with_inputs(labels.cache_package_set) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.cache_package_manifests
deps = labels.cache_package_labels
}
group_with_inputs(labels.cache_package_config_data) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.cache_package_config_data_sources
deps = labels.cache_package_config_data_deps
}
group(labels.target_name) {
forward_variables_from(invoker,
[
"inputs",
"testonly",
"visibility",
])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":${labels.assembly_config}" ]
if (!defined(deps)) {
deps = []
}
deps += [
# Add the group of inputs found by looking at the known config schema
# items that contain paths to files, so that they are guaranteed to be
# added to the ninja deps for the product assembly configuration.
":${labels.assembly_config_inputs}",
# Add the groups of package manifests that validate that each set of
# package labels contain labels that produce package manifests in the
# expected location.
":${labels.base_driver_package_set}",
":${labels.base_package_config_data}",
":${labels.base_package_set}",
":${labels.cache_package_config_data}",
":${labels.cache_package_set}",
]
# Block all metadata walks for packages, distribution entries, etc. These
# inputs should not exist in metadata walks, as they are added via the paths
# in the assembly config itself.
metadata = {
package_barrier = []
assembly_package_barrier = []
config_package_barrier = []
driver_package_barrier = []
system_image_package_barrier = []
distribution_entries_barrier = []
}
}
# A testonly group with no visibilty restrictions, that allows the use of the
# generated product assembly config file in validation actions that don't
# require the existence of the packages and binaries that it points to.
group(labels.assembly_config_for_validation) {
testonly = true
public_deps = [ ":${labels.assembly_config}" ]
}
}