blob: fbb6800f76fc4b600e29e3ea39dada1e98e0c6c0 [file] [log] [blame]
# Copyright 2017 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/cmx/cmx.gni")
import("//build/compiled_action.gni")
import("//build/disabled_for_asan.gni")
import("//build/images/manifest.gni")
import("//build/json/validate_json.gni")
import("//build/package/component.gni")
import("//build/testing/test_spec.gni")
declare_args() {
# The package key to use for signing Fuchsia packages made by the
# `package()` template (and the `system_image` package). If this
# doesn't exist yet when it's needed, it will be generated. New
# keys can be generated with the `pm -k FILE genkey` host command.
system_package_key = "//build/development.key"
# Board name used for paving and amber updates.
board_name = ""
}
# Generate a list of package metadata exported from all packages in the
# transitive closure of declared dependencies, with an implied barrier at each
# package (dependency chains cease processing at package targets).
#
# Parameters
#
# data_keys (required)
# [list of strings] A list of package target metadata keys to collect into a
# list. See gn help for data_keys for more information.
#
# Well known package metadata:
#
# snapshot_entries
# The snapshot entries are consumed for the production of the snapshots
# that feed into `fx delta` for version to version OTA size computations.
#
# blob_manifests
# The blob manifests are aggregated by the image build process to produce
# manifests to publish to repositories and to produce manifests to write
# into blobfs images.
#
# meta_far_index_entries
# The metafar index entries are aggregated in image builds to produce
# manifests of packages to publish to a repository.
#
# meta_far_merkle_index_entries
# The metafar merkle index entries are aggregated in image builds to
# produce package server indices for monolith serving.
#
# package_output_manifests
# The path of each output manifest for each package.
#
# package_barrier
# [list of labels] This is passed to walk_keys and can be used as a barrier to
# control dependency propgatation below a package target.
#
# data_deps (optional)
# deps (optional)
# metadata (optional)
# output_conversion (optional)
# outputs (optional)
# public_deps (optional)
# rebase (optional)
# testonly (optional)
# visibility (optional)
# Same as for any GN `generated_file()` target.
template("package_metadata_list") {
generated_file(target_name) {
forward_variables_from(invoker,
[
"data_deps",
"data_keys",
"deps",
"metadata",
"output_conversion",
"outputs",
"public_deps",
"rebase",
"testonly",
"visibility",
])
if (!defined(outputs)) {
outputs = [
target_gen_dir + "/" + target_name,
]
}
walk_keys = [ "package_barrier" ]
}
}
# Generate a list of fuchsia-pkg URLs for component from all packages in the
# transitive closure of declared dependencies, with an implied barrier at each
# package (dependency chains cease processing at package targets).
#
# Parameters
#
# data_deps (optional)
# deps (optional)
# outputs (optional)
# public_deps (optional)
# testonly (optional)
# visibility (optional)
# Same as for any GN `generated_file()` target.
template("component_index_metadata_list") {
generated_file(target_name) {
forward_variables_from(invoker,
[
"data_deps",
"deps",
"outputs",
"public_deps",
"testonly",
"visibility",
])
if (!defined(outputs)) {
outputs = [
target_gen_dir + "/" + target_name,
]
}
data_keys = [ "component_index" ]
walk_keys = [ "component_index_barrier" ]
}
}
# Generate a signed, sealed package file from a manifest.
#
# Parameters
#
# manifest (required)
# [label] A generate_manifest() target defined earlier in the same file.
# This provides the contents for the package.
#
# The following two items are only required in order to produce metadata about
# the package sets, and may be removed in the future:
#
# package_name (required)
# [string] Name of the package (should match what is in meta/package)
# package_variant (default: "0")
# [string] Variant of the package (should match what is in meta/package)
#
# deps (optional)
# test (optional)
# visibility (optional)
# Same as for any GN `action()` target.
template("pm_build_package") {
compiled_action(target_name) {
tool = "//garnet/go/src/pm:pm_bin"
tool_output_name = "pm"
forward_variables_from(invoker,
[
"data_deps",
"deps",
"public_deps",
"testonly",
"visibility",
"package_name",
"package_variant",
])
if (!defined(deps)) {
deps = []
}
pkg_manifest_outputs = get_target_outputs(invoker.manifest)
pkg_manifest_file = pkg_manifest_outputs[0]
pkg_out_dir = "$target_out_dir/$target_name"
if (!defined(package_variant)) {
package_variant = "0"
}
assert(defined(package_name), "A package name is required")
assert(package_name != "", "A package name is required")
deps += [
"//build/images:system_package_key_check",
invoker.manifest,
]
inputs = [
pkg_manifest_file,
system_package_key,
]
depfile = "$pkg_out_dir/meta.far.d"
pkg_output_manifest = "$pkg_out_dir/package_manifest.json"
outputs = [
# produced by seal, must be listed first because of depfile rules.
"$pkg_out_dir/meta.far",
# update
"$pkg_out_dir/meta/contents",
# sign
"$pkg_out_dir/meta/pubkey",
"$pkg_out_dir/meta/signature",
# seal
"$pkg_out_dir/meta.far.merkle",
# package blob json manifest
"$pkg_out_dir/blobs.json",
# package blob manifest
"$pkg_out_dir/blobs.manifest",
# package output manifest
pkg_output_manifest,
]
blobs_json_path = rebase_path("${pkg_out_dir}/blobs.json", root_build_dir)
# Package metadata must be kept in sync between package.gni and
# prebuilt_package.gni. The template package_metadata_list in package.gni
# documents the fields required to be implemented by both templates.
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
# We only ever want to collect the following package manifests from
# packages, not from dependencies of packages, so the barrier prevents us
# from walking any further in the dependency chain. See `gn help
# walk_keys` for more information on the mechanic.
package_barrier = []
package_names = [ package_name ]
snapshot_entries = [ "$package_name/$package_variant=$blobs_json_path" ]
blob_manifests =
[ rebase_path("$pkg_out_dir/blobs.manifest", root_build_dir) ]
meta_far_index_entries =
[ "$package_name/$package_variant=" +
rebase_path("$pkg_out_dir/meta.far", root_build_dir) ]
meta_far_merkle_index_entries =
[ "$package_name/$package_variant=" +
rebase_path("$pkg_out_dir/meta.far.merkle", root_build_dir) ]
package_output_manifests = [ pkg_output_manifest ]
}
args = [
"-k",
rebase_path(system_package_key, root_build_dir),
"-o",
rebase_path(pkg_out_dir, root_build_dir),
"-m",
rebase_path(pkg_manifest_file, root_build_dir),
"-n",
package_name,
"-version",
package_variant,
"build",
"-output-package-manifest",
rebase_path(pkg_output_manifest, root_build_dir),
"-depfile",
"-blobsfile",
"-blobs-manifest",
]
}
}
# Defines a package
#
# The package template is used to define a unit of related code and data.
# A package always has a name (defaulting to the target name) and lists of
# scopes describing the components of the package.
#
# Parameters
#
# deprecated_system_image (optional, default `false`)
# [bool] If true, the package is stored in the /system filesystem image
# rather than in a Fuchsia package.
#
# TODO(PKG-46): Will be removed entirely eventually.
#
# If this package uses the `drivers` parameter,
# `deprecated_system_image` must be set to `true` because we are not
# yet sophisticated enough to load drivers out of packages.
#
# meta (optional)
# [list of scopes] Defines the metadata entries in the package. A metadata
# entry is typically a source file and is placed in the `meta/` directory of
# the assembled package.
#
# Requires `deprecated_system_image` to be `false`.
#
# Entries in a scope in the meta list:
#
# path (required)
# [path] Location of entry in source or build directory. If the
# resource is checked in, this will typically be specified as a
# path relative to the BUILD.gn file containing the `package()`
# target. If the resource is generated, this will typically be
# specified relative to `$target_gen_dir`.
#
# dest (required)
# [path] Location the resource will be placed within `meta/`.
#
# binary (optional, *DEPRECATED*)
# [string] The path to the primary binary for the package, relative to
# `$root_out_dir`. The binary will be placed in the assembled package at
# `bin/app` and will be executed by default when running the package.
#
# Requires `deprecated_system_image` to be `false`.
#
# binaries (optional)
# [list of scopes] Defines the binaries in the package. A binary is
# typically produced by the build system and is placed in the `bin/`
# directory of the assembled package.
#
# Entries in a scope in the binaries list:
#
# name (required)
# [string] Name of the binary.
#
# source (optional)
# [path] Location of the binary in the build directory if it is not
# at `$root_out_dir/$name`.
#
# dest (optional)
# [path] Location the binary will be placed within `bin/`.
#
# shell (optional)
# [boolean] (default: false) When true, the binary is runnable from the shell.
# Shell binaries are run in the shell namespace and are not run as components.
#
# components (optional)
# [list of fuchsia_component targets] Defines all the components this
# package should include in assembled package.
#
# Requires `deprecated_system_image` to be `false`.
#
# tests (optional)
# [list of scopes] Defines the test binaries in the package. A test is
# typically produced by the build system and is placed in the `test/`
# directory of the assembled package.
#
# Entries in a scope in the tests list:
#
# name (required)
# [string] Name of the test.
#
# dest (optional)
# [path] Location the binary will be placed within `test/`.
#
# disabled (optional)
# [bool] Whether to disable the test on continuous integration
# jobs. This can be used when a test is temporarily broken, or if
# it is too flaky or slow for CI. The test will also be skipped by
# the `runtests` command.
#
# environments (optional, default: [ { dimensions = { device_type = "QEMU" } } ])
# [list of scopes] Device environments in which the test should run.
#
# Each scope in $environments contains:
#
# dimensions (required)
# [scope] Dimensions of bots to target. Valid dimensions are
# element-wise subsets of the test platform entries defined in
# //build/testing/platforms.gni.
#
# tags (optional)
# [list of strings] Keys on which tests may be grouped. Tests with
# given keys will be run (1) together, and (2) only with support
# from the Infrastructure team. Labels are used as an escape hatch
# from the default testing pipeline for special tests or environments.
#
# drivers (optional)
# [list of scopes] Defines the drivers in the package. A driver is
# typically produced by the build system and is placed in the `driver/`
# directory of the assembled package.
#
# Requires `deprecated_system_image` to be `true`.
#
# Entries in a scope in the drivers list:
#
# name (required)
# [string] Name of the driver.
#
# loadable_modules (optional)
# [list of scopes] Defines the loadable modules in the package. These
# are produced by `loadable_module()` GN targets, and are typically
# placed in the `lib/` directory of the assembled packaged.
#
# Entries in a scope in the loadable_modules list:
#
# name (required)
# [string] Name of the loadable_module.
#
# dest (optional, default: "lib")
# [string] Location the binary will be placed in the package.
#
# libraries (optional, *DEPRECATED*)
# [list of scopes] Defines the (shared) libraries in the package. A library
# is placed in the `lib/` directory of the assembled package.
#
# This is deprecated but is necessary in some `system_image` packages
# that install libraries used by things that don't properly isolate
# their dependencies. Do not use it unless you are sure you have to.
#
# Entries in a scope in the libraries list:
#
# name (required)
# [string] Name of the library
#
# source (optional)
# [path] Location of the binary in the build directory if it is not at
# `$root_out_dir/$name`
#
# dest (optional)
# [path] Location the binary will be placed within `lib/`
#
# resources (optional)
# [list of scopes] Defines the resources in the package. A resource is a
# data file that may be produced by the build system, checked in to a
# source repository, or produced by another system that runs before the
# build. Resources are placed in the `data/` directory of the assembled
# package.
#
# Entries in a scope in the resources list:
#
# path (required)
# [path] Location of resource in source or build directory. If the
# resource is checked in, this will typically be specified as a
# path relative to the BUILD.gn file containing the `package()`
# target. If the resource is generated, this will typically be
# specified relative to `$target_gen_dir`.
#
# dest (required)
# [path] Location the resource will be placed within `data/`.
#
# extra (optional)
# [list of paths] Manifest files containing extra entries, which
# might be generated by the build.
#
# deps (optional)
# public_deps (optional)
# data_deps (optional)
# testonly (optional)
# Usual GN meanings.
#
template("package") {
if (current_toolchain == target_toolchain) {
forward_variables_from(invoker, [ "testonly" ])
pkg_target_name = target_name
pkg = {
package_version = "0" # placeholder
forward_variables_from(invoker,
[
"binaries",
"binary",
"components",
"data_deps",
"deprecated_misc_storage",
"deprecated_system_image",
"deps",
"public_deps",
"drivers",
"extra",
"libraries",
"loadable_modules",
"meta",
"package_name",
"resources",
"visibility",
"tests",
])
if (!defined(binaries)) {
binaries = []
}
if (!defined(deprecated_system_image)) {
deprecated_system_image = false
}
if (!defined(deps)) {
deps = []
}
if (!defined(components)) {
components = []
}
if (!defined(data_deps)) {
data_deps = []
}
if (!defined(public_deps)) {
public_deps = []
}
if (!defined(extra)) {
extra = []
}
if (!defined(drivers)) {
drivers = []
}
if (!defined(loadable_modules)) {
loadable_modules = []
}
if (!defined(libraries)) {
libraries = []
}
if (!defined(meta)) {
meta = []
}
if (!defined(package_name)) {
package_name = pkg_target_name
}
foreach(component, components) {
deps += [ component ]
}
if (!defined(resources)) {
resources = []
}
if (!defined(tests)) {
tests = []
}
}
assert(pkg.package_name != "tests" && pkg.package_name != "app" &&
pkg.package_name != "bin" && pkg.package_name != "pkg" &&
pkg.package_name != "lib" && pkg.package_name != "package" &&
pkg.package_name != "binary" && pkg.package_name != "service" &&
pkg.package_name != "svc" && pkg.package_name != "component",
"${pkg.package_name} is too generic a name for a package")
pkg_label = get_label_info(":$pkg_target_name", "label_no_toolchain")
pkg_desc = "Package ${pkg_label} (${pkg.package_name}):"
if (pkg.deprecated_system_image) {
assert(pkg.meta == [],
"$pkg_desc deprecated_system_image incompatible with meta")
assert(pkg.components == [],
"$pkg_desc deprecated_system_image incompatible with components")
assert(!defined(pkg.binary),
"$pkg_desc deprecated_system_image incompatible with binary")
# Only appmgr can contribute binaries to /system/bin.
assert(pkg.binaries == [] || pkg.package_name == "appmgr",
"no new packages are allowed to add binaries to /system/bin")
assert(pkg.tests == [], "tests are not allowed in /system")
} else {
assert(pkg.drivers == [],
"$pkg_desc drivers requires deprecated_system_image")
assert(pkg.libraries == [],
"$pkg_desc libraries requires deprecated_system_image")
if (defined(pkg.binary)) {
pkg.binaries += [
{
name = "app"
source = pkg.binary
},
]
}
}
# Validate .cmx files
foreach(meta, pkg.meta) {
if (get_path_info(meta.dest, "extension") == "cmx") {
validate = "validate_" + pkg_target_name + "_" +
get_path_info(meta.dest, "file")
cmx_validate(validate) {
data = meta.path
# the cmx file may be generated by one of this package's dependencies,
# but we don't know which one, so depend on all package deps here.
deps = pkg.deps
public_deps = pkg.public_deps
extra_schemas = []
# Remove this validation when the "deprecated-misc-storage" feature is removed.
if (!defined(pkg.deprecated_misc_storage)) {
extra_schemas += [
{
schema = "//build/cmx/block_deprecated_misc_storage.json"
error_msg = "The 'deprecated-misc-storage' feature is deprecated and guarded by an allowlist."
},
]
}
}
pkg.deps += [ ":$validate" ]
}
}
# cmx_format will minify cmx files in non-debug builds, and pretty-print cmx
# files in debug builds
formatted_manifest = []
component_indices = []
foreach(meta, pkg.meta) {
if (get_path_info(meta.dest, "extension") == "cmx") {
component_indices += [ "fuchsia-pkg://fuchsia.com/" + pkg.package_name +
"#meta/" + meta.dest ]
format =
"format_" + pkg_target_name + "_" + get_path_info(meta.dest, "file")
cmx_format(format) {
data = rebase_path(meta.path)
deps = pkg.deps
}
formatted_outputs = []
formatted_outputs = get_target_outputs(":$format")
meta.path = formatted_outputs[0]
pkg.deps += [ ":$format" ]
} else if (get_path_info(meta.path, "extension") == "cml") {
component_indices += [ "fuchsia-pkg://fuchsia.com/" + pkg.package_name +
"#meta/" + meta.dest ]
compiled = "compiled_" + pkg_target_name + "_" +
get_path_info(meta.dest, "file")
cm_compile(compiled) {
data = rebase_path(meta.path)
deps = pkg.deps
public_deps = pkg.public_deps
}
pkg.deps += [ ":$compiled" ]
compiled_outputs = []
compiled_outputs = get_target_outputs(":$compiled")
meta.path = compiled_outputs[0]
}
formatted_manifest += [ meta ]
}
# Collect the package's primary manifest. For a system_image package,
# this is its contributions to the /system manifest. For an isolated
# package, this is the manifest for the package's `pkg/` filesystem.
pkg_manifest = []
foreach(meta, formatted_manifest) {
pkg_manifest += [
{
dest = "meta/${meta.dest}"
source = rebase_path(meta.path)
},
]
}
shell_binaries = []
foreach(binary, pkg.binaries) {
if (defined(binary.dest)) {
dest = binary.dest
} else {
dest = binary.name
}
dest = "bin/${dest}"
if (defined(binary.shell) && binary.shell) {
shellfile = target_gen_dir + "/" + dest + ".shell"
write_file(
shellfile,
[ "#!resolve fuchsia-pkg://fuchsia.com/${pkg.package_name}#${dest}" ])
shell_binaries +=
[ "${dest}=" + rebase_path(shellfile, root_build_dir) ]
}
pkg_manifest += [
{
dest = dest
if (defined(binary.source)) {
source = binary.source
} else {
source = binary.name
}
source = rebase_path(source, "", root_out_dir)
},
]
}
foreach(test, pkg.tests) {
is_disabled = defined(test.disabled) && test.disabled
if (defined(test.dest)) {
dest = test.dest
} else {
dest = test.name
}
if (is_disabled) {
dest = "disabled/${dest}"
}
dest = "test/${dest}"
if (pkg.deprecated_system_image) {
path = "/system/$dest"
} else {
path =
"/pkgfs/packages/${pkg.package_name}/${pkg.package_version}/$dest"
}
# TODO(TC-359): Remove this block once no more tests remain in
# disabled_for_asan.
is_asan = select_variant + [ "asan" ] - [ "asan" ] != select_variant
is_disabled_for_asan =
disabled_for_asan + [ path ] - [ path ] != disabled_for_asan
is_disabled = is_disabled || (is_asan && is_disabled_for_asan)
# In case of short-circuit evaluation, these values might go unused.
not_needed([
"is_asan",
"is_disabled_for_asan",
])
if (!is_disabled) {
test_spec_name = "${dest}_test_spec"
pkg.deps += [ ":$test_spec_name" ]
test_spec(test_spec_name) {
name = test.name
location = path
forward_variables_from(test, [ "environments" ])
}
}
pkg_manifest += [
{
dest = dest
source = rebase_path(test.name, "", root_out_dir)
},
]
}
foreach(module, pkg.loadable_modules) {
pkg_manifest += [
{
if (defined(module.dest)) {
dest = module.dest
} else {
dest = "lib"
}
dest += "/${module.name}"
source = rebase_path(module.name, "", root_out_dir)
},
]
}
foreach(driver, pkg.drivers) {
pkg_manifest += [
{
dest = "driver/${driver.name}"
source = rebase_path(driver.name, "", root_out_dir)
},
]
}
foreach(resource, pkg.resources) {
pkg_manifest += [
{
dest = "data/${resource.dest}"
source = rebase_path(resource.path)
},
]
}
# TODO(mcgrathr): Remove this when we can! Packages installing
# libraries in the system image is all kinds of wrong.
foreach(library, pkg.libraries) {
pkg_manifest += [
{
if (defined(library.dest)) {
dest = library.dest
} else {
dest = library.name
}
dest = "lib/${dest}"
if (defined(library.source)) {
source = library.source
} else {
# TODO(mcgrathr): This breaks when everything is a variant so
# that only this here is using the non-variant shlib build.
source = get_label_info(shlib_toolchain, "name")
source += "/${library.name}"
}
source = rebase_path(source, "", root_out_dir)
},
]
}
# Collect all the arguments describing input manifest files
# and all the entries we've just synthesized in `pkg_manifest`.
manifest_sources = pkg.extra
manifest_args = []
foreach(manifest_file, pkg.extra) {
manifest_file = rebase_path(manifest_file, root_build_dir)
manifest_args += [ "--manifest=${manifest_file}" ]
}
manifest_args += [ "--entry-manifest=${pkg_label}" ]
foreach(entry, pkg_manifest) {
manifest_sources += [ entry.source ]
manifest_args += [ "--entry=${entry.dest}=${entry.source}" ]
}
pkg.metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
shell_binary_entries = shell_binaries
# Subtle: we only want dependency on the package target to consume
# package metadata propagation for the package we're building, never
# for transitive package dependencies of this package. In the fuchsia
# package case, we want to therefore only include our local package
# build target. For the system image package case, we don't want to
# include anything.
package_barrier = []
}
if (defined(pkg.visibility)) {
pkg.visibility += [
# TODO(raggi): tighten build/images visibility once we've
# finished doing large scale cleanups.
"//build/images:*",
"//build/packages/*",
"//bundles/*",
"//garnet/packages/*",
"//peridot/packages/*",
"//src/modular/bundles/*",
"//topaz/packages/*",
"//vendor/*",
]
}
# An empty package() target doesn't actually generate a package at all.
# Conveniently, an empty system_image package has exactly that effect.
if (manifest_sources == [] && pkg.components == []) {
pkg.deprecated_system_image = true
}
if (pkg.deprecated_system_image) {
# System image packages just donate manifest arguments
generate_response_file(pkg_target_name) {
if (defined(pkg.visibility)) {
visibility = pkg.visibility
}
deps = pkg.deps
data_deps = pkg.data_deps
public_deps = pkg.public_deps
output_name = "${pkg_target_name}.system.rsp"
response_file_contents = manifest_args
metadata = {
forward_variables_from(pkg.metadata, "*")
system_image_rsps = [ "@" +
rebase_path(target_out_dir + "/" + output_name,
root_build_dir) ]
}
}
not_needed(component_indices)
} else {
# Fuchsia package aggregates a manifest from it's arguments and builds a
# signed metadata archive.
manifest = "${pkg_target_name}.manifest"
# Synthesize the meta/package file.
pkg_meta_package = "${pkg_target_name}_meta_package.json"
action(pkg_meta_package) {
visibility = [ ":$manifest" ]
script = "//build/gn/write_package_json.py"
outputs = [
"$target_out_dir/$pkg_meta_package",
]
args = [
"--name",
pkg.package_name,
"--version",
pkg.package_version,
rebase_path(pkg_meta_package, root_build_dir, target_out_dir),
]
}
generate_manifest(manifest) {
visibility = [ ":" + pkg_target_name ]
sources = manifest_sources + get_target_outputs(":$pkg_meta_package")
args = manifest_args +
[ "--entry=meta/package=" +
rebase_path(pkg_meta_package, "", target_out_dir) ]
deps = pkg.deps + [ ":$pkg_meta_package" ]
public_deps = pkg.public_deps
foreach(component, pkg.components) {
# TODO(CF-162): Add to component_indices
deps += [ component ]
component_name = get_label_info(component, "name")
dir = get_label_info(component, "target_out_dir")
rsp_file = "${dir}/${component_name}.rsp"
sources += [ rsp_file ]
args += [ "@" + rebase_path(rsp_file) ]
}
}
# Next generate a signed, sealed package file.
pm_build_package(pkg_target_name) {
if (defined(pkg.visibility)) {
visibility = pkg.visibility
}
package_name = pkg.package_name
manifest = ":$manifest"
metadata = {
forward_variables_from(pkg.metadata, "*")
component_index = component_indices
# We only ever want to collect the following component indices from
# packages, not from dependencies of packages, so the barrier prevents
# us from walking any further in the dependency chain. See `gn help
# walk_keys` for more information on the mechanic.
component_index_barrier = []
}
deps = pkg.deps
data_deps = pkg.data_deps
public_deps = pkg.public_deps
# Add the dep on the allowlist here rather than in pkg.deps earlier so that we can allow
# specific targets by the package target's name using visibility.
if (defined(pkg.deprecated_misc_storage)) {
deps += [
"${pkg.deprecated_misc_storage}:deprecated_misc_storage_allowlist",
]
}
}
}
} else {
group(target_name) {
forward_variables_from(invoker,
[
"public_deps",
"deps",
"testonly",
])
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
}
}