blob: 487cdd59c8e48255a34075b9a1b740289b910118 [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/compiled_action.gni")
import("//build/images/manifest.gni")
declare_args() {
# The package key to use for signing Fuchsia packages made by the
# `package()` template (and the `system_image` packge). 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"
}
# 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.
#
# 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"
deps = []
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
pkg_manifest_outputs = get_target_outputs(invoker.manifest)
pkg_manifest_file = pkg_manifest_outputs[0]
pkg_out_dir = "$target_out_dir/$target_name"
deps += [
"//build/images:system_package_key_check",
invoker.manifest,
]
inputs = [
pkg_manifest_file,
system_package_key,
]
outputs = [
# update
"$pkg_out_dir/meta/contents",
# sign
"$pkg_out_dir/meta/pubkey",
"$pkg_out_dir/meta/signature",
# seal
"$pkg_out_dir/meta.far",
"$pkg_out_dir/meta.far.merkle",
]
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),
"build",
]
}
}
# 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)
# [string] The path to the 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/`.
#
# 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.
#
# 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)
# 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",
"data_deps",
"deprecated_system_image",
"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(data_deps)) {
data_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
}
if (!defined(resources)) {
resources = []
}
if (!defined(tests)) {
tests = []
}
}
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(!defined(pkg.binary),
"$pkg_desc deprecated_system_image incompatible with binary")
} 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
},
]
}
}
# 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, pkg.meta) {
pkg_manifest += [
{
dest = "meta/${meta.dest}"
source = rebase_path(meta.path)
},
]
}
foreach(binary, pkg.binaries) {
pkg_manifest += [
{
if (defined(binary.dest)) {
dest = binary.dest
} else {
dest = binary.name
}
dest = "bin/${dest}"
if (defined(binary.source)) {
source = binary.source
} else {
source = binary.name
}
source = rebase_path(source, "", root_out_dir)
},
]
}
foreach(test, pkg.tests) {
pkg_manifest += [
{
if (defined(test.dest)) {
dest = test.dest
} else {
dest = test.name
}
if (defined(test.disabled) && test.disabled) {
dest = "disabled/${dest}"
}
dest = "test/${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}" ]
}
# 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.deprecated_system_image = true
}
if (pkg.deprecated_system_image) {
# Dummy target to deposit an empty ids.txt file for
# //build/images:ids.txt to collect.
action("${pkg_target_name}.manifest") {
visibility = [
":${pkg_target_name}",
"//build/images:blob.manifest",
"//build/images:ids.txt",
]
script = "/bin/cp"
# Add sources and deps so that the package gets rebuilt whenever they
# change.
sources = manifest_sources
deps = pkg.deps
outputs = [
"$target_out_dir/${target_name}.ids.txt",
]
args = [
"-f",
"/dev/null",
] + rebase_path(outputs, root_build_dir)
}
} else {
# Synthesize the meta/package file.
pkg_meta_package = "${pkg_target_name}_meta_package.json"
action(pkg_meta_package) {
visibility = [ ":${pkg_target_name}.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),
]
}
# For a real package, generate the package manifest with all its
# dynamically linked libraries resolved.
generate_manifest("${pkg_target_name}.manifest") {
visibility = [
":${pkg_target_name}.meta",
"//build/images:blob.manifest",
"//build/images:ids.txt",
]
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" ]
}
# Next generate a signed, sealed package file.
pm_build_package("${pkg_target_name}.meta") {
visibility = [
":${pkg_target_name}.blob.rsp",
":${pkg_target_name}.pkgsvr_index.rsp",
":${pkg_target_name}.amber_index.rsp",
]
manifest = ":${pkg_target_name}.manifest"
}
# Clear it so we don't put anything into the system image.
manifest_args = []
}
generate_response_file(pkg_target_name) {
if (defined(pkg.visibility)) {
visibility = pkg.visibility + [
"//build/gn:packages",
"//build/images:system_image.manifest",
]
}
deps = pkg.deps
if (pkg.deprecated_system_image) {
deps += [
":${pkg_target_name}.manifest",
]
}
data_deps = pkg.data_deps
output_name = "${pkg_target_name}.system.rsp"
response_file_contents = manifest_args
}
generate_response_file("${pkg_target_name}.blob.rsp") {
visibility = [ "//build/images:blob.manifest" ]
if (pkg.deprecated_system_image) {
# A system_image package has no blobs of its own.
response_file_contents = []
} else {
# A real package needs blobs for all its contents and for its
# synthesized meta.far file.
deps = [
":${pkg_target_name}.meta",
]
response_file_contents = [
"--manifest=" + rebase_path("${pkg_target_name}.manifest",
root_build_dir,
target_out_dir),
"--entry-manifest=${pkg_label}",
"--entry=${pkg.package_name}/meta.far=" +
rebase_path("${pkg_target_name}.meta/meta.far",
root_build_dir,
target_out_dir),
]
}
}
foreach(index,
[
{
name = "pkgsvr_index"
file = "meta.far.merkle"
},
{
name = "amber_index"
file = "meta.far"
},
]) {
generate_response_file("${pkg_target_name}.${index.name}.rsp") {
if (pkg.deprecated_system_image) {
# A system_image package has no index entry.
response_file_contents = []
} else {
# A real package needs has a pkgsvr index entry mapping its package
# name and version to its meta.far file's merkleroot.
# The amber index is the same, but needs the meta.far filename
# in the build, rather than its merkleroot.
# TODO(mcgrathr): Make amber publish use pkgsvr index directly.
deps = [
":${pkg_target_name}.meta",
]
response_file_contents = [
"--entry-manifest=${pkg_label}",
"--entry=${pkg.package_name}/${pkg.package_version}=" +
rebase_path("${pkg_target_name}.meta/${index.file}",
root_build_dir,
target_out_dir),
]
}
}
}
} else {
group(target_name) {
forward_variables_from(invoker,
[
"deps",
"testonly",
])
}
# Suppress unused variable warnings.
not_needed(invoker, "*")
}
}