blob: ec16545e2372744d0471b9b67b059f3c91270504 [file] [log] [blame]
# Copyright 2020 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/clang/clang.gni")
import("//build/config/fuchsia/target_api_level.gni")
import("//build/dist/distribution_manifest_generated_file.gni")
import("//build/packages/package_metadata.gni")
import("//build/python/python_action.gni")
import("//build/rust/config.gni")
# Which tool(s) to use in this process are conditional based on this being a
# sub-build or normal build. Sub-builds use prebuilt binaries, whereas the
# normal build depends on the actual build targets. For each of the tools
# used by the `fuchsia_package()` template, we need to calculate the label,
# path, etc.
#
# This logic is taken from `compiled_action()`, and is similar to the logic
# there, although slightly simplified because we know we will not be opt'ing
# out of the use of prebuilt tools.
foreach(tool_info,
# This is a list of all input tools
[
{
name = "package-tool"
label = "//src/sys/pkg/bin/package-tool(${host_toolchain})"
},
{
name = "configc"
label = "//tools/configc(${host_toolchain})"
},
]) {
tool_desc = {
}
tool_output_name = get_label_info(tool_info.label, "name")
tool_output_dir = get_label_info(tool_info.label, "root_out_dir")
host_executable = "$tool_output_dir/$tool_output_name"
if (host_tools_base_path_override != "") {
# Use a prebuilt version of the host tool. Assume the tool is already
# built so do not add any dependency to the target.
host_executable_rebased = host_tools_base_path_override + "/" +
rebase_path(host_executable, root_build_dir)
host_executable =
"//" + rebase_path(host_executable_rebased, "//", root_build_dir)
tool_desc = {
name = tool_info.name
input = host_executable
rebased = host_executable_rebased
}
} else {
# Use the compilation dep for the tool
tool_desc = {
name = tool_info.name
dep = tool_info.label
input = host_executable
rebased = rebase_path(host_executable, root_build_dir)
}
}
# Setup useful variables for each of these tools. Because we don't have the
# ability in GN to reference scope variables by name in L-value contexts, we
# need to do this odd bit of logic.
if (tool_info.name == "package-tool") {
# Setup a variable for the description of the package-tool
_tools_package_tool = tool_desc
} else if (tool_info.name == "configc") {
# Setup a variable for the description of the configc tool
_tools_configc = tool_desc
}
}
# Defines a Fuchsia package.
# See: https://fuchsia.dev/fuchsia-src/development/components/build
#
# Fuchsia packages are a collection of any number of files (or resources), each
# with a unique path that is relative to the package's root.
# Package targets collect resources via their dependencies. These dependencies
# are typically either:
#
# * `fuchsia_component()` targets, which provide their component manifest and
# other files that the component needs (such as an executable)
# * other `fuchsia_package()` targets, declared as `subpackages`
#
# Packages can be defined as a collection of pairs each representing a file in
# the package. Each pair consists of the path in the package that is assigned
# to the file, and a path relative to the build system's output directory where
# the contents of the file will be sourced from.
# This mapping is generated at build time, and is known as the package
# manifest.
#
# To view the package manifest, For instance assume you have defined
# a package at `path/to/project:my_package` and built it:
# ```
# $ fx build path/to/project:my_package
# ```
# You can then find the path to the generated manifest:
# ```
# $ fx gn outputs out/default path/to/project:my_package_manifest
# ```
#
# The package name is defined by the target name.
# Some rules apply to package names.
# See: https://fuchsia.dev/fuchsia-src/concepts/packages/package_url#package-name
#
# It's recommended for a package to depend on one or more `fuchsia_component()`
# targets, and zero or more `subpackages` and/or `renameable_subpackages`.
#
# Examples:
# ```
# fuchsia_package("my-package") {
# deps = [
# ":main_component",
# ]
# subpackages = [
# ":second_package",
# ]
# }
# ```
#
# ```
# fuchsia_package("my-package") {
# deps = [
# ":main_component",
# ]
# renameable_subpackages = [
# {
# package = ":second_package"
# },
# {
# name = "renamed-subpackage"
# package = ":third_package"
# }
# ]
# }
# ```
#
# ```
# fuchsia_package("my-package") {
# deps = [
# ":first_component",
# ":second_component",
# ]
# }
# ```
#
# Parameters
#
# package_name (optional)
# The name of the package.
# Type: string
# Default: target_name
#
# disable_elf_binaries_checks (optional)
# Set to true to disable ELF binaries verification checks. Useful
# if your package includes non-Fuchsia ELF binaries, or if some
# of them are unstripped.
# Type: boolean
# Default: false
#
# validate_structured_config (optional)
# If true, check that component manifests which declare config schemas have been
# packaged with the resources needed to resolve them at runtime. Only useful for
# those packages which fully generate their configuration during the build. If a
# component has configuration provided by assembly tooling, that happens after the
# package is built and this should be set to false to prevent spurious errors.
# Type: boolean
# Default: true
#
# is_system_package (optional)
# Used internally to implement fuchsia_system_package(), do not use!
# If this is true, this is a fuchsia_system_package(), and it is allowed
# to be included in //build/input:system_image.
# Type: boolean
#
# is_driver_package (optional)
# Used internally to implement fuchsia_driver_package(), do not use!
# If defined, this is a fuchsia_driver_package(). The only behavior
# difference is a fuchsia_driver_package can be added as a dependency
# to the boot image, and its contents will appear in the boot image.
# This flag will be removed eventually as fuchsia_driver_packages
# are included in the build in the correct way.
# Type: boolean
#
# repository (optional)
# The repository host name part of the package URL. Defaults to "fuchsia.com".
# See https://fuchsia.dev/fuchsia-src/concepts/packages/package_url#repository
# for more details.
# Type: string
# Default: fuchsia.com
#
# subpackages (optional)
# A list of `fuchsia_package` targets. Each target is converted into the
# equivalent of a `renameable_targets` entry with its `package` property set
# to the target, and no `name` override. All included `package` entries will
# be added as `deps` of the generated meta file, and do not need to be
# listed as additional `deps` of the `fuchsia_package` target.
# Subpackage names must be unique (relative to the containing package), but
# both `renameable_subpackages` and `subpackages` may be included,
# additively.
# Type: list of targets
#
# renameable_subpackages (optional)
# A list of subpackages defined by scoped variables `package` and an
# optional `name`. If `name` is omitted, the subpackage target's package
# name is used by default. The generated package will include a
# `subpackages` meta file that declares dependencies on the listed packages,
# using the current package hash of each package. All included `package`
# entries will be added as `deps` of the generated meta file, and do not
# need to be listed as additional `deps` of the `fuchsia_package` target.
# Subpackage names must be unique (relative to the containing package), but
# both `renameable_subpackages` and `subpackages` may be included,
# additively.
# Type: list of scopes
#
# data_deps
# deps
# testonly
# visibility
template("fuchsia_package") {
if (current_toolchain == target_toolchain) {
main_target = target_name
package_name = target_name
if (defined(invoker.package_name)) {
package_name = invoker.package_name
}
repository = "fuchsia.com"
if (defined(invoker.repository)) {
repository = invoker.repository
}
verify_elf = !(defined(invoker.disable_elf_binaries_checks) &&
invoker.disable_elf_binaries_checks)
_validate_structured_config = true
if (defined(invoker.validate_structured_config)) {
_validate_structured_config = invoker.validate_structured_config
}
_labels = {
dist_manifest = "${target_name}.dist"
build_target = "${target_name}.pkg"
}
_files = {
dist_manifest = "${target_out_dir}/${target_name}.dist"
fini_manifest = "${target_out_dir}/${target_name}.manifest"
# LINT.IfChange
package_out_dir = "${target_out_dir}/$target_name"
package_manifest = "${package_out_dir}/package_manifest.json"
# LINT.ThenChange(//build/packages/exported_fuchsia_package_archive.gni)
blobs_json = "${package_out_dir}/blobs.json"
blobs_manifest = "${package_out_dir}/blobs.manifest"
}
# Generate the "meta/package" file
meta_package_target = "${target_name}.meta_package"
generate_meta_package(meta_package_target) {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
])
visibility = [ ":*" ]
package_name = package_name
}
# Generate the subpackages manifest
if (defined(invoker.subpackages) ||
defined(invoker.renameable_subpackages) ||
defined(invoker.test_pkg__subpackages) ||
defined(invoker.test_pkg__renameable_subpackages)) {
subpackages_list = []
if (defined(invoker.renameable_subpackages)) {
subpackages_list = invoker.renameable_subpackages
}
if (defined(invoker.test_pkg__renameable_subpackages)) {
subpackages_list = invoker.test_pkg__renameable_subpackages
}
if (defined(invoker.subpackages)) {
foreach(subpackage, invoker.subpackages) {
subpackages_list += [
{
package = subpackage
},
]
}
}
if (defined(invoker.test_pkg__subpackages)) {
foreach(subpackage, invoker.test_pkg__subpackages) {
subpackages_list += [
{
package = subpackage
},
]
}
}
# Generate the "meta/fuchsia.pkg/subpackages" file
meta_subpackages_target = "${target_name}.meta_subpackages"
generate_meta_subpackages(meta_subpackages_target) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
subpackages = subpackages_list
}
meta_subpackages_outputs =
get_target_outputs(":${meta_subpackages_target}")
meta_subpackages_file = meta_subpackages_outputs[0]
}
# Get a JSON manifest of all runtime objects.
# Gather metadata about runtime objects.
distribution_manifest_generated_file("${_labels.dist_manifest}") {
forward_variables_from(invoker,
[
"applicable_licenses",
"deps",
"testonly",
])
visibility = [ ":${_labels.build_target}" ]
# Include the meta/package file in the set of resources to include in the package
if (!defined(deps)) {
deps = []
}
deps += [ ":$meta_package_target" ]
outputs = [ _files.dist_manifest ]
}
metadata_target_name = "${target_name}.metadata"
define_package_metadata(metadata_target_name) {
package_name = package_name
snapshot_entry = "$package_name/0=" +
rebase_path("${_files.blobs_json}", root_build_dir)
blob_manifest = _files.blobs_manifest
package_output_manifest = _files.package_manifest
}
# Call the wrapper script that builds the package
python_action("${_labels.build_target}") {
mnemonic = "PACKAGE"
forward_variables_from(invoker,
[
"applicable_licenses",
"data_deps",
"testonly",
"visibility",
])
binary_label =
"//build/components:package_creation_wrapper($host_toolchain)"
# These are the base deps/inputs/outputs/args for all package creation calls
deps = [
":${_labels.dist_manifest}",
":${metadata_target_name}",
]
if (defined(_tools_package_tool.dep)) {
deps += [ _tools_package_tool.dep ]
}
# The invoker deps are added here (as well as to the dist manifest above)
# so that barriers specified in invoker.metadata will work correctly.
if (defined(invoker.deps)) {
deps += invoker.deps
}
inputs = [
# GN metadata on package entries
_files.dist_manifest,
# package tool binary
_tools_package_tool.input,
]
depfile = "${target_out_dir}/${target_name}.d"
outputs = [
_files.package_manifest,
"${_files.package_out_dir}/meta.far",
"${_files.package_out_dir}/blobs.json",
"${_files.package_out_dir}/blobs.manifest",
# Intermediate outputs written
_files.fini_manifest,
"${_files.package_out_dir}/meta.far.d",
]
args = [
"--distribution-manifest",
rebase_path(_files.dist_manifest, root_build_dir),
"--fini-manifest",
rebase_path(_files.fini_manifest, root_build_dir),
"--package-tool",
_tools_package_tool.rebased,
"--output-dir",
rebase_path(_files.package_out_dir, root_build_dir),
"--repository",
repository,
"--api-level",
"${current_build_target_api_level}",
"--depfile",
rebase_path(depfile, root_build_dir),
]
if (defined(meta_subpackages_target)) {
deps += [ ":${meta_subpackages_target}" ]
inputs += [ meta_subpackages_file ]
args += [
"--subpackages-manifest",
rebase_path(meta_subpackages_file, root_build_dir),
]
}
if (verify_elf) {
args += [
"--verify-elf-binaries",
"--toolchain-lib-dir=${rebased_clang_dir}/lib",
"--toolchain-lib-dir=${rebased_rustc_prefix}/lib",
]
}
if (_validate_structured_config) {
if (defined(_tools_configc.dep)) {
deps += [ _tools_configc.dep ]
}
inputs += [ _tools_configc.input ]
args += [
"--validate-structured-config",
_tools_configc.rebased,
]
}
metadata = {
if (defined(invoker.metadata)) {
forward_variables_from(invoker.metadata, "*")
}
# Only report this package's metadata (and not say, any subpackage's metadata)
package_barrier = [ ":${metadata_target_name}" ]
# Installing a Fuchsia package into another one should not also install
# its content to the root install location, which is why this metadata key
# is set to an empty list.
distribution_entries_barrier = []
component_id_index_barrier = []
test_component_manifest_barrier = []
if (!defined(test_components_barrier)) {
# don't produce entries for non-test packages.
test_components_barrier = []
}
test_component_manifest_program_barrier = []
expect_includes_barrier = []
}
# Ensure that even if the top-level package restricts its visibility,
# this action is visible to the main target group.
if (defined(visibility)) {
visibility += [ ":${main_target}" ]
} else {
not_needed([ "main_target" ])
}
}
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${_labels.build_target}" ]
if (defined(invoker.package_deps)) {
deps = invoker.package_deps
}
}
} else {
# Fuchsia 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 fuchsia_package expand to nothing.
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
}
}