blob: 46063c460958d00b533b27db43acebf2c1053f7d [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/config/clang/clang.gni")
declare_args() {
# Build packages as FAR archives and store them in /system. This is required
# for --bootfs builds.
bootfs_packages = false
}
# 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
#
# fuchsia_package (bool, optional)
# If true, the package is constructed and signed ready for distribution and
# inclusion in /pkgfs. system_image supersedes fuchsia_package.
#
# TODO(PKG-46) Eventually all packages should be fuchsia_package..
#
# Defaults to false.
#
# system_image (bool, optional)
# If true, the package is stored in the system boot image rather than in a
# Fuchsia Archive.
#
# If this package uses the |drivers| parameter, the system_image must be set
# to true because we are not yet sophisticated enough to load drivers out of
# archives.
#
# Defaults to false.
#
# deprecated_system_image (bool, optional)
# New alias for system_image to reflect its deprecated status, per PKG-46.
#
# boot_firmware_image (bool, optional)
# If true, the package is a boot firmware package instead of a regular
# package. A boot firmware package may contain only resources which are
# installed into /boot/lib/fw.
#
# meta (optional)
# List of scopes defining 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 |system_image| to be false.
#
# Entries in a scope in the meta list:
#
# path (path, required)
# 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() rule. If the resource is
# generated, this will typically be specified relative to
# $target_gen_dir
#
# dest (path, required)
# Location the resource will be placed within "meta/"
#
# binary (optional)
# The path to the the primary binary for the package, relative to
# $root_build_dir. The binary will be placed in the assembled package at
# "bin/app" and will be executed by default when running the package.
#
# Requires |system_image| to be false.
#
# binaries (optional)
# List of scopes defining 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 (string, required)
# Name of the binary
#
# source (path, optional)
# Location of the binary in the build directory if it is not at
# $root_build_dir/$name
#
# dest (path, optional)
# Location the binary will be placed within "bin/"
#
# tests (optional)
# List of scopes defining 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 (string, required)
# Name of the test
#
# dest (path, optional)
# Location the binary will be placed within "test/"
#
# disabled (bool, optional)
# 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.
#
# drivers (optional)
# List of scopes defining 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 |system_image| to be true.
#
# Entries in a scope in the drivers list:
#
# name (string, required)
# Name of the driver
#
# libraries (optional)
# List of scopes defining the (shared) libraries in the package. A library
# is placed in the "lib/" directory of the assembled package.
#
# Entries in a scope in the libraries list:
#
# name (string, required)
# Name of the library
#
# source (path, optional)
# Location of the binary in the build directory if it is not at
# $root_build_dir/$name
#
# dest (path, optional)
# Location the binary will be placed within "lib/"
#
# resources (optional)
# List of scopes defining 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 (path, required)
# 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() rule. If the resource is
# generated, this will typically be specified relative to
# $target_gen_dir
#
# dest (path, required)
# Location the resource will be placed within "data/", unless the package
# is a boot firmware package in which case this is the location the resource
# will be placed within "/boot/lib/fw".
#
# extra (optional)
# A list of potentially build-time generated files containing extra entries
# in the package, formatted as a manifest.
template("package") {
if (current_toolchain == target_toolchain) {
pkg = {
# By default we want to build Fuchsia packages, but they are not
# supported under bootfs, so in that case we build the old archive format
# packages.
fuchsia_package = !bootfs_packages
system_image = false
deprecated_system_image = false
boot_firmware_image = false
binaries = []
drivers = []
libraries = []
meta = []
package_name = target_name
package_version = "0" # placeholder
resources = []
tests = []
forward_variables_from(invoker, "*")
}
pkg_out_dir = "$root_build_dir/package/${pkg.package_name}"
if (pkg.deprecated_system_image) {
pkg.system_image = true
}
shared_toolchain_out_dir =
get_label_info("//lib($shlib_toolchain)", "root_out_dir")
not_needed([ "shared_toolchain_out_dir" ])
assert(pkg.drivers == [] || pkg.system_image)
if (pkg.boot_firmware_image) {
assert(!pkg.fuchsia_package,
"boot firmware packages cannot be true fuchsia packages")
assert(!pkg.system_image,
"boot firmware packages cannot be system images")
assert(pkg.binaries == [],
"boot firmware packages cannot contain binaries")
assert(pkg.drivers == [], "boot firmware packages cannot contain drivers")
assert(pkg.libraries == [],
"boot firmware packages cannot contain libraries")
assert(pkg.meta == [], "boot firmware packages cannot contain metadata")
assert(pkg.tests == [], "boot firmware packages cannot contain tests")
assert(!defined(pkg.extra) || pkg.extra == [],
"boot firmware packages cannot contain extra build-time entries")
}
package_manifest = "$pkg_out_dir/package_manifest"
archive_manifest = "$pkg_out_dir/archive_manifest"
system_manifest = "$pkg_out_dir/system_manifest"
boot_manifest = "$pkg_out_dir/boot_manifest"
manifest_target_name = "${target_name}__manifest"
# If there are extra build-time entries to put into the package, then
# collect GN-time entries in partial_package_manifest, and concatenate them
# with the build-time entries into package_manifest.
if (defined(pkg.extra)) {
extra_target_name = "${target_name}__extra"
if (pkg.fuchsia_package) {
partial_archive_manifest = archive_manifest
partial_package_manifest = "$pkg_out_dir/partial_package_manifest"
partial = partial_package_manifest
output = package_manifest
} else {
partial_archive_manifest = "$pkg_out_dir/partial_archive_manifest"
partial_package_manifest = package_manifest
partial = partial_archive_manifest
output = archive_manifest
}
action(extra_target_name) {
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":${manifest_target_name}" ] + invoker.deps
inputs = [ partial ] + pkg.extra
outputs = [
output,
]
script = "//build/gn/combine_manifests.py"
args = [
rebase_path(output, root_build_dir),
rebase_path(partial, root_build_dir),
]
foreach(extra, pkg.extra) {
args += [ rebase_path(extra, root_build_dir) ]
}
}
} else {
partial_package_manifest = package_manifest
partial_archive_manifest = archive_manifest
}
contents = []
extra_inputs = []
foreach(entry, pkg.meta) {
assert(!pkg.system_image, "Metadata cannot be used in system packages.")
source = entry.path
dest = "meta/${entry.dest}"
contents += [ "$dest=$source" ]
extra_inputs += [ source ]
}
if (defined(pkg.binary)) {
assert(
!pkg.system_image,
"Binary requires the package to be archived. Consider using 'binaries' for system packages.")
source = rebase_path("$root_build_dir/${pkg.binary}")
contents += [ "bin/app=$source" ]
extra_inputs += [ source ]
}
foreach(binary, pkg.binaries) {
assert(
(pkg.system_image || pkg.fuchsia_package),
"Binaries cannot be used in archived packages. Consider using 'binary'.")
# Binary paths are relative to the root of the build dir.
if (defined(binary.source)) {
source = rebase_path("$root_build_dir/${binary.source}")
} else {
source = rebase_path("$root_build_dir/${binary.name}")
}
dirname = "bin"
basename = binary.name
if (defined(binary.dest)) {
basename = binary.dest
}
contents += [ "$dirname/$basename=$source" ]
extra_inputs += [ source ]
}
foreach(test, pkg.tests) {
source = rebase_path("$root_build_dir/${test.name}")
basename = test.name
if (defined(test.dest)) {
basename = test.dest
}
if (defined(test.disabled) && test.disabled) {
dest = "test/disabled/" + basename
} else {
dest = "test/" + basename
}
contents += [ "$dest=$source" ]
extra_inputs += [ source ]
}
foreach(driver, pkg.drivers) {
# Driver paths are relative to the root of the build dir.
source = rebase_path("$root_build_dir/${driver.name}")
dest = "driver/" + driver.name
contents += [ "$dest=$source" ]
extra_inputs += [ source ]
}
foreach(library, pkg.libraries) {
# Library paths are relative to the root of the build dir.
if (defined(library.source)) {
library_path = rebase_path(library.source)
} else {
library_path = "$shared_toolchain_out_dir/${library.name}"
}
source = rebase_path(library_path)
if (defined(library.dest)) {
dest = "lib/" + library.dest
} else {
dest = "lib/" + library.name
}
contents += [ "$dest=$source" ]
extra_inputs += [ source ]
}
foreach(resource, pkg.resources) {
# Resource path are absolute.
source = resource.path
if (pkg.boot_firmware_image) {
dest = "lib/fw/" + resource.dest
} else {
dest = "data/" + resource.dest
}
contents += [ "$dest=$source" ]
extra_inputs += [ source ]
}
if (pkg.system_image || pkg.boot_firmware_image) {
action(manifest_target_name) {
script = "//build/gn/write_manifest.py"
outputs = [
partial_package_manifest,
partial_archive_manifest,
system_manifest,
boot_manifest,
]
args = [
"--manifest",
rebase_path(partial_package_manifest, root_build_dir),
"--manifest",
rebase_path(partial_archive_manifest, root_build_dir),
]
if (pkg.boot_firmware_image) {
args += [
"--manifest",
rebase_path(system_manifest, root_build_dir),
"--manifest",
rebase_path(boot_manifest, root_build_dir),
]
} else {
args += [
"--manifest",
rebase_path(boot_manifest, root_build_dir),
"--manifest",
rebase_path(system_manifest, root_build_dir),
]
}
args += contents
inputs = extra_inputs
forward_variables_from(invoker,
[
"deps",
"testonly",
])
}
group(target_name) {
forward_variables_from(invoker, [ "testonly" ])
public_deps = [
":${manifest_target_name}",
]
if (defined(pkg.extra)) {
public_deps += [ ":${extra_target_name}" ]
}
}
} else if (pkg.fuchsia_package) {
# If the caller doesn't supply a package, we'll generate one
need_package_json = true
foreach(meta, pkg.meta) {
if (meta == "package") {
need_package_json = false
}
}
if (need_package_json) {
package_json_file = "$pkg_out_dir/meta/package"
package_json_target_name = "${target_name}_package_json"
action(package_json_target_name) {
script = "//build/gn/write_package_json.py"
outputs = [
package_json_file,
]
args = [
"--name",
pkg.package_name,
"--version",
pkg.package_version,
rebase_path(package_json_file, root_build_dir),
]
}
full_pkg_json_file = rebase_path(package_json_file)
contents += [ "meta/package=${full_pkg_json_file}" ]
extra_inputs += [ package_json_file ]
}
action(manifest_target_name) {
script = "//build/gn/write_manifest.py"
outputs = [
partial_package_manifest,
partial_archive_manifest,
system_manifest,
boot_manifest,
]
args = [
"--manifest",
rebase_path(partial_archive_manifest, root_build_dir),
"--manifest",
rebase_path(system_manifest, root_build_dir),
"--manifest",
rebase_path(boot_manifest, root_build_dir),
"--manifest",
rebase_path(partial_package_manifest, root_build_dir),
] + contents
inputs = extra_inputs
forward_variables_from(invoker,
[
"deps",
"testonly",
])
if (!defined(deps)) {
deps = []
}
if (need_package_json) {
deps += [ ":${package_json_target_name}" ]
}
}
group(target_name) {
forward_variables_from(invoker, [ "testonly" ])
public_deps = [
":${manifest_target_name}",
]
if (defined(pkg.extra)) {
public_deps += [ ":${extra_target_name}" ]
}
}
} else {
output = "$pkg_out_dir/${pkg.package_name}.far"
action(manifest_target_name) {
script = "//build/gn/write_manifest.py"
outputs = [
partial_package_manifest,
partial_archive_manifest,
system_manifest,
boot_manifest,
]
args = [
"--manifest",
rebase_path(partial_package_manifest, root_build_dir),
"--manifest",
rebase_path(system_manifest, root_build_dir),
"pkgs/${pkg.package_name}=" + rebase_path(output),
"--manifest",
rebase_path(boot_manifest, root_build_dir),
"--manifest",
rebase_path(partial_archive_manifest, root_build_dir),
] + contents
inputs = extra_inputs
forward_variables_from(invoker,
[
"deps",
"testonly",
])
}
# This action assembles an image containing all binaries, tests, drivers,
# libraries, and resources defined in the package.
action(target_name) {
forward_variables_from(invoker, [ "testonly" ])
public_deps = [
":${manifest_target_name}",
]
if (defined(pkg.extra)) {
public_deps += [ ":${extra_target_name}" ]
}
archiver_label = "//garnet/bin/far:bin($host_toolchain)"
deps = [
archiver_label,
]
archiver_out_dir = get_label_info(archiver_label, "root_out_dir")
far_path = "$archiver_out_dir/far"
script = "//build/gn_run_binary.sh"
inputs = [
far_path,
archive_manifest,
]
outputs = [
output,
]
args = [
clang_prefix,
rebase_path(far_path, root_build_dir),
"create",
"--archive=" + rebase_path(output),
"--manifest=" + rebase_path(archive_manifest),
]
}
}
} else {
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"deps",
])
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
}
}