aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Li <weiwli@google.com>2023-05-05 10:50:47 -0700
committerCherrypicker Worker <android-build-cherrypicker-worker@google.com>2023-05-19 00:35:29 +0000
commite51200d82f04a1e762fcb6a7437559bee22095ae (patch)
treeec7fc6abfc5947b2ce79db2749e714ac7c5af309
parent6c1b2ef1df8753463a3f99b382ffce0beaed0e8d (diff)
downloadbazel-e51200d82f04a1e762fcb6a7437559bee22095ae.tar.gz
Changes in Bazel rules to support SBOM generation of b build unbundled APEXs.
1) Add MetadataFileInfo provider to modules if METADATA file is declared in attribute applicable_licenses, which might be from default_metadata_file of packages 2) cc_aspect collect MetadataFileInfo for some rules that are transitive deps and included in APEXs 3) Generate SBOM for .apex or .capex in apex rule Bug: 275472038 Test: CIs Test: b build //packages/modules/adb/apex:com.android.adbd --config=android --output_groups=+apex_sbom (cherry picked from https://android-review.googlesource.com/q/commit:8efceb357b097cf977cb06ad9dc4f5b3089f9d6a) Merged-In: I727610608487a12873671dd27d56465fc2c19436 Change-Id: I727610608487a12873671dd27d56465fc2c19436
-rw-r--r--rules/apex/BUILD9
-rw-r--r--rules/apex/METADATA1
-rw-r--r--rules/apex/apex.bzl105
-rw-r--r--rules/apex/apex_test.bzl87
-rw-r--r--rules/apex/cc.bzl5
-rw-r--r--rules/license/license_aspect.bzl20
-rw-r--r--rules/metadata.bzl19
7 files changed, 236 insertions, 10 deletions
diff --git a/rules/apex/BUILD b/rules/apex/BUILD
index 61bacae8..3c02fad2 100644
--- a/rules/apex/BUILD
+++ b/rules/apex/BUILD
@@ -11,6 +11,15 @@ load(":apex_key_test.bzl", "apex_key_test_suite")
load(":apex_mk_test.bzl", "apex_mk_test_suite")
load(":apex_test.bzl", "apex_test_suite")
+# Setup package and default_metadata_file for _test_apex_sbom in apex_test.bzl
+package(default_package_metadata = [":default_metadata_file"])
+
+filegroup(
+ name = "default_metadata_file",
+ srcs = ["METADATA"],
+ applicable_licenses = [],
+)
+
string_setting(
name = "apex_name",
build_setting_default = "",
diff --git a/rules/apex/METADATA b/rules/apex/METADATA
new file mode 100644
index 00000000..503057ea
--- /dev/null
+++ b/rules/apex/METADATA
@@ -0,0 +1 @@
+# Created for _test_apex_sbom in apex_test.bzl \ No newline at end of file
diff --git a/rules/apex/apex.bzl b/rules/apex/apex.bzl
index c31b6404..be1cf588 100644
--- a/rules/apex/apex.bzl
+++ b/rules/apex/apex.bzl
@@ -18,6 +18,7 @@ load("@soong_injection//apex_toolchain:constants.bzl", "default_manifest_version
load("//build/bazel/platforms:platform_utils.bzl", "platforms")
load("//build/bazel/product_config:product_variables_providing_rule.bzl", "ProductVariablesInfo")
load("//build/bazel/rules:common.bzl", "get_dep_targets")
+load("//build/bazel/rules:metadata.bzl", "MetadataFileInfo")
load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo")
load("//build/bazel/rules:sh_binary.bzl", "ShBinaryInfo")
load("//build/bazel/rules:toolchain_utils.bzl", "verify_toolchain_exists")
@@ -61,6 +62,7 @@ def _create_file_mapping(ctx):
requires = {}
provides = {}
make_modules_to_install = {}
+ metadata_file_mapping = {}
# Generate a str -> str dictionary to define Make modules and variables for the
# packaging step in a mixed build. This is necessary as long as there are
@@ -71,13 +73,14 @@ def _create_file_mapping(ctx):
arch = platforms.get_target_arch(ctx.attr._platform_utils)
is_target_64_bit = platforms.get_target_bitness(ctx.attr._platform_utils) == 64
- def add_file_mapping(install_dir, basename, bazel_file, klass, owner, arch = None, unstripped = None):
+ def add_file_mapping(install_dir, basename, bazel_file, klass, owner, arch = None, unstripped = None, metadata_file = None):
installed_path = paths.join(install_dir, basename)
if installed_path in file_mapping and file_mapping[installed_path] != bazel_file:
# TODO: we should figure this out and make it a failure
print("Warning: %s in this apex is already installed to %s, overwriting it with %s" %
(file_mapping[installed_path].path, installed_path, bazel_file.path))
file_mapping[installed_path] = bazel_file
+ metadata_file_mapping[installed_path] = metadata_file
files_info = {
"built_file": bazel_file.path,
@@ -110,6 +113,7 @@ def _create_file_mapping(ctx):
stripped.owner,
arch = arch,
unstripped = unstripped,
+ metadata_file = lib_file.metadata_file,
)
# For bundled builds.
@@ -139,7 +143,15 @@ def _create_file_mapping(ctx):
filename = prebuilt_file_info.filename
else:
filename = dep.label.name
- add_file_mapping(prebuilt_file_info.dir, filename, prebuilt_file_info.src, "etc", dep.label, arch = arch)
+ add_file_mapping(
+ prebuilt_file_info.dir,
+ filename,
+ prebuilt_file_info.src,
+ "etc",
+ dep.label,
+ arch = arch,
+ metadata_file = dep[MetadataFileInfo].metadata_file,
+ )
# Handle binaries
for dep in ctx.attr.binaries:
@@ -155,7 +167,15 @@ def _create_file_mapping(ctx):
if sh_binary_info.filename:
filename = sh_binary_info.filename
- add_file_mapping(directory, filename, dep[DefaultInfo].files_to_run.executable, "shBinary", dep.label, arch = arch)
+ add_file_mapping(
+ directory,
+ filename,
+ dep[DefaultInfo].files_to_run.executable,
+ "shBinary",
+ dep.label,
+ arch = arch,
+ metadata_file = dep[MetadataFileInfo].metadata_file,
+ )
elif ApexCcInfo in dep:
# cc_binary just takes the final executable from the runfiles.
add_file_mapping(
@@ -166,6 +186,7 @@ def _create_file_mapping(ctx):
dep.label,
arch,
unstripped = dep[CcUnstrippedInfo].unstripped[0].files.to_list()[0],
+ metadata_file = dep[MetadataFileInfo].metadata_file,
)
# Add transitive shared lib deps of apex binaries to the apex.
@@ -181,6 +202,7 @@ def _create_file_mapping(ctx):
backing_libs,
sorted(make_modules_to_install),
sorted(make_files_info.values(), key = lambda x: ":".join([x["package"], x["make_module_name"], x["arch"]])),
+ metadata_file_mapping,
)
def _add_so(label):
@@ -406,7 +428,7 @@ def _run_apexer(ctx, apex_toolchain):
pubkey = apex_key_info.public_key
android_jar = apex_toolchain.android_jar
- file_mapping, requires_native_libs, provides_native_libs, backing_libs, make_modules_to_install, make_files_info = _create_file_mapping(ctx)
+ file_mapping, requires_native_libs, provides_native_libs, backing_libs, make_modules_to_install, make_files_info, metadata_file_mapping = _create_file_mapping(ctx)
canned_fs_config = _generate_canned_fs_config(ctx, file_mapping.keys())
file_contexts = _generate_file_contexts(ctx)
full_apex_manifest_json = _add_apex_manifest_information(ctx, apex_toolchain, requires_native_libs, provides_native_libs)
@@ -567,6 +589,8 @@ def _run_apexer(ctx, apex_toolchain):
installed_files = _generate_installed_files_list(ctx, file_mapping),
make_modules_to_install = make_modules_to_install,
make_files_info = make_files_info,
+ file_mapping = file_mapping,
+ metadata_file_mapping = metadata_file_mapping,
)
def _run_signapk(ctx, unsigned_file, signed_file, private_key, public_key, mnemonic):
@@ -757,6 +781,65 @@ def _verify_updatability(ctx):
if not ctx.attr.min_sdk_version:
fail("updatable APEXes must set min_sdk_version.")
+def _generate_sbom(ctx, file_mapping, metadata_file_mapping, apex_file):
+ apex_filename = paths.basename(apex_file.path)
+ sbom_metadata_csv = ctx.actions.declare_file(apex_filename + "-sbom-metadata.csv")
+ command = []
+ metadata_files = []
+ command.append("echo installed_file,module_path,soong_module_type,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,build_output_path")
+ command.append("echo %s,%s,,,,,,%s" % (apex_filename, ctx.label.package, apex_file.path))
+ for installed_file, bazel_output_file in file_mapping.items():
+ if metadata_file_mapping[installed_file]:
+ metadata_files.append(metadata_file_mapping[installed_file])
+ command.append("echo %s,%s,,,,,,%s" % (installed_file, paths.dirname(bazel_output_file.short_path), bazel_output_file.path))
+ ctx.actions.run_shell(
+ inputs = file_mapping.values(),
+ outputs = [sbom_metadata_csv],
+ mnemonic = "GenerateSBOMMetadata",
+ command = "(" + "; ".join(command) + ") > " + sbom_metadata_csv.path,
+ )
+
+ sbom_file = ctx.actions.declare_file(apex_filename + ".spdx.json")
+ sbom_fragment_file = ctx.actions.declare_file(apex_filename + "-fragment.spdx")
+ inputs = [
+ apex_file,
+ sbom_metadata_csv,
+ ctx.executable._generate_sbom,
+ ]
+ inputs += file_mapping.values()
+ inputs += metadata_files
+
+ product_vars = ctx.attr._product_variables[ProductVariablesInfo]
+ build_fingerprint = "%s/%s/%s:%s/%s/%s:%s/%s" % (
+ product_vars.ProductBrand,
+ product_vars.DeviceProduct,
+ product_vars.DeviceName,
+ product_vars.Platform_version_name,
+ product_vars.BuildId,
+ "",
+ product_vars.TargetBuildVariant,
+ "_".join(product_vars.BuildVersionTags),
+ )
+ ctx.actions.run(
+ inputs = inputs,
+ outputs = [sbom_file, sbom_fragment_file],
+ arguments = [
+ "--output_file",
+ sbom_file.path,
+ "--metadata",
+ sbom_metadata_csv.path,
+ "--build_version",
+ build_fingerprint,
+ "--product_mfr",
+ product_vars.ProductManufacturer,
+ "--json",
+ "--unbundled_apex",
+ ],
+ mnemonic = "GenerateSBOM",
+ executable = ctx.executable._generate_sbom,
+ )
+ return [sbom_file, sbom_fragment_file]
+
# See the APEX section in the README on how to use this rule.
def _apex_rule_impl(ctx):
verify_toolchain_exists(ctx, "//build/bazel/rules/apex:apex_toolchain_type")
@@ -829,6 +912,8 @@ def _apex_rule_impl(ctx):
backing_libs = depset([apexer_outputs.backing_libs]),
installed_files = depset([apexer_outputs.installed_files]),
transitive_unvalidated_targets = depset([transitive_unvalidated_targets_output_file]),
+ apex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_apex)),
+ capex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_capex) if signed_capex else []),
_validation = apex_deps_validation_files,
**optional_output_groups
),
@@ -895,13 +980,13 @@ APEX is truly updatable. To be updatable, min_sdk_version should be set as well.
# Attributes that contribute to the payload.
"native_shared_libs_32": attr.label_list(
providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
- aspects = [apex_cc_aspect] + STANDARD_PAYLOAD_ASPECTS,
+ aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
cfg = shared_lib_transition_32,
doc = "The libs compiled for 32-bit",
),
"native_shared_libs_64": attr.label_list(
providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
- aspects = [apex_cc_aspect] + STANDARD_PAYLOAD_ASPECTS,
+ aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
cfg = shared_lib_transition_64,
doc = "The libs compiled for 64-bit",
),
@@ -912,7 +997,7 @@ APEX is truly updatable. To be updatable, min_sdk_version should be set as well.
[StrippedCcBinaryInfo, CcInfo, ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo], # cc_binary (stripped)
],
cfg = apex_transition,
- aspects = [apex_cc_aspect] + STANDARD_PAYLOAD_ASPECTS,
+ aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
),
"prebuilts": attr.label_list(
providers = [PrebuiltFileInfo, RuleLicensedDependenciesInfo],
@@ -953,6 +1038,12 @@ APEX is truly updatable. To be updatable, min_sdk_version should be set as well.
"_platform_utils": attr.label(
default = Label("//build/bazel/platforms:platform_utils"),
),
+ "_generate_sbom": attr.label(
+ cfg = "exec",
+ doc = "SBOM generation tool",
+ executable = True,
+ default = "//build/make/tools/sbom:generate-sbom",
+ ),
# allowed deps check
"_unsafe_disable_apex_allowed_deps_check": attr.label(
diff --git a/rules/apex/apex_test.bzl b/rules/apex/apex_test.bzl
index 51ba3d07..e155bf58 100644
--- a/rules/apex/apex_test.bzl
+++ b/rules/apex/apex_test.bzl
@@ -2682,6 +2682,92 @@ def _test_min_target_sdk_version_api_fingerprint_min_sdk_version_not_specified()
return test_name
+def _apex_sbom_test(ctx):
+ env = analysistest.begin(ctx)
+
+ # Action GenerateSBOMMetadata
+ actions = [a for a in analysistest.target_actions(env) if a.mnemonic == "GenerateSBOMMetadata"]
+ asserts.true(
+ env,
+ len(actions) == 1,
+ "No GenerateSBOMMetadata action found for creating <apex>-sbom-metadata.csv file: %s" % actions,
+ )
+
+ input_files = [input.basename for input in actions[0].inputs.to_list()]
+ asserts.true(
+ env,
+ "apex_sbom_lib_cc.so" in input_files,
+ "No expected file in inputs of GenerateSBOMMetadata action",
+ )
+
+ output_files = [output.basename for output in actions[0].outputs.to_list()]
+ asserts.true(
+ env,
+ "apex_sbom.apex-sbom-metadata.csv" in output_files,
+ "No expected file in outputs of GenerateSBOMMetadata action",
+ )
+
+ # Action GenerateSBOM
+ actions = [a for a in analysistest.target_actions(env) if a.mnemonic == "GenerateSBOM"]
+ asserts.true(
+ env,
+ len(actions) == 1,
+ "No GenerateSBOM action found for creating sbom.spdx.json file: %s" % actions,
+ )
+ input_files = [input.short_path for input in actions[0].inputs.to_list()]
+ expected_input_files = [
+ "build/bazel/rules/apex/apex_sbom.apex",
+ "build/bazel/rules/apex/apex_sbom.apex-sbom-metadata.csv",
+ "build/make/tools/sbom/generate-sbom",
+ "build/bazel/rules/apex/apex_sbom_lib_cc.so",
+ "build/bazel/rules/apex/METADATA",
+ ]
+ asserts.true(
+ env,
+ all([f in input_files for f in expected_input_files]),
+ "Missing input files: %s" % input_files,
+ )
+
+ output_files = [output.basename for output in actions[0].outputs.to_list()]
+ expected_output_files = [
+ "apex_sbom.apex.spdx.json",
+ "apex_sbom.apex-fragment.spdx",
+ ]
+ asserts.true(
+ env,
+ all([f in output_files for f in expected_output_files]),
+ "Missing output files: %s" % input_files,
+ )
+
+ return analysistest.end(env)
+
+apex_sbom_test = analysistest.make(
+ _apex_sbom_test,
+)
+
+def _test_apex_sbom():
+ name = "apex_sbom"
+ test_name = name + "_test"
+
+ cc_library_shared(
+ name = name + "_lib_cc",
+ srcs = [name + "_lib.cc"],
+ tags = ["manual"],
+ )
+
+ test_apex(
+ name = name,
+ native_shared_libs_32 = [name + "_lib_cc"],
+ android_manifest = "AndroidManifest.xml",
+ )
+
+ apex_sbom_test(
+ name = test_name,
+ target_under_test = name,
+ )
+
+ return test_name
+
def apex_test_suite(name):
native.test_suite(
name = name,
@@ -2739,5 +2825,6 @@ def apex_test_suite(name):
_test_apex_no_compression(),
_test_min_target_sdk_version_api_fingerprint_min_sdk_version_specified(),
_test_min_target_sdk_version_api_fingerprint_min_sdk_version_not_specified(),
+ _test_apex_sbom(),
] + _test_apex_transition(),
)
diff --git a/rules/apex/cc.bzl b/rules/apex/cc.bzl
index 1bb14871..b47b29b0 100644
--- a/rules/apex/cc.bzl
+++ b/rules/apex/cc.bzl
@@ -14,10 +14,12 @@
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("//build/bazel/product_config:product_variables_providing_rule.bzl", "ProductVariablesInfo")
+load("//build/bazel/rules:metadata.bzl", "MetadataFileInfo")
load("//build/bazel/rules/cc:cc_library_common.bzl", "parse_apex_sdk_version")
load("//build/bazel/rules/cc:cc_library_shared.bzl", "CcSharedLibraryOutputInfo", "CcStubLibrariesInfo")
load("//build/bazel/rules/cc:cc_stub_library.bzl", "CcStubLibrarySharedInfo")
load("//build/bazel/rules/cc:stripped_cc_common.bzl", "CcUnstrippedInfo")
+load("//build/bazel/rules/license:license_aspect.bzl", "license_aspect")
ApexCcInfo = provider(
"Info needed to use CC targets in APEXes",
@@ -219,6 +221,7 @@ def _apex_cc_aspect_impl(target, ctx):
shared_object_files.append(struct(
stripped = target[CcSharedLibraryOutputInfo].output_file,
unstripped = target[CcUnstrippedInfo].unstripped,
+ metadata_file = target[MetadataFileInfo].metadata_file,
))
if hasattr(ctx.rule.attr, "shared"):
transitive_deps.append(ctx.rule.attr.shared[0])
@@ -247,6 +250,7 @@ def _apex_cc_aspect_impl(target, ctx):
shared_object_files.append(struct(
stripped = output_file,
unstripped = unstripped,
+ metadata_file = dep[MetadataFileInfo].metadata_file,
))
transitive_deps.append(dep)
@@ -297,5 +301,6 @@ apex_cc_aspect = aspect(
"_product_variables": attr.label(default = "//build/bazel/product_config:product_vars"),
},
attr_aspects = CC_ATTR_ASPECTS,
+ requires = [license_aspect],
# TODO: Have this aspect also propagate along attributes of native_shared_libs?
)
diff --git a/rules/license/license_aspect.bzl b/rules/license/license_aspect.bzl
index 2302137b..00a91f5b 100644
--- a/rules/license/license_aspect.bzl
+++ b/rules/license/license_aspect.bzl
@@ -1,4 +1,5 @@
load("@rules_license//rules:providers.bzl", "LicenseInfo")
+load("//build/bazel/rules:metadata.bzl", "MetadataFileInfo")
RuleLicensedDependenciesInfo = provider(
doc = """Rule's licensed dependencies.""",
@@ -14,9 +15,18 @@ def _maybe_expand(rule, transitive_licenses):
if hasattr(dep_info, "license_closure"):
transitive_licenses.append(dep_info.license_closure)
+def create_metadata_file_info(ctx):
+ if hasattr(ctx.rule.attr, "applicable_licenses"):
+ for lic in ctx.rule.attr.applicable_licenses:
+ files = lic.files.to_list()
+ if len(files) == 1 and files[0].basename == "METADATA":
+ return MetadataFileInfo(metadata_file = files[0])
+
+ return MetadataFileInfo(metadata_file = None)
+
def _rule_licenses_aspect_impl(_rule, ctx):
if ctx.rule.kind == "_license":
- return RuleLicensedDependenciesInfo()
+ return [RuleLicensedDependenciesInfo(), MetadataFileInfo()]
licenses = []
transitive_licenses = []
@@ -32,14 +42,18 @@ def _rule_licenses_aspect_impl(_rule, ctx):
for item in vlist:
if type(item) == "Target" and RuleLicensedDependenciesInfo in item:
_maybe_expand(item, transitive_licenses)
- return RuleLicensedDependenciesInfo(license_closure = depset(licenses, transitive = transitive_licenses))
+
+ return [
+ RuleLicensedDependenciesInfo(license_closure = depset(licenses, transitive = transitive_licenses)),
+ create_metadata_file_info(ctx),
+ ]
license_aspect = aspect(
doc = """Collect transitive license closure.""",
implementation = _rule_licenses_aspect_impl,
attr_aspects = ["*"],
apply_to_generating_rules = True,
- provides = [RuleLicensedDependenciesInfo],
+ provides = [RuleLicensedDependenciesInfo, MetadataFileInfo],
)
_license_kind_template = """
diff --git a/rules/metadata.bzl b/rules/metadata.bzl
new file mode 100644
index 00000000..287905cd
--- /dev/null
+++ b/rules/metadata.bzl
@@ -0,0 +1,19 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+MetadataFileInfo = provider(
+ fields = {
+ "metadata_file": "METADATA file of a module",
+ },
+)