blob: 081bf605fe6ee43876ca21b8d6ee6e43d0a7eeea [file]
# Copyright 2023 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/config/fuchsia/target_api_level.gni")
import("//build/sdk/sdk_atom.gni")
import("//build/sdk/sdk_noop_atom.gni")
import("//tools/cmc/build/expect_includes.gni")
# Defines an in-tree package for distribution in the SDK, landing
# the end package manifest at
# `sdk://packages/{arch}/release/${distribution_name}/package_manifest.json`.
# May only be used in the default_toolchain.
# Does not define any targets when targeting API level "PLATFORM".
#
# Example:
# ```
# fuchsia_package("my-package") {
# deps = [
# ":main_component",
# ]
# }
#
# sdk_fuchsia_package("my-sdk-package") {
# distribution_name = "my-package"
# package_label = ":my-package"
# category = "partner"
# expected_files_exact = [
# "relative/path/to/file_a",
# ]
# }
# ```
#
# Parameters
#
# package_label
# The fuchsia_package to distribute. Must point to a fuchsia_package() target, or
# one of its wrappers (e.g. fuchsia_test_package()).
# Type: label.
#
# distribution_name
# Distribution name of the package. The package manifest will be edited to use this
# name.
# Type: string.
#
# category
# The SDK category for the package. Must be "partner".
# See //build/sdk/sdk_atom.gni for more.
# Type: string.
#
# sdk_area (optional)
# [string] The API area responsible for maintaining this package.
# See //build/sdk/sdk_atom.gni.
#
# api_level_added
# API level at which the package was added to the SDK. API level defined at:
# https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0002_platform_versioning#sdk
# Type: unsigned integer.
#
# api_level_removed (optional)
# API level at which the package was removed from the SDK. If unspecified,
# we assume the package is available at all API levels after
# `api_level_added`. API level defined at:
# https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0002_platform_versioning#sdk
# Type: unsigned integer.
#
# expected_files_exact (optional)
# List of paths, relative to the package's root, for all files that
# participate in that package's content checklist file. Their content hash will be
# compared to the ones in
# //sdk/packages/${distribution_name}/${target_cpu}/release/content_checklist.api.
# Any paths specified in this list that are not found, or do not have hashes that match
# their golden content checklist file equivalent, will cause an error during checklist
# file generation.
# Type: list of relative package paths.
#
# expected_files_present (optional)
# List of paths, relative to the package's root, for all files that
# do not participate in that package's content checklist file. Their content
# does not matter for verification, but these paths must appear in
# the golden
# //sdk/packages/${distribution_name}/${target_cpu}/release/content_checklist.api file
# and in the same order.
# Any paths specified in this list that are not found in the specified
# manifest will cause an error during checklist file generation.
# Type: list of relative package paths.
#
template("sdk_fuchsia_package") {
assert(!defined(invoker.stable), "Packages are always stable.")
assert(
current_toolchain == default_toolchain,
"Unexpected use of `sdk_fuchsia_package` template with non-default toolchain `${current_toolchain}`.")
assert(defined(invoker.api_level_added),
"Must specify the API level at which the package was added")
assert(defined(invoker.package_label), "Must define a package to distribute.")
assert(defined(invoker.distribution_name),
"Must define a distribution name for this package.")
assert(defined(invoker.category),
"Must define an SDK category for this package.")
valid_categories = [
# "compat_test" is only for ABI compatibility and thus not applicable.
# "host_tool" is only for ABI compatibility and thus not applicable.
# "prebuilt" is only for ABI compatibility and thus not applicable.
"partner",
]
assert(
valid_categories + [ invoker.category ] - [ invoker.category ] !=
valid_categories,
"'${target_name}' has unsupported SDK category '${invoker.category}'. Must be one of ${valid_categories}.")
# The IDK only contains packages for specific API levels.
if (current_build_target_api_level != "PLATFORM") {
# Build the package for supported API levels.
package_in_target_api_level =
invoker.api_level_added <= current_build_target_api_level_as_integer
if (package_in_target_api_level && defined(invoker.api_level_removed)) {
package_in_target_api_level =
current_build_target_api_level_as_integer < invoker.api_level_removed
}
if (package_in_target_api_level) {
if (defined(invoker.expected_files_exact)) {
expected_files_exact = invoker.expected_files_exact
} else {
expected_files_exact = []
}
if (defined(invoker.expected_files_present)) {
expected_files_present = invoker.expected_files_present
} else {
expected_files_present = []
}
# Label of package to be included in the SDK. Used for tracking
# `package_manifest.json`
_full_package_label =
get_label_info(invoker.package_label, "label_no_toolchain")
package_manifest_file =
get_label_info(_full_package_label, "target_out_dir") + "/" +
get_label_info(_full_package_label, "name") + "/package_manifest.json"
# End output directory, different for each arch and API level.
base = "${target_cpu_dir_name_for_target_api_level}"
_sdk_package_dir = "${target_out_dir}/sdk_packages/${base}/release"
_distribution_name = invoker.distribution_name
# Note: Golden file does not support multiple API level currently.
golden_content_checklist_file =
"//sdk/packages/${_distribution_name}/release/content_checklist.api"
computed_content_checklist_file =
"${_sdk_package_dir}/content_checklist.api"
_content_checklist_target = "${target_name}.content_checklist"
_content_checklist_depfile =
"${target_gen_dir}/${_content_checklist_target}.d"
_tool_deps = []
_ffx_package_bin = "${host_out_dir}/ffx-package_unversioned"
_far_tool_bin = "${host_out_dir}/far"
if (host_tools_base_path_override == "") {
_tool_deps += [
"//src/developer/ffx/plugins/package:ffx_package_tool($host_toolchain)",
"//src/sys/pkg/bin/far:bin($host_toolchain)",
]
} else {
_host_tool_dir = "${root_build_dir}/${host_tools_base_path_override}"
_ffx_package_bin =
_host_tool_dir + "/" + rebase_path(_ffx_package_bin, root_build_dir)
_far_tool_bin =
_host_tool_dir + "/" + rebase_path(_far_tool_bin, root_build_dir)
}
# Generates content checklist file using input package manifest, and runs
# checks against golden. See
# `//build/packages/generate_sdk_package_content_checklist.py` for more.
action(_content_checklist_target) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/packages/generate_sdk_package_content_checklist.py"
inputs = [
package_manifest_file,
_ffx_package_bin,
_far_tool_bin,
]
outputs = [ computed_content_checklist_file ]
deps = [ _full_package_label ] + _tool_deps
depfile = _content_checklist_depfile
args = [
"--manifest",
rebase_path(package_manifest_file, root_build_dir),
"--output",
rebase_path(computed_content_checklist_file, root_build_dir),
"--reference",
rebase_path("${golden_content_checklist_file}", root_build_dir),
"--ffx-bin",
rebase_path(_ffx_package_bin, root_build_dir),
"--far-bin",
rebase_path(_far_tool_bin, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
]
# FIXME(https://fxbug.dev/42082703)
if (is_coverage) {
args += [ "--is-coverage" ]
}
foreach(exact_file, expected_files_exact) {
args += [
"--expected-files-exact",
exact_file,
]
}
foreach(present_file, expected_files_present) {
args += [
"--expected-files-present",
present_file,
]
}
}
sdk_atom(target_name) {
forward_variables_from(invoker,
[
"testonly",
"category",
"sdk_area",
])
id = "sdk://packages/${_distribution_name}"
idk_name = _distribution_name
meta = {
type = "package"
dest = "packages/${_distribution_name}/meta.json"
source_prebuild_info = {
api_level = "$current_build_target_api_level"
arch = target_cpu
package_manifest =
rebase_path(package_manifest_file, root_build_dir)
}
}
non_sdk_deps = [ ":${_content_checklist_target}" ]
# Barrier enforced to prevent assembly from adding to `base` set,
# keeping this target as an SDK-only addition.
metadata = {
package_barrier = []
}
}
} else {
# Current API level is not supported, so create an empty atom.
print(
"Empty IDK atom created for package `${target_name}`, which is not supported in API level ${current_build_target_api_level}.")
sdk_noop_atom(target_name) {
forward_variables_from(invoker,
[
"testonly",
"category",
"sdk_area",
])
id = "sdk://packages/${invoker.distribution_name}"
}
# Suppress unused variable warnings.
not_needed(invoker,
[
"distribution_name",
"expected_files_exact",
"expected_files_present",
"package_label",
])
} # end if (package_in_target_api_level)
} else {
not_needed(invoker, "*")
}
}