| # 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}" ] |
| } |
| } |