[sdk][bazel] Package up the proper version of prebuilt SDK libraries.
This change improves the packaging strategy for C++ binaries by defining explicit mappings for SDK libraries.
Test: added test for packaging non-SDK libraries, verified contents of resulting package
Bug: DX-312 #done
Change-Id: If619f9a82e40d4f5e4f99ce9d108fef849fdc444
diff --git a/sdk/bazel/base/build_defs/package.bzl b/sdk/bazel/base/build_defs/package.bzl
index 7a2fa67..9d406cd 100644
--- a/sdk/bazel/base/build_defs/package.bzl
+++ b/sdk/bazel/base/build_defs/package.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", "PackageAggregateInfo", "PackageLocalInfo")
+load(":package_info.bzl", "get_aggregate_info", "PackageAggregateInfo", "PackageGeneratedInfo", "PackageLocalInfo")
"""
Defines a Fuchsia package
@@ -18,23 +18,47 @@
The list of targets to be built into this package
"""
+# 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,
+}
+
def _info_impl(target, context):
mappings = []
if PackageLocalInfo in target:
mappings = target[PackageLocalInfo].mappings
- deps = context.attr.deps if hasattr(context.attr, "deps") else []
- return [get_aggregate_info(mappings, deps)]
+ elif PackageGeneratedInfo in target:
+ mappings = target[PackageGeneratedInfo].mappings
+ deps = []
+ for attribute, is_list in _ASPECT_ATTRIBUTES.items():
+ if hasattr(context.rule.attr, attribute):
+ value = getattr(context.rule.attr, attribute)
+ if is_list:
+ deps += value
+ else:
+ deps.append(value)
+ return [
+ get_aggregate_info(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 = [
- "deps",
- ],
+ attr_aspects = _ASPECT_ATTRIBUTES.keys(),
provides = [
PackageAggregateInfo,
],
+ # If any other aspect is applied to produce package mappings, let the result
+ # of that process be visible to the present aspect.
+ required_aspect_providers = [
+ PackageGeneratedInfo,
+ ],
)
def _fuchsia_package_impl(context):
diff --git a/sdk/bazel/base/build_defs/package_info.bzl b/sdk/bazel/base/build_defs/package_info.bzl
index ac16137..1c34d05 100644
--- a/sdk/bazel/base/build_defs/package_info.bzl
+++ b/sdk/bazel/base/build_defs/package_info.bzl
@@ -12,6 +12,15 @@
},
)
+# Identical to PackageLocalInfo, but a different type is needed when that
+# information if generated from an aspect so that it does not collide with any
+# existing PackageLocalInfo returned provider.
+PackageGeneratedInfo = provider(
+ fields = {
+ "mappings": "list of (package dest, source) pairs",
+ },
+)
+
PackageAggregateInfo = provider(
fields = {
"contents": "depset of (package dest, source) pairs",
@@ -19,6 +28,10 @@
)
def get_aggregate_info(mappings, deps):
- transitive_info = [dep[PackageAggregateInfo].contents for dep in deps]
+ transitive_info = []
+ for dep in deps:
+ if PackageAggregateInfo not in dep:
+ continue
+ transitive_info.append(dep[PackageAggregateInfo].contents)
return PackageAggregateInfo(contents = depset(mappings,
transitive = transitive_info))
diff --git a/sdk/bazel/base/build_defs/packageable_cc_binary.bzl b/sdk/bazel/base/build_defs/packageable_cc_binary.bzl
index 15677b2..b6bdbef 100644
--- a/sdk/bazel/base/build_defs/packageable_cc_binary.bzl
+++ b/sdk/bazel/base/build_defs/packageable_cc_binary.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", "PackageLocalInfo")
+load(":package_info.bzl", "get_aggregate_info", "PackageGeneratedInfo", "PackageLocalInfo")
"""
Makes a cc_binary ready for inclusion in a fuchsia_package.
@@ -13,38 +13,76 @@
The cc_binary to package up.
"""
-def _packageable_cc_binary(context):
- runfiles = []
- runfiles.append(context.attr.binary[DefaultInfo].default_runfiles.files)
- for dep in context.attr._deps:
- runfiles.append(dep[DefaultInfo].default_runfiles.files)
- runfiles_depset = depset(transitive = runfiles)
+def _cc_contents_impl(target, context):
+ if context.rule.kind != "cc_binary":
+ return [
+ PackageGeneratedInfo(mappings = []),
+ ]
mappings = {}
- for file in runfiles_depset.to_list():
+ for file in target[DefaultInfo].files.to_list():
if file.extension == "":
mappings["bin/" + file.basename] = file
elif file.extension == "so":
mappings["lib/" + file.basename] = file
- else:
- print("Ignoring file: " + file.path)
return [
- PackageLocalInfo(mappings = mappings.items()),
+ PackageGeneratedInfo(mappings = mappings.items()),
]
-packageable_cc_binary = rule(
- implementation = _packageable_cc_binary,
+# This aspect looks for cc_binary targets in the dependency tree of the given
+# target. For each of these targets, it then generates package content mappings.
+_cc_contents_aspect = aspect(
+ implementation = _cc_contents_impl,
+ attr_aspects = [
+ "data",
+ "deps",
+ "srcs",
+ ],
+ provides = [
+ PackageGeneratedInfo,
+ ],
+)
+
+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")
+ return [
+ # TODO(pylaligand): remove this extra mapping once it's obsolete.
+ PackageLocalInfo(mappings = [("bin/app", output)]),
+ ]
+
+_packageable_cc_binary = rule(
+ implementation = _packageable_cc_binary_impl,
attrs = {
- "binary": attr.label(
+ "target": attr.label(
doc = "The cc_binary to package",
allow_files = False,
- ),
- "_deps": attr.label_list(
- doc = "The dependencies needed in all packages",
- default = [
- # TODO(pylaligand): this label should be arch-independent.
- Label("//arch/x64/sysroot:dist"),
- Label("//pkg/fdio"),
+ aspects = [
+ _cc_contents_aspect,
],
- )
+ ),
},
)
+
+def packageable_cc_binary(name, target):
+ packaged_name = name + "_packaged"
+
+ _packageable_cc_binary(
+ name = packaged_name,
+ target = target,
+ )
+
+ # The filegroup is needed so that the packaging can properly crawl all the
+ # dependencies and look for package contents.
+ native.filegroup(
+ name = name,
+ srcs = [
+ ":" + packaged_name,
+ # TODO(pylaligand): this label should be arch-independent.
+ Label("//arch/x64/sysroot:dist"),
+ Label("//pkg/fdio"),
+ ]
+ )
diff --git a/sdk/bazel/generate.py b/sdk/bazel/generate.py
index 9b4bc3f..824f22b 100755
--- a/sdk/bazel/generate.py
+++ b/sdk/bazel/generate.py
@@ -190,8 +190,9 @@
destination = file.destination
extension = os.path.splitext(destination)[1][1:]
if extension == 'so' or extension == 'a' or extension == 'o':
- dest = os.path.join(base, 'arch',
- self.metadata.target_arch, destination)
+ relative_dest = os.path.join('arch', self.metadata.target_arch,
+ destination)
+ dest = os.path.join(base, relative_dest)
if os.path.isfile(dest):
raise Exception('File already exists: %s.' % dest)
self.make_dir(dest)
@@ -200,10 +201,11 @@
destination.startswith('lib')):
if library.prebuilt:
raise Exception('Multiple prebuilts for %s.' % dest)
- src = os.path.join('arch', self.metadata.target_arch,
- destination)
- library.prebuilt = src
+ library.prebuilt = relative_dest
library.is_static = extension == 'a'
+ if file.is_packaged:
+ package_path = 'lib/%s' % os.path.basename(relative_dest)
+ library.packaged_files[package_path] = relative_dest
elif self.is_overlay:
# Only binaries get installed in overlay mode.
continue
diff --git a/sdk/bazel/template_model.py b/sdk/bazel/template_model.py
index cd67e18..bf2ff92 100644
--- a/sdk/bazel/template_model.py
+++ b/sdk/bazel/template_model.py
@@ -28,6 +28,7 @@
self.prebuilt = ""
self.is_static = False
self.target_arch = target_arch
+ self.packaged_files = {}
class FidlLibrary(object):
diff --git a/sdk/bazel/templates/cc_prebuilt_library_srcs.mako b/sdk/bazel/templates/cc_prebuilt_library_srcs.mako
index 1b6215b..8a42f5f 100644
--- a/sdk/bazel/templates/cc_prebuilt_library_srcs.mako
+++ b/sdk/bazel/templates/cc_prebuilt_library_srcs.mako
@@ -7,3 +7,12 @@
shared_library = "${data.prebuilt}",
% endif
)
+
+package_files(
+ name = "${data.target_arch}_dist",
+ contents = {
+ % for path, source in sorted(data.packaged_files.iteritems()):
+ "${source}": "${path}",
+ % endfor
+ },
+)
diff --git a/sdk/bazel/templates/cc_prebuilt_library_top.mako b/sdk/bazel/templates/cc_prebuilt_library_top.mako
index c1c4b45..44ee02b 100644
--- a/sdk/bazel/templates/cc_prebuilt_library_top.mako
+++ b/sdk/bazel/templates/cc_prebuilt_library_top.mako
@@ -2,6 +2,8 @@
package(default_visibility = ["//visibility:public"])
+load("//build_defs:package_files.bzl", "package_files")
+
# Note: the cc_library / cc_import combo serves two purposes:
# - it allows the use of a select clause to target the proper architecture;
# - it works around an issue with cc_import which does not have an "includes"
@@ -26,6 +28,10 @@
"${include}",
% endfor
],
+ data = select({
+ "//build_defs/target_cpu:arm64": [":arm64_dist"],
+ "//build_defs/target_cpu:x64": [":x64_dist"],
+ }),
)
# Architecture-specific targets
diff --git a/sdk/bazel/tests/cc/BUILD b/sdk/bazel/tests/cc/BUILD
index f255f94..5f0487c 100644
--- a/sdk/bazel/tests/cc/BUILD
+++ b/sdk/bazel/tests/cc/BUILD
@@ -13,6 +13,29 @@
],
)
+# Local shared library for packaging.
+cc_binary(
+ name = "libshared.so",
+ srcs = [
+ "library.cc",
+ "library.h",
+ ],
+ linkshared = True,
+)
+
+cc_library(
+ name = "shared_library",
+ hdrs = [
+ "library.h",
+ ],
+ srcs = [
+ ":libshared.so",
+ ],
+ includes = [
+ ".",
+ ],
+)
+
# C++ program with dependency on a Fuchsia library.
cc_binary(
name = "pkg_dep",
@@ -20,14 +43,15 @@
"pkg_dep.cc",
],
deps = [
- "@fuchsia_sdk//pkg/fdio",
+ ":shared_library",
+ "@fuchsia_sdk//pkg/svc",
],
)
# Prepare the binary for inclusion in a package.
packageable_cc_binary(
name = "packageable",
- binary = ":pkg_dep",
+ target = ":pkg_dep",
)
# C++ program in a Fuchsia package.
diff --git a/sdk/bazel/tests/cc/library.cc b/sdk/bazel/tests/cc/library.cc
new file mode 100644
index 0000000..c72f367
--- /dev/null
+++ b/sdk/bazel/tests/cc/library.cc
@@ -0,0 +1,13 @@
+// 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.
+
+#include "library.h"
+
+namespace library {
+
+void do_something() {
+ // Maybe not...
+}
+
+} // namespace library
diff --git a/sdk/bazel/tests/cc/library.h b/sdk/bazel/tests/cc/library.h
new file mode 100644
index 0000000..cea815b
--- /dev/null
+++ b/sdk/bazel/tests/cc/library.h
@@ -0,0 +1,9 @@
+// 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.
+
+namespace library {
+
+void do_something();
+
+} // namespace library
diff --git a/sdk/bazel/tests/cc/pkg_dep.cc b/sdk/bazel/tests/cc/pkg_dep.cc
index 7d8fec9..4ad11dc 100644
--- a/sdk/bazel/tests/cc/pkg_dep.cc
+++ b/sdk/bazel/tests/cc/pkg_dep.cc
@@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <lib/fdio/namespace.h>
+#include <lib/svc/dir.h>
+
+#include "library.h"
int main(int argc, const char** argv) {
- fdio_ns_create(NULL);
+ svc_dir_destroy(NULL);
+ library::do_something();
}