From 6c1b2ef1df8753463a3f99b382ffce0beaed0e8d Mon Sep 17 00:00:00 2001 From: Wei Li Date: Fri, 5 May 2023 10:56:51 -0700 Subject: Check LicenseInfo provider since there might be more metadata included in attribute "applicable_licenses". Ignore default_metadata_file targets in apex_deps_aspect. Bug: 275472038 Test: CIs (cherry picked from https://android-review.googlesource.com/q/commit:0cb9f8fccce40c5dfc0b05107361ad04f77fc874) Merged-In: I98d3f9bb0235b082a7db64e3f2ad790e4d9c3d15 Change-Id: I98d3f9bb0235b082a7db64e3f2ad790e4d9c3d15 --- rules/apex/apex_deps_validation.bzl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'rules/apex') diff --git a/rules/apex/apex_deps_validation.bzl b/rules/apex/apex_deps_validation.bzl index 4d5e39de..f69b30e3 100644 --- a/rules/apex/apex_deps_validation.bzl +++ b/rules/apex/apex_deps_validation.bzl @@ -75,6 +75,9 @@ _IGNORED_ATTRS = [ "androidmk_dynamic_deps", "androidmk_deps", ] +_IGNORED_TARGETS = [ + "default_metadata_file", +] def _should_skip_apex_dep(target, ctx): # Ignore Bazel-specific targets like platform/os/arch constraints, @@ -85,7 +88,8 @@ def _should_skip_apex_dep(target, ctx): ctx.label.workspace_name in _IGNORED_REPOSITORIES or ctx.label.package in _IGNORED_PACKAGES or ctx.rule.kind in _IGNORED_RULE_KINDS or - True in [p in target for p in _IGNORED_PROVIDERS] + True in [p in target for p in _IGNORED_PROVIDERS] or + target.label.name in _IGNORED_TARGETS ) def _apex_dep_validation_aspect_impl(target, ctx): -- cgit v1.2.3 From e51200d82f04a1e762fcb6a7437559bee22095ae Mon Sep 17 00:00:00 2001 From: Wei Li Date: Fri, 5 May 2023 10:50:47 -0700 Subject: 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 --- rules/apex/BUILD | 9 ++++ rules/apex/METADATA | 1 + rules/apex/apex.bzl | 105 +++++++++++++++++++++++++++++++++++++++++++---- rules/apex/apex_test.bzl | 87 +++++++++++++++++++++++++++++++++++++++ rules/apex/cc.bzl | 5 +++ 5 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 rules/apex/METADATA (limited to 'rules/apex') 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 -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? ) -- cgit v1.2.3