[gn] Introduce initial component() rule in GN
CF-232 #comment
TEST=manual
Change-Id: I4f11c8bd88ffdd2becd3f83c93a975247d6c6864
diff --git a/cat.py b/cat.py
new file mode 100755
index 0000000..2e0c1a8
--- /dev/null
+++ b/cat.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# Copyright 2018 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 argparse
+import sys
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='Concat files.')
+ parser.add_argument('-i', action='append', dest='inputs', default=[],
+ help='Input files', required=True)
+ parser.add_argument('-o', dest='output', help='Output file', required=True)
+ args = parser.parse_args()
+ return args
+
+def main():
+ args = parse_args()
+ with open(args.output, 'w') as outfile:
+ for fname in args.inputs:
+ with open(fname) as infile:
+ outfile.write(infile.read())
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/cmx/cmx.gni b/cmx/cmx.gni
index 59aa85f..507b47f 100644
--- a/cmx/cmx.gni
+++ b/cmx/cmx.gni
@@ -4,7 +4,7 @@
#
# Parameters
# cmx (required)
-# This is the .cmx file that wants to be validated.
+# This is the .cmx file that wants to be validated.
#
# deps (optional)
template("validate_cmx") {
@@ -15,6 +15,7 @@
forward_variables_from(invoker,
[
"deps",
+ "public_deps",
"testonly",
"visibility",
])
diff --git a/package.gni b/package.gni
index cbe4e29..264e240 100644
--- a/package.gni
+++ b/package.gni
@@ -5,6 +5,7 @@
import("//build/assert_cmx.gni")
import("//build/cmx/cmx.gni")
import("//build/compiled_action.gni")
+import("//build/package/component.gni")
import("//build/gn/packages.gni")
import("//build/images/manifest.gni")
import("//build/json/validate_json.gni")
@@ -158,6 +159,12 @@
# [boolean] (default: false) When true, the binary is runnable from the shell.
# Shell binaries are run in the shell namespace and are not run as components.
#
+# components (optional)
+# [list of fuchsia_component targets] Defines all the components this
+# package should include in assembled package.
+#
+# Requires `deprecated_system_image` to be `false`.
+#
# 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/`
@@ -270,6 +277,7 @@
[
"binaries",
"binary",
+ "components",
"data_deps",
"deprecated_system_image",
"deprecated_no_cmx",
@@ -294,6 +302,9 @@
if (!defined(deps)) {
deps = []
}
+ if (!defined(components)) {
+ components = []
+ }
if (!defined(data_deps)) {
data_deps = []
}
@@ -321,8 +332,13 @@
if (defined(deprecated_no_cmx)) {
deps += [ "${deprecated_no_cmx}:no_cmx_whitelist" ]
}
- assert_cmx(package_name) {
- forward_variables_from(invoker, "*")
+ if (components == []) {
+ assert_cmx(package_name) {
+ forward_variables_from(invoker, "*")
+ }
+ }
+ foreach(component, components) {
+ deps += [ component ]
}
if (!defined(resources)) {
resources = []
@@ -338,6 +354,8 @@
if (pkg.deprecated_system_image) {
assert(pkg.meta == [],
"$pkg_desc deprecated_system_image incompatible with meta")
+ assert(pkg.components == [],
+ "$pkg_desc deprecated_system_image incompatible with components")
assert(!defined(pkg.binary),
"$pkg_desc deprecated_system_image incompatible with binary")
} else {
@@ -358,10 +376,11 @@
# Validate .cmx files
foreach(meta, pkg.meta) {
if (get_path_info(meta.dest, "extension") == "cmx") {
- validate = "validate_" + pkg_target_name +
- "_" + get_path_info(meta.dest, "file")
+ validate = "validate_" + pkg_target_name + "_" +
+ get_path_info(meta.dest, "file")
validate_cmx(validate) {
cmx = meta.path
+
# the cmx file may be generated by one of this package's dependencies,
# but we don't know which one, so depend on all package deps here.
deps = pkg.deps
@@ -514,7 +533,7 @@
# 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 == []) {
+ if (manifest_sources == [] && pkg.components == []) {
pkg.deprecated_system_image = true
}
@@ -556,8 +575,6 @@
]
}
- # For a real package, generate the package manifest with all its
- # dynamically linked libraries resolved.
generate_manifest("${pkg_target_name}.manifest") {
sources = manifest_sources + get_target_outputs(":$pkg_meta_package")
args = manifest_args +
@@ -567,9 +584,47 @@
public_deps = pkg.public_deps
}
+ final_manifest = "${pkg_target_name}.final.manifest"
+
+ # TODO(CF-224): clean this up when gn has metadata support for templates
+ action(final_manifest) {
+ script = "//build/cat.py"
+ output_name = "${target_out_dir}/${final_manifest}"
+ outputs = [
+ output_name,
+ ]
+ visibility = [ "*" ]
+
+ pmx = "$target_out_dir/${pkg_target_name}.manifest"
+ args = [
+ "-o",
+ rebase_path(output_name),
+ "-i",
+ rebase_path("$target_out_dir/${pkg_target_name}.manifest"),
+ ]
+ inputs = [
+ pmx,
+ ]
+ deps = [
+ ":${pkg_target_name}.manifest",
+ ]
+
+ foreach(component, pkg.components) {
+ deps += [ component ]
+ component_name = get_label_info(component, "name")
+ dir = get_label_info(component, "target_out_dir")
+ manifest_file = "${dir}/${component_name}.manifest"
+ inputs += [ manifest_file ]
+ args += [
+ "-i",
+ rebase_path(manifest_file),
+ ]
+ }
+ }
+
# Next generate a signed, sealed package file.
pm_build_package("${pkg_target_name}.meta") {
- manifest = ":${pkg_target_name}.manifest"
+ manifest = ":$final_manifest"
}
# Clear it so we don't put anything into the system image.
@@ -615,11 +670,10 @@
response_file_contents = []
foreach(binary, shell_binaries) {
-
response_file_contents += [
# TODO(CF-105): fuchsia-pkg URIs should always have a variant (add /${pkg.package_version}).
"--uri",
- "fuchsia-pkg://fuchsia.com/${pkg.package_name}#${binary}"
+ "fuchsia-pkg://fuchsia.com/${pkg.package_name}#${binary}",
]
}
}
diff --git a/package/component.gni b/package/component.gni
new file mode 100644
index 0000000..f2f2e09
--- /dev/null
+++ b/package/component.gni
@@ -0,0 +1,181 @@
+# Copyright 2018 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/cmx/cmx.gni")
+import("//build/images/manifest.gni")
+import("//build/json/validate_json.gni")
+
+# Defines a fuchsia component.
+#
+# This template is used to define a unit of component.
+# A component always has a manifest defining that component.
+#
+# Parameters
+#
+# name (optional)
+# [path] Defines name of the component. Cmx destination depends on this
+# name, so if name = foo, cmx would be copied to meta/foo.cmx in assembled
+# package.
+#
+# If not provided, defaults to $target_name
+#
+# manifest (required)
+# [path] Defines the manifest source path for this component.
+#
+# binary (required)
+# [path] The path to the the primary binary for the component.
+#
+# 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 lib will be placed in the package.
+#
+# resources (optional)
+# [list of scopes] Defines the resources for this component. 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/`.
+#
+# deps (optional)
+# public_deps (optional)
+# data_deps (optional)
+# visibility (optional)
+# Usual GN meanings.
+#
+template("fuchsia_component") {
+ component_label = get_label_info(":$target_name", "label_with_toolchain")
+ assert(
+ current_toolchain == target_toolchain,
+ "Component $component_label rule only supported for $target_toolchain.")
+ component = {
+ forward_variables_from(invoker,
+ [
+ "name",
+ "binary",
+ "data_deps",
+ "deps",
+ "public_deps",
+ "loadable_modules",
+ "resources",
+ "visibility",
+ "manifest",
+ ])
+
+ # remove this check once we support dart and flutter
+ assert(defined(binary), "Component $component_label should define binary")
+ if (!defined(deps)) {
+ deps = []
+ }
+ if (!defined(name)) {
+ name = target_name
+ }
+ if (!defined(data_deps)) {
+ data_deps = []
+ }
+ if (!defined(public_deps)) {
+ public_deps = []
+ }
+ if (!defined(loadable_modules)) {
+ loadable_modules = []
+ }
+ assert(defined(manifest),
+ "Component $component_label should define manifest file")
+ if (!defined(resources)) {
+ resources = []
+ }
+ }
+
+ validate = "validate_" + target_name + "_${component.name}.cmx"
+
+ validate_cmx(validate) {
+ cmx = component.manifest
+
+ # the cmx file may be generated by one of this component's dependencies,
+ # but we don't know which one, so depend on all package deps here.
+ deps = component.deps
+ public_deps = component.public_deps
+ }
+ component.deps += [ ":$validate" ]
+
+ # Collect the component's primary manifest.
+ component_manifest = [
+ {
+ dest = "meta/${component.name}.cmx"
+ source = rebase_path(component.manifest)
+ },
+ ]
+
+ component_manifest += [
+ {
+ dest = "bin/" + get_path_info(component.binary, "file")
+ source = rebase_path(component.binary, "", root_out_dir)
+ },
+ ]
+ foreach(module, component.loadable_modules) {
+ component_manifest += [
+ {
+ if (defined(module.dest)) {
+ dest = module.dest
+ } else {
+ dest = "lib"
+ }
+ dest += "/${module.name}"
+ source = rebase_path(module.name, "", root_out_dir)
+ },
+ ]
+ }
+ foreach(resource, component.resources) {
+ component_manifest += [
+ {
+ dest = "data/${resource.dest}"
+ source = rebase_path(resource.path)
+ },
+ ]
+ }
+
+ # Collect all the arguments describing input manifest files
+ # and all the entries we've just synthesized in `component_manifest`.
+ manifest_sources = []
+ manifest_args = []
+ foreach(entry, component_manifest) {
+ manifest_sources += [ entry.source ]
+ manifest_args += [ "--entry=${entry.dest}=${entry.source}" ]
+ }
+
+ # Generate component build manifest with all its dynamically linked libraries
+ # resolved.
+ generate_manifest("${target_name}.manifest") {
+ sources = manifest_sources
+ args = manifest_args
+ deps = component.deps
+ public_deps = component.public_deps
+ }
+
+ group(target_name) {
+ public_deps = [
+ ":${target_name}.manifest",
+ ]
+ }
+}