Skip to content

Generate @package_metadata info for jvm_import targets #1410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 25, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@package_metadata//licenses/rules:license.bzl", "license")
load("@package_metadata//rules:package_metadata.bzl", "package_metadata")

exports_files([
"defs.bzl",
Expand All @@ -8,12 +10,31 @@ exports_files([

licenses(["notice"]) # Apache 2.0

package_metadata(
name = "package_metadata",
attributes = [
":license",
],
purl = "pkg:bazel/{}@{}".format(
module_name(),
module_version(),
) if module_version() else "pkg:bazel/{}".format(module_name()),
visibility = ["//visibility:public"],
)

license(
name = "license",
kind = "@package_metadata//licenses/spdx:Apache-2.0",
text = "LICENSE",
)

bzl_library(
name = "implementation",
srcs = [
":defs.bzl",
":specs.bzl",
"@bazel_features//:bzl_files",
"@package_metadata//:srcs",
"@rules_license//:docs_deps",
],
visibility = [
Expand Down
4 changes: 4 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ bazel_dep(
name = "bazel_skylib",
version = "1.7.1",
)
bazel_dep(
name = "package_metadata",
version = "0.0.3",
)
bazel_dep(
name = "platforms",
version = "0.0.10",
Expand Down
7 changes: 7 additions & 0 deletions REPO.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""File declaring metadata applying to all packages and targets in the Bazel module."""

repo(
default_package_metadata = [
"//:package_metadata",
],
)
33 changes: 32 additions & 1 deletion private/dependency_tree_parser.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ def _get_maven_url(artifact_urls):
# Return anything
return artifact_urls[0]

def _create_purl(coordinates):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jvm_import rule exposes the provider directly. We should do the same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should: users (companies) will want to override or add some attributes of package_metadata with info they need. They way this will work is that companies can define a toolchain for all the overrides/additions, keyed by the label of the package_metadata target. if we generate PackageMetadataInfo outside of package_metadata, that'll need to integrate with all the override toolchain stuff, which is probably not something I want rules to do.

See bazel-contrib/supply-chain#1 (comment) for a draft how the overrides are going to work

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't that happen at the point where people are consuming this data? In your example, the users will be the ones who want to (eg) annotate a dual-licensed project as using one specific license for their use.

# https://github.com/package-url/purl-spec/blob/main/PURL-TYPES.rst#maven
#
# TODO(yannic): Support qualifiers (e.g., the maven repo).
if coordinates.version:
return "pkg:maven/{}/{}@{}".format(coordinates.group, coordinates.artifact, coordinates.version)
else:
return "pkg:maven/{}/{}".format(coordinates.group, coordinates.artifact)

def _generate_target(
repository_ctx,
jar_versionless_target_labels,
Expand Down Expand Up @@ -244,22 +253,44 @@ copy_file(
target_import_string.append("\tmaven_coordinates = \"%s\"," % coordinates)
if len(artifact["urls"]):
target_import_string.append("\tmaven_url = \"%s\"," % maven_url)

package_metadata_name = "%s_package_metadata" % target_label
target_import_string.append("\tapplicable_licenses = [\":{}\"],".format(package_metadata_name))
to_return.append("""
package_metadata(
name = {package_metadata_name},
purl = {purl},
visibility = ["//visibility:public"],
)
""".format(
package_metadata_name = repr(package_metadata_name),
purl = repr(_create_purl(unpack_coordinates(coordinates))),
))
else:
unpacked = unpack_coordinates(coordinates)
url = maven_url if len(artifact["urls"]) else None

package_info_name = "%s_package_info" % target_label
target_import_string.append("\tapplicable_licenses = [\":%s\"]," % package_info_name)
package_metadata_name = "%s_package_metadata" % target_label
target_import_string.append("\tapplicable_licenses = [\n\t\t\":{}\",\n\t\t\":{}\",\n\t],".format(package_info_name, package_metadata_name))
to_return.append("""
package_info(
name = {name},
package_name = {coordinates},
package_url = {url},
package_version = {version},
)

package_metadata(
name = {package_metadata_name},
purl = {purl},
visibility = ["//visibility:public"],
)
""".format(
coordinates = repr(coordinates),
name = repr(package_info_name),
package_metadata_name = repr(package_metadata_name),
purl = repr(_create_purl(unpacked)),
url = repr(url),
version = repr(unpacked.version),
))
Expand Down
1 change: 1 addition & 0 deletions private/rules/coursier.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ _BUILD = """

load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@package_metadata//rules:package_metadata.bzl", "package_metadata")
load("@rules_license//rules:package_info.bzl", "package_info")
load("@rules_java//java:defs.bzl", "java_binary", "java_library", "java_plugin")
load("@rules_jvm_external//private/rules:pin_dependencies.bzl", "pin_dependencies")
Expand Down
11 changes: 11 additions & 0 deletions repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ def rules_jvm_external_deps(
patch_args = ["-p1"],
)

maybe(
http_archive,
name = "package_metadata",
urls = [
"https://mirror.bazel.build/github.com/bazel-contrib/supply-chain/releases/download/v0.0.3/supply-chain-v0.0.3.tar.gz",
"https://github.com/bazel-contrib/supply-chain/releases/download/v0.0.3/supply-chain-v0.0.3.tar.gz",
],
sha256 = "0e89367f1cb6d93a5a1afea4b55b11ea6b28f63f653b47154153677ca7d4afea",
strip_prefix = "supply-chain-0.0.3/metadata",
)

maybe(
http_archive,
name = "rules_license",
Expand Down
57 changes: 57 additions & 0 deletions tests/unit/jvm_import/jvm_import_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This module contains a test suite for testing jvm_import
"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("@package_metadata//providers:package_metadata_info.bzl", "PackageMetadataInfo")
load("@rules_license//rules:gather_metadata.bzl", "gather_metadata_info")
load("@rules_license//rules:providers.bzl", "PackageInfo")
load("@rules_license//rules_gathering:gathering_providers.bzl", "TransitiveMetadataInfo")
Expand All @@ -28,6 +29,13 @@ TagsInfo = provider(
},
)

PackageMetadataInfoCollectionInfo = provider(
doc = "Provider to propagate jvm_import's applicable_licenses for testing purposes",
fields = {
"info": "A PackageMetadataInfo provider from jvm_import's applicable_licenses for testing purposes",
},
)

def _tags_propagator_impl(target, ctx):
tags = getattr(ctx.rule.attr, "tags")
return TagsInfo(tags = tags)
Expand All @@ -38,6 +46,30 @@ tags_propagator = aspect(
implementation = _tags_propagator_impl,
)

def _package_metadata_info_propagator_impl(target, ctx):
direct = getattr(ctx.rule.attr, "package_metadata", None)
if not direct:
direct = getattr(ctx.rule.attr, "applicable_licenses")

infos = [t[PackageMetadataInfo] for t in direct if PackageMetadataInfo in t]
for t in direct:
if PackageMetadataInfo not in t:
continue

return [
PackageMetadataInfoCollectionInfo(
info = t[PackageMetadataInfo],
),
]

return []

package_metadata_info_propagator = aspect(
doc = "Aspect that propagates applicable_licenses to help with testing jvm_import",
attr_aspects = [],
implementation = _package_metadata_info_propagator_impl,
)

def _does_jvm_import_have_tags_impl(ctx):
env = analysistest.begin(ctx)

Expand All @@ -62,6 +94,26 @@ does_jvm_import_have_tags_test = analysistest.make(
},
)

def _does_jvm_import_have_applicable_licenses_impl(ctx):
env = analysistest.begin(ctx)

asserts.true(env, PackageMetadataInfoCollectionInfo in ctx.attr.src)
info = ctx.attr.src[PackageMetadataInfoCollectionInfo]
asserts.true(env, info.info)

return analysistest.end(env)

does_jvm_import_have_applicable_licenses_test = analysistest.make(
_does_jvm_import_have_applicable_licenses_impl,
attrs = {
"src": attr.label(
doc = "Target to traverse for applicable_licenses",
aspects = [package_metadata_info_propagator],
mandatory = True,
),
},
)

def _does_jvm_import_export_a_package_provider_impl(ctx):
env = analysistest.begin(ctx)

Expand Down Expand Up @@ -116,6 +168,11 @@ def jvm_import_test_suite(name):
target_under_test = "@jvm_import_test//:com_google_code_findbugs_jsr305_3_0_2",
src = "@jvm_import_test//:com_google_code_findbugs_jsr305_3_0_2",
)
does_jvm_import_have_applicable_licenses_test(
name = "does_jvm_import_have_applicable_licenses_test",
target_under_test = "@jvm_import_test//:com_google_code_findbugs_jsr305_3_0_2",
src = "@jvm_import_test//:com_google_code_findbugs_jsr305_3_0_2",
)
does_jvm_import_export_a_package_provider_test(
name = "does_jvm_import_export_a_package_provider",
target_under_test = "@jvm_import_test//:com_google_code_findbugs_jsr305",
Expand Down