blob: ad8404a58466afa709ca9ed8fa05b927a2e8e35d [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/board.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.
#
# 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") {
# Board-provided drivers shouldn't be part of the product assembly config,
# but we don't yet have that mechanism in place, so we will insert them here
# as if they are product-provided.
#
# But only include them on "minimal" feature_set_level products. This is
# the default if `platform` is omitted, or `platform.feature_set_level` is
# unspecified. include_board_drivers = true
include_board_drivers = true
if (defined(invoker.platform)) {
_platform = invoker.platform
if (defined(_platform.feature_set_level) &&
_platform.feature_set_level != "minimal") {
include_board_drivers = false
} else {
not_needed([ "_platform" ])
}
}
labels = {
# So it can be reused.
target_name = target_name
assembly_config = "${target_name}.product_assembly_config.json"
# 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 ]
}
}
}
}
}
# 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 ]
}
}
# Board-provided drivers shouldn't be part of the product assembly config,
# but we don't yet have that mechanism in place, so we will insert them here
# as if they are product-provided.
#
# This is a GN arg defined in `//build/board.gni`, and is set in the _board_
# gni files that are imported into `args.gni`.
board_driver_package_labels = []
if (include_board_drivers) {
foreach(package, board_provided_drivers) {
board_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
}
},
]
}
}
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
},
]
}
}
# The board drivers are added to a separate list from the base drivers, so
# that they can be distinguised better in the GN build graph.
#
# As a global GN build arg, it is always defined, so the if(defined()) check
# is not necessary here.
board_driver_packages = []
board_driver_package_manifests = []
if (include_board_drivers) {
foreach(driver_package, board_provided_drivers) {
assert(
defined(driver_package.package_target),
"board driver did not supply a package target: ${driver_package}")
_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)
board_driver_package_manifests += [ _manifest_path ]
board_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
},
]
}
}
}
_sensor_config = false
if (defined(invoker.platform)) {
_p = invoker.platform
if (defined(_p.ui)) {
_ui = _p.ui
if (defined(_ui.sensor_config)) {
_sensor_config = _ui.sensor_config
} else {
not_needed([ "_ui" ])
}
} else {
not_needed([ "_p" ])
}
}
_assembly_config = {
# Create the platform configuration section from the caller's argument
platform = {
if (defined(invoker.platform)) {
forward_variables_from(invoker.platform, "*")
}
if (_sensor_config != false) {
ui.sensor_config = rebase_path(_sensor_config, root_build_dir)
}
assert(defined(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
}
# Pass both the board-provided and the product-provided base drivers in the config
# until we have support for the board input to product assembly to provide
# drivers.
base_drivers = files.base_driver_packages + files.board_driver_packages
}
}
# Generate the Product Assembly configuration file itself.
#
# This does _not_ have deps on any of the passed in targets, which is why it
# restricts it's visibility to the target that does dep on them.
#
generated_file(labels.assembly_config) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [
":${labels.assembly_config_for_validation}",
":${labels.target_name}",
]
outputs = [ files.assembly_config_file ]
output_conversion = "json"
contents = _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
}
# Create a target for the board-provided driver packages, so they appear in
# the dep graph as distinct from the product-provided driver packages, and
# validate that they produce all of the manifests whose paths were computed
# from the labels.
group_with_inputs(labels.board_driver_package_set) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":${labels.target_name}" ]
inputs = files.board_driver_package_manifests
deps = labels.board_driver_package_labels
}
group_with_inputs(labels.target_name) {
forward_variables_from(invoker,
[
"deps",
"public_deps",
"testonly",
"visibility",
])
if (!defined(public_deps)) {
public_deps = []
}
inputs = []
public_deps += [ ":${labels.assembly_config}" ]
if (_sensor_config != false) {
inputs += [ _sensor_config ]
}
if (!defined(deps)) {
deps = []
}
deps += [
":${labels.base_driver_package_set}",
":${labels.base_package_config_data}",
":${labels.base_package_set}",
":${labels.board_driver_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}" ]
}
}