blob: cd37ebfb3f12de2761993250114ed0702f83ece0 [file] [log] [blame]
# 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`.
#
# Example:
# ```
# fuchsia_package("my-package") {
# deps = [
# ":main_component",
# ]
# }
#
# sdk_fuchsia_package("my-sdk-package") {
# distribution_name = "my-package"
# package_label = ":my-package"
# category = "public"
# 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 either "public" or "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.api_level_added),
"Must specify the API level at which the package was added")
package_in_target_api_level =
override_target_api_level != "PLATFORM" &&
invoker.api_level_added <= override_target_api_level
if (defined(invoker.api_level_removed)) {
package_in_target_api_level =
package_in_target_api_level &&
override_target_api_level < invoker.api_level_removed
}
if (package_in_target_api_level && current_toolchain == target_toolchain) {
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 = [
"excluded",
"experimental",
"internal",
"cts",
"partner_internal",
"partner",
"public",
]
assert(
valid_categories + [ invoker.category ] - [ invoker.category ] !=
valid_categories,
"Invalid SDK category ${invoker.category}, must be one of ${valid_categories}")
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_package_file_list = "${_sdk_package_dir}/file_list.fini"
_sdk_package_metadata = "${_sdk_package_dir}/metadata"
_sdk_manifest_target = "${target_name}.sdk_manifest"
_sdk_package_depfile = "${target_gen_dir}/${_sdk_manifest_target}.d"
# Generates SDK primitives, including `file_list`, `metadata`, and
# package manifests (both target package and subpackages) with
# files structured relative to SDK directories. See
# `//build/packages/generate_sdk_package_manifest.py` for more.
action(_sdk_manifest_target) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/packages/generate_sdk_package_manifest.py"
inputs = [
package_manifest_file,
computed_content_checklist_file,
]
outputs = [
_sdk_package_file_list,
_sdk_package_metadata,
]
deps = [
":${_content_checklist_target}",
_full_package_label,
]
depfile = _sdk_package_depfile
args = [
"--manifest",
rebase_path(package_manifest_file, root_build_dir),
"--sdk-package-content-checklist-path",
rebase_path(computed_content_checklist_file, root_build_dir),
"--output",
rebase_path(_sdk_package_dir, root_build_dir),
"--api-level",
"${override_target_api_level}",
"--target-cpu",
"${target_cpu}",
"--depfile",
rebase_path(depfile, root_build_dir),
"--distribution-name",
_distribution_name,
]
}
sdk_atom(target_name) {
forward_variables_from(invoker,
[
"testonly",
"category",
"sdk_area",
])
id = "sdk://packages/${_distribution_name}"
meta = {
schema = "package"
dest = "packages/${_distribution_name}/meta.json"
source = "${_sdk_package_metadata}"
}
file_list = _sdk_package_file_list
non_sdk_deps = [ ":${_sdk_manifest_target}" ]
# Barrier enforced to prevent assembly from adding to `base` set,
# keeping this target as an SDK-only addition.
metadata = {
package_barrier = []
}
}
} else if (!package_in_target_api_level) {
# Current API level is not supported, so create an empty atom.
sdk_noop_atom(target_name) {
forward_variables_from(invoker,
[
"testonly",
"category",
"sdk_area",
])
id = "sdk://packages/${invoker.distribution_name}"
type = "package"
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
} else {
# Fuchsia SDK packages should only be built with target_toolchain. However, it
# is possible for package targets to be expanded in other toolchains (host,
# variant, etc.). In these cases, make sdk_fuchsia_package expand to nothing.
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
}
}