[sdk][bazel] Give components an official presence in the build hierarchy.
A package now has access to the list of components it contains.
This is a preamble to being able to bazel-run individual components out of a given package.
Test: added tests to verify package contents, suite passes.
Bug: DX-750
Change-Id: Ia437a71e5ab102b6421ead574fafae48db3d8ad7
diff --git a/sdk/bazel/base/cc/build_defs/packageable_cc_binary.bzl b/sdk/bazel/base/cc/build_defs/cc_binary_component.bzl
similarity index 62%
rename from sdk/bazel/base/cc/build_defs/packageable_cc_binary.bzl
rename to sdk/bazel/base/cc/build_defs/cc_binary_component.bzl
index b591f04..156f665 100644
--- a/sdk/bazel/base/cc/build_defs/packageable_cc_binary.bzl
+++ b/sdk/bazel/base/cc/build_defs/cc_binary_component.bzl
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-load(":package_info.bzl", "get_aggregate_info", "PackageGeneratedInfo", "PackageLocalInfo")
+load(":package_info.bzl", "get_aggregate_info", "PackageGeneratedInfo", "PackageComponentInfo")
"""
Makes a cc_binary ready for inclusion in a fuchsia_package.
@@ -42,37 +42,47 @@
],
)
-def _packageable_cc_binary_impl(context):
- target_files = context.attr.target[DefaultInfo].files.to_list()
- if len(target_files) != 1:
- fail("Packaged binary should produce a single output", attr="target")
- output = target_files[0]
- if output.extension != "":
- fail("Expected executable, got: " + output.basename, attr="target")
+def _cc_binary_component_impl(context):
+ if len(context.attr.deps) != 1:
+ fail("'deps' attribute must have exactly one element.", "deps")
return [
- # TODO(pylaligand): remove this extra mapping once it's obsolete.
- PackageLocalInfo(mappings = [("bin/app", output)]),
+ PackageComponentInfo(
+ name = context.attr.component_name,
+ manifest = context.file.manifest,
+ ),
]
-_packageable_cc_binary = rule(
- implementation = _packageable_cc_binary_impl,
+_cc_binary_component = rule(
+ implementation = _cc_binary_component_impl,
attrs = {
- "target": attr.label(
- doc = "The cc_binary to package",
+ "deps": attr.label_list(
+ doc = "The cc_binary for the component",
+ mandatory = True,
+ allow_empty = False,
allow_files = False,
- aspects = [
- _cc_contents_aspect,
- ],
+ aspects = [_cc_contents_aspect],
),
+ "component_name": attr.string(
+ doc = "The name of the component",
+ mandatory = True,
+ ),
+ "manifest": attr.label(
+ doc = "The component's manifest file (.cmx)",
+ mandatory = True,
+ allow_single_file = True,
+ )
},
+ provides = [PackageComponentInfo],
)
-def packageable_cc_binary(name, target, **kwargs):
+def cc_binary_component(name, deps, component_name, manifest, **kwargs):
packaged_name = name + "_packaged"
- _packageable_cc_binary(
+ _cc_binary_component(
name = packaged_name,
- target = target,
+ deps = deps,
+ component_name = component_name,
+ manifest = manifest,
**kwargs
)
diff --git a/sdk/bazel/base/common/build_defs/package.bzl b/sdk/bazel/base/common/build_defs/package.bzl
index 9a5b65f..3358d4b 100644
--- a/sdk/bazel/base/common/build_defs/package.bzl
+++ b/sdk/bazel/base/common/build_defs/package.bzl
@@ -2,7 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-load(":package_info.bzl", "PackageAggregateInfo", "PackageGeneratedInfo", "PackageLocalInfo", "get_aggregate_info")
+load(":package_info.bzl", "PackageAggregateInfo", "PackageComponentInfo",
+ "PackageGeneratedInfo", "PackageInfo", "PackageLocalInfo",
+ "get_aggregate_info")
"""
Defines a Fuchsia package
@@ -19,38 +21,36 @@
"""
# The attributes along which the aspect propagates.
-# The value denotes whether the attribute represents a list of target or a
-# single target.
-_ASPECT_ATTRIBUTES = {
- "data": True,
- "target": False,
- "deps": True,
- "srcs": True,
-}
+_ASPECT_ATTRIBUTES = [
+ "data",
+ "deps",
+ "srcs",
+]
def _info_impl(target, context):
+ components = []
mappings = []
+ if PackageComponentInfo in target:
+ info = target[PackageComponentInfo]
+ components += [(info.name, info.manifest)]
if PackageLocalInfo in target:
- mappings = target[PackageLocalInfo].mappings
- elif PackageGeneratedInfo in target:
- mappings = target[PackageGeneratedInfo].mappings
+ mappings += target[PackageLocalInfo].mappings
+ if PackageGeneratedInfo in target:
+ mappings += target[PackageGeneratedInfo].mappings
deps = []
- for attribute, is_list in _ASPECT_ATTRIBUTES.items():
+ for attribute in _ASPECT_ATTRIBUTES:
if hasattr(context.rule.attr, attribute):
value = getattr(context.rule.attr, attribute)
- if is_list:
- deps += value
- else:
- deps.append(value)
+ deps += value
return [
- get_aggregate_info(mappings, deps),
+ get_aggregate_info(components, mappings, deps),
]
# An aspect which turns PackageLocalInfo providers into a PackageAggregateInfo
# provider to identify all elements which need to be included in the package.
_info_aspect = aspect(
implementation = _info_impl,
- attr_aspects = _ASPECT_ATTRIBUTES.keys(),
+ attr_aspects = _ASPECT_ATTRIBUTES,
provides = [
PackageAggregateInfo,
],
@@ -63,7 +63,7 @@
def _fuchsia_package_impl(context):
# List all the files that need to be included in the package.
- info = get_aggregate_info([], context.attr.deps)
+ info = get_aggregate_info([], [], context.attr.deps)
manifest_file_contents = ""
package_contents = []
@@ -72,16 +72,24 @@
manifest_file = context.actions.declare_file(base + "package_manifest")
content = "#!/bin/bash\n"
- for key, file in info.contents.to_list():
+ for dest, source in info.mappings.to_list():
# Only add file to the manifest if not empty.
- content += "if [[ -s %s ]]; then\n" % file.path
- content += " echo '%s=%s' >> %s\n" % (key, file.path, manifest_file.path)
+ content += "if [[ -s %s ]]; then\n" % source.path
+ content += " echo '%s=%s' >> %s\n" % (dest, source.path,
+ manifest_file.path)
content += "fi\n"
- package_contents.append(file)
+ package_contents.append(source)
+
+ # Add cmx file for each component.
+ for name, cmx in info.components.to_list():
+ content += "echo 'meta/%s.cmx=%s' >> %s\n" % (name, cmx.path,
+ manifest_file.path)
+ package_contents.append(cmx)
# Add the meta/package file to the manifest.
meta_package = context.actions.declare_file(base + "meta/package")
- content += "echo 'meta/package=%s' >> %s\n" % (meta_package.path, manifest_file.path)
+ content += "echo 'meta/package=%s' >> %s\n" % (meta_package.path,
+ manifest_file.path)
# Write the manifest file.
manifest_script = context.actions.declare_file(base + "package_manifest.sh")
@@ -188,7 +196,14 @@
outs = [package_archive]
return [
- DefaultInfo(files = depset(outs), runfiles = context.runfiles(files = outs)),
+ DefaultInfo(
+ files = depset(outs),
+ runfiles = context.runfiles(files = outs),
+ ),
+ PackageInfo(
+ name = context.attr.name,
+ archive = package_archive,
+ ),
]
fuchsia_package = rule(
@@ -208,4 +223,5 @@
cfg = "host",
),
},
+ provides = [PackageInfo],
)
diff --git a/sdk/bazel/base/common/build_defs/package_info.bzl b/sdk/bazel/base/common/build_defs/package_info.bzl
index 1c34d05..2a62bfb 100644
--- a/sdk/bazel/base/common/build_defs/package_info.bzl
+++ b/sdk/bazel/base/common/build_defs/package_info.bzl
@@ -6,6 +6,15 @@
Some utilities to declare and aggregate package contents.
"""
+# Identifies a component added to a package.
+PackageComponentInfo = provider(
+ fields = {
+ "name": "name of the component",
+ "manifest": "path to the component manifest file",
+ },
+)
+
+# Represents a set of files to be added to a package.
PackageLocalInfo = provider(
fields = {
"mappings": "list of (package dest, source) pairs",
@@ -21,17 +30,31 @@
},
)
+# Aggregates the information provided by the above providers.
PackageAggregateInfo = provider(
fields = {
- "contents": "depset of (package dest, source) pairs",
+ "components": "depset of (name, manifest) pairs",
+ "mappings": "depset of (package dest, source) pairs",
},
)
-def get_aggregate_info(mappings, deps):
- transitive_info = []
+def get_aggregate_info(components, mappings, deps):
+ transitive_components = []
+ transitive_mappings = []
for dep in deps:
if PackageAggregateInfo not in dep:
continue
- transitive_info.append(dep[PackageAggregateInfo].contents)
- return PackageAggregateInfo(contents = depset(mappings,
- transitive = transitive_info))
+ transitive_components.append(dep[PackageAggregateInfo].components)
+ transitive_mappings.append(dep[PackageAggregateInfo].mappings)
+ return PackageAggregateInfo(
+ components = depset(components, transitive = transitive_components),
+ mappings = depset(mappings, transitive = transitive_mappings),
+ )
+
+# Contains information about a built Fuchsia package.
+PackageInfo = provider(
+ fields = {
+ "name": "name of the package",
+ "archive": "archive file",
+ },
+)
diff --git a/sdk/bazel/base/dart/build_defs/dart_app.bzl b/sdk/bazel/base/dart/build_defs/dart_app.bzl
index f59848f..9c21b06 100644
--- a/sdk/bazel/base/dart/build_defs/dart_app.bzl
+++ b/sdk/bazel/base/dart/build_defs/dart_app.bzl
@@ -3,7 +3,7 @@
# found in the LICENSE file.
load(":dart.bzl", "COMMON_COMPILE_KERNEL_ACTION_ATTRS", "compile_kernel_action")
-load(":package_info.bzl", "PackageLocalInfo")
+load(":package_info.bzl", "PackageComponentInfo", "PackageLocalInfo")
# A Fuchsia Dart application
#
@@ -34,11 +34,14 @@
main_dilp_file = context.outputs.main_dilp,
dilp_list_file = context.outputs.dilp_list,
)
- mappings["meta/%s.cmx" % component_name] = context.files.component_manifest[0]
outs = [kernel_snapshot_file, manifest_file]
return [
DefaultInfo(files = depset(outs), runfiles = context.runfiles(files = outs)),
PackageLocalInfo(mappings = mappings.items()),
+ PackageComponentInfo(
+ name = component_name,
+ manifest = context.files.component_manifest[0],
+ ),
]
dart_app = rule(
diff --git a/sdk/bazel/base/dart/build_defs/flutter_app.bzl b/sdk/bazel/base/dart/build_defs/flutter_app.bzl
index 148a1e5..9631f88 100644
--- a/sdk/bazel/base/dart/build_defs/flutter_app.bzl
+++ b/sdk/bazel/base/dart/build_defs/flutter_app.bzl
@@ -3,7 +3,7 @@
# found in the LICENSE file.
load(":dart.bzl", "COMMON_COMPILE_KERNEL_ACTION_ATTRS", "compile_kernel_action")
-load(":package_info.bzl", "PackageLocalInfo")
+load(":package_info.bzl", "PackageComponentInfo", "PackageLocalInfo")
# A Fuchsia Flutter application
#
@@ -34,7 +34,6 @@
main_dilp_file = context.outputs.main_dilp,
dilp_list_file = context.outputs.dilp_list,
)
- mappings["meta/%s.cmx" % component_name] = context.files.component_manifest[0]
# Package the assets.
data_root = "data/%s/" % component_name
@@ -58,6 +57,10 @@
return [
DefaultInfo(files = depset(outs), runfiles = context.runfiles(files = outs)),
PackageLocalInfo(mappings = mappings.items()),
+ PackageComponentInfo(
+ name = component_name,
+ manifest = context.files.component_manifest[0],
+ ),
]
flutter_app = rule(
diff --git a/sdk/bazel/tests/cc/cc/BUILD b/sdk/bazel/tests/cc/cc/BUILD
index 62df7a3..35c8341 100644
--- a/sdk/bazel/tests/cc/cc/BUILD
+++ b/sdk/bazel/tests/cc/cc/BUILD
@@ -3,7 +3,9 @@
# found in the LICENSE file.
load("@fuchsia_sdk//build_defs:package.bzl", "fuchsia_package")
-load("@fuchsia_sdk//build_defs:packageable_cc_binary.bzl", "packageable_cc_binary")
+load("@fuchsia_sdk//build_defs:cc_binary_component.bzl", "cc_binary_component")
+
+load("//build_defs:verify_package.bzl", "verify_package")
# Vanilla C++ program.
cc_binary(
@@ -49,9 +51,11 @@
)
# Prepare the binary for inclusion in a package.
-packageable_cc_binary(
+cc_binary_component(
name = "packageable",
- target = ":pkg_dep",
+ deps = [":pkg_dep"],
+ component_name = "packageable",
+ manifest = "manifest.cmx",
)
# C++ program in a Fuchsia package.
@@ -62,6 +66,19 @@
],
)
+# Verify that the package contains all the expected files.
+verify_package(
+ name = "package_verify",
+ package = ":package",
+ files = [
+ "bin/pkg_dep",
+ "lib/ld.so.1",
+ "lib/libshared.so",
+ "lib/libsvc.so",
+ "meta/packageable.cmx",
+ ],
+)
+
# Test the testonly attribute.
cc_test(
@@ -78,9 +95,11 @@
],
)
-packageable_cc_binary(
+cc_binary_component(
name = "packageable_testonly",
- target = ":pkg_dep_test",
+ deps = [":pkg_dep_test"],
+ component_name = "packageable_testonly",
+ manifest = "manifest.cmx",
testonly = 1,
)
@@ -91,3 +110,12 @@
],
testonly = 1,
)
+
+verify_package(
+ name = "package_test_verify",
+ package = ":package_test",
+ files = [
+ "bin/pkg_dep_test",
+ ],
+ testonly = 1,
+)
diff --git a/sdk/bazel/tests/cc/cc/manifest.cmx b/sdk/bazel/tests/cc/cc/manifest.cmx
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/sdk/bazel/tests/cc/cc/manifest.cmx
@@ -0,0 +1 @@
+{}
diff --git a/sdk/bazel/tests/common/build_defs/BUILD b/sdk/bazel/tests/common/build_defs/BUILD
new file mode 100644
index 0000000..f99e954
--- /dev/null
+++ b/sdk/bazel/tests/common/build_defs/BUILD
@@ -0,0 +1,16 @@
+# 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.
+
+licenses(["notice"])
+
+package(default_visibility = ["//visibility:public"])
+
+exports_files(
+ glob(["*.bzl"]),
+)
+
+py_binary(
+ name = "package_verifier",
+ srcs = ["package_verifier.py"],
+)
diff --git a/sdk/bazel/tests/common/build_defs/package_verifier.py b/sdk/bazel/tests/common/build_defs/package_verifier.py
new file mode 100644
index 0000000..2b660f7
--- /dev/null
+++ b/sdk/bazel/tests/common/build_defs/package_verifier.py
@@ -0,0 +1,52 @@
+# 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 os
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--meta',
+ help='The path to the package\'s meta directory',
+ required=True)
+ parser.add_argument('--files',
+ help='The list of expected files in the package',
+ default=[],
+ nargs='*')
+ parser.add_argument('--stamp',
+ help='The path to the stamp file in case of success',
+ required=True)
+ args = parser.parse_args()
+
+ all_files = []
+ # List the files in the meta directory itself.
+ for root, dirs, files in os.walk(args.meta):
+ all_files += map(
+ lambda f: os.path.relpath(os.path.join(root, f), args.meta), files)
+ # Add the files outside of the meta directory, which are listed in
+ # meta/contents.
+ with open(os.path.join(args.meta, 'meta', 'contents')) as contents_file:
+ all_files += map(lambda l: l.strip().split('=', 1)[0],
+ contents_file.readlines())
+
+ has_errors = False
+ for file in args.files:
+ if file not in all_files:
+ print('Missing ' + file)
+ has_errors = True
+ if has_errors:
+ print('Known files:')
+ print(all_files)
+ return 1
+
+ with open(args.stamp, 'w') as stamp_file:
+ stamp_file.write('Success!')
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/sdk/bazel/tests/common/build_defs/verify_package.bzl b/sdk/bazel/tests/common/build_defs/verify_package.bzl
new file mode 100644
index 0000000..f29d3b1
--- /dev/null
+++ b/sdk/bazel/tests/common/build_defs/verify_package.bzl
@@ -0,0 +1,94 @@
+# 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.
+
+load("@fuchsia_sdk//build_defs:package_info.bzl", "PackageInfo")
+
+def _verify_package_impl(context):
+ # Unpack the package archive.
+ archive = context.attr.package[PackageInfo].archive
+ archive_dir = context.actions.declare_directory(context.attr.name)
+ context.actions.run(
+ executable = context.executable._far,
+ arguments = [
+ "extract",
+ "--archive=" + archive.path,
+ "--output=" + archive_dir.path,
+ ],
+ inputs = [
+ archive,
+ ],
+ outputs = [
+ archive_dir,
+ ],
+ mnemonic = "UnpackArchive",
+ )
+
+ # Unpack the meta.far archive.
+ meta_dir = context.actions.declare_directory(context.attr.name + "_meta")
+ context.actions.run(
+ executable = context.executable._far,
+ arguments = [
+ "extract",
+ "--archive=" + archive_dir.path + "/meta.far",
+ "--output=" + meta_dir.path,
+ ],
+ inputs = [
+ archive_dir,
+ ],
+ outputs = [
+ meta_dir,
+ ],
+ mnemonic = "UnpackMeta",
+ )
+
+ # Read meta/contents and verify that it contains the expected files.
+ success_stamp = context.actions.declare_file(context.attr.name + "_success")
+ context.actions.run(
+ executable = context.executable._verifier,
+ arguments = [
+ "--meta",
+ meta_dir.path,
+ "--stamp",
+ success_stamp.path,
+ "--files",
+ ] + context.attr.files,
+ inputs = [
+ meta_dir,
+ ],
+ outputs = [
+ success_stamp,
+ ],
+ )
+ return [
+ DefaultInfo(files = depset([success_stamp])),
+ ]
+
+verify_package = rule(
+ implementation = _verify_package_impl,
+ attrs = {
+ "package": attr.label(
+ doc = "The label of the package to verify",
+ mandatory = True,
+ allow_files = False,
+ providers = [PackageInfo],
+ ),
+ "files": attr.string_list(
+ doc = "The files expected to exist in the package",
+ mandatory = False,
+ allow_empty = True,
+ ),
+ "_far": attr.label(
+ default = Label("@fuchsia_sdk//tools:far"),
+ allow_single_file = True,
+ executable = True,
+ cfg = "host",
+ ),
+ "_verifier": attr.label(
+ default = Label("//build_defs:package_verifier"),
+ allow_files = True,
+ executable = True,
+ cfg = "host",
+ ),
+ },
+)