diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..bc86ac11
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @confluentinc/builds-automations-and-repositories
diff --git a/private/rules/has_maven_deps.bzl b/private/rules/has_maven_deps.bzl
index 93ade76c..15a0536e 100644
--- a/private/rules/has_maven_deps.bzl
+++ b/private/rules/has_maven_deps.bzl
@@ -129,7 +129,6 @@ def _has_maven_deps_impl(target, ctx):
return _EMPTY_INFO
coordinates = read_coordinates(ctx.rule.attr.tags)
- label_to_javainfo = {target.label: target[JavaInfo]}
gathered = _gathered(
all_infos = [],
diff --git a/private/rules/java_export.bzl b/private/rules/java_export.bzl
index 09283f1c..7cbddbd8 100644
--- a/private/rules/java_export.bzl
+++ b/private/rules/java_export.bzl
@@ -1,3 +1,4 @@
+load("//:specs.bzl", "parse", _json = "json")
load(":javadoc.bzl", "javadoc")
load(":maven_bom_fragment.bzl", "maven_bom_fragment")
load(":maven_project_jar.bzl", "DEFAULT_EXCLUDED_WORKSPACES", "maven_project_jar")
@@ -10,6 +11,7 @@ def java_export(
manifest_entries = {},
deploy_env = [],
excluded_workspaces = {name: None for name in DEFAULT_EXCLUDED_WORKSPACES},
+ exclusions = {},
pom_template = None,
visibility = None,
tags = [],
@@ -70,6 +72,9 @@ def java_export(
that should not be included in the maven jar to a `Label` pointing to the dependency
that workspace should be replaced by, or `None` if the exclusion shouldn't be replaced
with an extra dependency.
+ exclusions: Mapping of target labels to a list of exclusions to be added to the POM file.
+ Each label must correspond to a direct maven dependency of this target.
+ Each exclusion is represented as a `group:artifact` string.
classifier_artifacts: A dict of classifier -> artifact of additional artifacts to publish to Maven.
doc_deps: Other `javadoc` targets that are referenced by the generated `javadoc` target
(if not using `tags = ["no-javadoc"]`)
@@ -105,6 +110,7 @@ def java_export(
manifest_entries = manifest_entries,
deploy_env = deploy_env,
excluded_workspaces = excluded_workspaces,
+ exclusions = exclusions,
pom_template = pom_template,
visibility = visibility,
tags = tags,
@@ -124,6 +130,7 @@ def maven_export(
manifest_entries = {},
deploy_env = [],
excluded_workspaces = {},
+ exclusions = {},
pom_template = None,
visibility = None,
tags = [],
@@ -188,6 +195,9 @@ def maven_export(
that should not be included in the maven jar to a `Label` pointing to the dependency
that workspace should be replaced by, or `None` if the exclusion shouldn't be replaced
with an extra dependency.
+ exclusions: Mapping of target labels to a list of exclusions to be added to the POM file.
+ Each label must correspond to a direct maven dependency of this target.
+ Each exclusion is represented as a `group:artifact` string.
doc_deps: Other `javadoc` targets that are referenced by the generated `javadoc` target
(if not using `tags = ["no-javadoc"]`)
doc_url: The URL at which the generated `javadoc` will be hosted (if not using
@@ -271,6 +281,13 @@ def maven_export(
)
classifier_artifacts.setdefault("javadoc", docs_jar)
+ exclusions_dict_json_strings = {
+ target: _json.write_exclusion_spec_list(
+ parse.parse_exclusion_spec_list(targetExclusions),
+ )
+ for target, targetExclusions in exclusions.items()
+ }
+
pom_file(
name = "%s-pom" % name,
target = ":%s" % lib_name,
@@ -280,6 +297,7 @@ def maven_export(
tags = tags,
testonly = testonly,
toolchains = toolchains,
+ exclusions = exclusions_dict_json_strings,
)
maven_publish(
diff --git a/private/rules/maven_bom.bzl b/private/rules/maven_bom.bzl
index a6ae9b83..20e6b710 100644
--- a/private/rules/maven_bom.bzl
+++ b/private/rules/maven_bom.bzl
@@ -41,7 +41,7 @@ def _maven_bom_impl(ctx):
bom = generate_pom(
ctx,
coordinates = coordinates,
- versioned_dep_coordinates = [f[MavenBomFragmentInfo].coordinates for f in ctx.attr.fragments],
+ versioned_dep_coordinates = [unpack_coordinates(f[MavenBomFragmentInfo].coordinates) for f in ctx.attr.fragments],
pom_template = ctx.file.pom_template,
out_name = "%s.xml" % ctx.label.name,
)
@@ -77,13 +77,22 @@ def _maven_dependencies_bom_impl(ctx):
# included in the main BOM
first_order_deps = [f[MavenBomFragmentInfo].coordinates for f in ctx.attr.fragments]
all_deps = depset(transitive = [f.maven_info.maven_deps for f in fragments]).to_list()
- combined_deps = [a for a in all_deps if a not in first_order_deps]
+ combined_deps = [unpack_coordinates(a) for a in all_deps if a not in first_order_deps]
unpacked = unpack_coordinates(ctx.attr.bom_coordinates)
+ unpacked = struct(
+ groupId = unpacked.groupId,
+ artifactId = unpacked.artifactId,
+ type = "pom",
+ scope = "import",
+ classifier = unpacked.classifier,
+ version = unpacked.version,
+ )
+
dependencies_bom = generate_pom(
ctx,
coordinates = ctx.attr.maven_coordinates,
- versioned_dep_coordinates = combined_deps + ["%s:%s:pom:import:%s" % (unpacked.groupId, unpacked.artifactId, unpacked.version)],
+ versioned_dep_coordinates = combined_deps + [unpacked],
pom_template = ctx.file.pom_template,
out_name = "%s.xml" % ctx.label.name,
indent = 12,
diff --git a/private/rules/maven_utils.bzl b/private/rules/maven_utils.bzl
index ac46e81a..af4e7c7e 100644
--- a/private/rules/maven_utils.bzl
+++ b/private/rules/maven_utils.bzl
@@ -2,11 +2,11 @@ load("//private/lib:bzlmod.bzl", "get_module_name_of_owner_of_repo")
def unpack_coordinates(coords):
"""Takes a maven coordinate and unpacks it into a struct with fields
- `groupId`, `artifactId`, `version`, `type`, `scope`
+ `groupId`, `artifactId`, `version`, `type`, `classifier`
where type and scope are optional.
Assumes following maven coordinate syntax:
- groupId:artifactId[:type[:scope]]:version
+ groupId:artifactId[:type[:classifier]]:version
"""
if not coords:
return None
@@ -20,6 +20,7 @@ def unpack_coordinates(coords):
artifactId = parts[1],
type = None,
scope = None,
+ classifier = None,
version = None,
)
@@ -32,7 +33,8 @@ def unpack_coordinates(coords):
groupId = parts.get(0),
artifactId = parts.get(1),
type = parts.get(2),
- scope = parts.get(3),
+ scope = None,
+ classifier = parts.get(3),
version = version,
)
@@ -42,7 +44,7 @@ def _whitespace(indent):
whitespace = whitespace + " "
return whitespace
-def format_dep(unpacked, indent = 8, include_version = True):
+def format_dep(unpacked, indent = 8, include_version = True, exclusions = {}):
whitespace = _whitespace(indent)
dependency = [
@@ -72,6 +74,33 @@ def format_dep(unpacked, indent = 8, include_version = True):
" %s\n" % unpacked.scope,
])
+ if unpacked.classifier:
+ dependency.extend([
+ whitespace,
+ " %s\n" % unpacked.classifier,
+ ])
+
+ if exclusions:
+ dependency.extend([
+ whitespace,
+ " \n",
+ ])
+ for exclusion in exclusions:
+ dependency.extend([
+ whitespace,
+ " \n",
+ whitespace,
+ " %s\n" % exclusion["group"],
+ whitespace,
+ " %s\n" % exclusion["artifact"],
+ whitespace,
+ " \n",
+ ])
+ dependency.extend([
+ whitespace,
+ " \n",
+ ])
+
dependency.extend([
whitespace,
"",
@@ -88,16 +117,22 @@ def generate_pom(
versioned_dep_coordinates = [],
unversioned_dep_coordinates = [],
runtime_deps = [],
- indent = 8):
+ indent = 8,
+ exclusions = {}):
unpacked_coordinates = unpack_coordinates(coordinates)
substitutions = {
"{groupId}": unpacked_coordinates.groupId,
"{artifactId}": unpacked_coordinates.artifactId,
"{version}": unpacked_coordinates.version,
"{type}": unpacked_coordinates.type or "jar",
+ "{classifier}": unpacked_coordinates.classifier or "",
"{scope}": unpacked_coordinates.scope or "compile",
}
+ for key in exclusions:
+ if key not in versioned_dep_coordinates and key not in unversioned_dep_coordinates:
+ fail("Key %s in exclusions does not occur in versioned_dep_coordinates or unversioned_dep_coordinates" % key)
+
if parent:
# We only want the groupId, artifactID, and version
unpacked_parent = unpack_coordinates(parent)
@@ -114,18 +149,18 @@ def generate_pom(
substitutions.update({"{parent}": "".join(parts)})
deps = []
- for dep in sorted(versioned_dep_coordinates) + sorted(unversioned_dep_coordinates):
+ for dep in _sort_unpacked(versioned_dep_coordinates) + _sort_unpacked(unversioned_dep_coordinates):
include_version = dep in versioned_dep_coordinates
- unpacked = unpack_coordinates(dep)
- new_scope = "runtime" if dep in runtime_deps else unpacked.scope
+ new_scope = "runtime" if dep in runtime_deps else dep.scope
unpacked = struct(
- groupId = unpacked.groupId,
- artifactId = unpacked.artifactId,
- type = unpacked.type,
+ groupId = dep.groupId,
+ artifactId = dep.artifactId,
+ type = dep.type,
scope = new_scope,
- version = unpacked.version,
+ classifier = dep.classifier,
+ version = dep.version,
)
- deps.append(format_dep(unpacked, indent = indent, include_version = include_version))
+ deps.append(format_dep(unpacked, indent = indent, exclusions = exclusions.get(dep, {}), include_version = include_version))
substitutions.update({"{dependencies}": "\n".join(deps)})
@@ -158,3 +193,11 @@ def determine_additional_dependencies(jar_files, additional_dependencies):
to_return.append(dep)
return to_return
+
+def _sort_unpacked(unpacked_dep):
+ """Sorts a list of unpacked dependencies by groupId, artifactId, and version."""
+
+ def _sort_key(dep):
+ return (dep.groupId, dep.artifactId, dep.version)
+
+ return sorted(unpacked_dep, key = _sort_key)
diff --git a/private/rules/pom_file.bzl b/private/rules/pom_file.bzl
index 9a1177d2..5075a83e 100644
--- a/private/rules/pom_file.bzl
+++ b/private/rules/pom_file.bzl
@@ -1,6 +1,6 @@
load("@rules_java//java:defs.bzl", "JavaInfo")
load(":has_maven_deps.bzl", "MavenInfo", "calculate_artifact_jars", "has_maven_deps")
-load(":maven_utils.bzl", "determine_additional_dependencies", "generate_pom")
+load(":maven_utils.bzl", "determine_additional_dependencies", "generate_pom", "unpack_coordinates")
def _pom_file_impl(ctx):
# Ensure the target has coordinates
@@ -12,6 +12,18 @@ def _pom_file_impl(ctx):
artifact_jars = calculate_artifact_jars(info)
additional_deps = determine_additional_dependencies(artifact_jars, ctx.attr.additional_dependencies)
+ def get_exclusion_coordinates(target):
+ if not info.label_to_javainfo.get(target.label):
+ fail("exclusions key %s not found in dependencies %s" % (target, info.label_to_javainfo.keys()))
+ else:
+ coords = ctx.expand_make_variables("exclusions", target[MavenInfo].coordinates, ctx.var)
+ return unpack_coordinates(coords)
+
+ exclusions = {
+ get_exclusion_coordinates(target): json.decode(targetExclusions)
+ for target, targetExclusions in ctx.attr.exclusions.items()
+ }
+
all_maven_deps = info.maven_deps.to_list()
runtime_maven_deps = info.maven_runtime_deps.to_list()
@@ -20,11 +32,11 @@ def _pom_file_impl(ctx):
all_maven_deps.append(coords)
expanded_maven_deps = [
- ctx.expand_make_variables("additional_deps", coords, ctx.var)
+ unpack_coordinates(ctx.expand_make_variables("additional_deps", coords, ctx.var))
for coords in all_maven_deps
]
expanded_runtime_deps = [
- ctx.expand_make_variables("maven_runtime_deps", coords, ctx.var)
+ unpack_coordinates(ctx.expand_make_variables("maven_runtime_deps", coords, ctx.var))
for coords in runtime_maven_deps
]
@@ -34,10 +46,11 @@ def _pom_file_impl(ctx):
out = generate_pom(
ctx,
coordinates = coordinates,
- versioned_dep_coordinates = sorted(expanded_maven_deps),
+ versioned_dep_coordinates = expanded_maven_deps,
runtime_deps = expanded_runtime_deps,
pom_template = ctx.file.pom_template,
out_name = "%s.xml" % ctx.label.name,
+ exclusions = exclusions,
)
return [
@@ -62,7 +75,8 @@ The following substitutions are performed on the template file:
{type}: Replaced by the maven coordinates type, if present (defaults to "jar")
{scope}: Replaced by the maven coordinates type, if present (defaults to "compile")
{dependencies}: Replaced by a list of maven dependencies directly relied upon
- by java_library targets within the artifact.
+ by java_library targets within the artifact. Dependencies have exclusions
+ for any transitive dependencies that are occur in deploy_env.
""",
attrs = {
"pom_template": attr.label(
@@ -90,5 +104,12 @@ The following substitutions are performed on the template file:
has_maven_deps,
],
),
+ "exclusions": attr.label_keyed_string_dict(
+ doc = "Mapping of dependency labels to a list of exclusions (encoded as a json string). Each exclusion is a dict with a group and an artifact.",
+ allow_empty = True,
+ aspects = [
+ has_maven_deps,
+ ],
+ ),
},
)
diff --git a/specs.bzl b/specs.bzl
index b928e1ae..a57657b1 100644
--- a/specs.bzl
+++ b/specs.bzl
@@ -212,6 +212,12 @@ def _exclusion_spec_to_json(exclusion_spec):
"""
return "{ \"group\": \"" + exclusion_spec["group"] + "\", \"artifact\": \"" + exclusion_spec["artifact"] + "\" }"
+def _exclusion_spec_list_to_json(exclusion_spec):
+ """
+ Given a list of artifact exclusion spec, returns its json serialization.
+ """
+ return "[" + ", ".join([_exclusion_spec_to_json(spec) for spec in exclusion_spec]) + "]"
+
def _override_license_types_spec_to_json(override_license_types_spec):
"""
Given an override license types spec, returns the json serialization of the object.
@@ -248,6 +254,7 @@ json = struct(
write_repository_credentials_spec = _repository_credentials_spec_to_json,
write_repository_spec = _repository_spec_to_json,
write_exclusion_spec = _exclusion_spec_to_json,
+ write_exclusion_spec_list = _exclusion_spec_list_to_json,
write_override_license_types_spec = _override_license_types_spec_to_json,
write_artifact_spec = _artifact_spec_to_json,
)
diff --git a/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD
index 1d41b9d9..e45d8e95 100644
--- a/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD
+++ b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD
@@ -15,3 +15,19 @@ java_test(
artifact("com.google.guava:guava"),
],
)
+
+java_test(
+ name = "ResourceTest",
+ srcs = ["ResourceTest.java"],
+ test_class = "com.github.bazelbuild.rules_jvm_external.javadoc.ResourceTest",
+ deps = [
+ "//private/tools/java/com/github/bazelbuild/rules_jvm_external",
+ "//private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc",
+ "//tests/com/github/bazelbuild/rules_jvm_external:zip_utils",
+ artifact(
+ "junit:junit",
+ repository_name = "regression_testing_coursier",
+ ),
+ artifact("com.google.guava:guava"),
+ ],
+)
diff --git a/tests/integration/maven_bom/BUILD b/tests/integration/maven_bom/BUILD
index 354a7e18..8f93e800 100644
--- a/tests/integration/maven_bom/BUILD
+++ b/tests/integration/maven_bom/BUILD
@@ -37,6 +37,10 @@ java_export(
name = "one-dep",
srcs = ["OneDep.java"],
maven_coordinates = "com.example:one-dep:1.0.0",
+ visibility = [
+ ":__pkg__",
+ "//tests/integration/pom_file:__pkg__",
+ ],
deps = [
artifact("com.google.guava:guava"),
],
diff --git a/tests/integration/pom_file/BUILD b/tests/integration/pom_file/BUILD
new file mode 100644
index 00000000..73b3310e
--- /dev/null
+++ b/tests/integration/pom_file/BUILD
@@ -0,0 +1,67 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+load("//:defs.bzl", "artifact", "java_export", "maven_bom")
+
+java_export(
+ name = "pom-example",
+ srcs = ["PomExample.java"],
+ maven_coordinates = "com.example:app:1.0.0",
+ deps = [
+ "//tests/integration/maven_bom:one-dep",
+ ],
+)
+
+diff_test(
+ name = "validate-pom",
+ file1 = ":pom-example-pom",
+ file2 = "pom.golden.xml",
+)
+
+java_export(
+ name = "pom-example-with-runtime-dep",
+ srcs = ["PomExample.java"],
+ maven_coordinates = "com.example:app:1.0.0",
+ runtime_deps = [
+ "//tests/integration/maven_bom:one-dep",
+ ],
+ deps = [
+ "//tests/integration/maven_bom:one-dep",
+ artifact("com.google.guava:guava"),
+ ],
+)
+
+diff_test(
+ name = "validate-pom-with-runtime-dep",
+ file1 = ":pom-example-with-runtime-dep-pom",
+ file2 = "pom-with-runtime-dep.golden.xml",
+)
+
+java_export(
+ name = "pom-example-with-classifier-dep",
+ srcs = ["PomExample.java"],
+ maven_coordinates = "com.example:app:1.0.0",
+ runtime_deps = [
+ ":classifier-artifact-one",
+ ],
+ deps = [
+ ":classifier-artifact-one",
+ ":classifier-artifact-two",
+ ],
+)
+
+java_export(
+ name = "classifier-artifact-one",
+ srcs = ["ClassifierArtifactOne.java"],
+ maven_coordinates = "com.example:lib:jar:linux-x86:1.0.0",
+)
+
+java_export(
+ name = "classifier-artifact-two",
+ srcs = ["ClassifierArtifactTwo.java"],
+ maven_coordinates = "com.example:lib:jar:linux-arm64:1.0.0",
+)
+
+diff_test(
+ name = "validate-pom-example-with-classifier-dep",
+ file1 = ":pom-example-with-classifier-dep-pom",
+ file2 = "pom-with-classifier-dep.golden.xml",
+)
diff --git a/tests/integration/pom_file/ClassifierArtifactOne.java b/tests/integration/pom_file/ClassifierArtifactOne.java
new file mode 100644
index 00000000..16b98b89
--- /dev/null
+++ b/tests/integration/pom_file/ClassifierArtifactOne.java
@@ -0,0 +1,5 @@
+package tests.integration.pom_file;
+
+public class ClassifierArtifactOne {
+ // This space left blank intentionally
+}
diff --git a/tests/integration/pom_file/ClassifierArtifactTwo.java b/tests/integration/pom_file/ClassifierArtifactTwo.java
new file mode 100644
index 00000000..2be8fc4a
--- /dev/null
+++ b/tests/integration/pom_file/ClassifierArtifactTwo.java
@@ -0,0 +1,5 @@
+package tests.integration.pom_file;
+
+public class ClassifierArtifactTwo {
+ // This space left blank intentionally
+}
diff --git a/tests/integration/pom_file/PomExample.java b/tests/integration/pom_file/PomExample.java
new file mode 100644
index 00000000..275be457
--- /dev/null
+++ b/tests/integration/pom_file/PomExample.java
@@ -0,0 +1,9 @@
+package tests.integration.pom_file;
+
+public class PomExample {
+
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ }
+
+}
diff --git a/tests/integration/pom_file/pom-with-classifier-dep.golden.xml b/tests/integration/pom_file/pom-with-classifier-dep.golden.xml
new file mode 100755
index 00000000..5c72ee38
--- /dev/null
+++ b/tests/integration/pom_file/pom-with-classifier-dep.golden.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.example
+ app
+ 1.0.0
+
+
+
+ com.example
+ lib
+ 1.0.0
+ runtime
+ linux-x86
+
+
+ com.example
+ lib
+ 1.0.0
+ linux-arm64
+
+
+
diff --git a/tests/integration/pom_file/pom-with-runtime-dep.golden.xml b/tests/integration/pom_file/pom-with-runtime-dep.golden.xml
new file mode 100755
index 00000000..7de26c63
--- /dev/null
+++ b/tests/integration/pom_file/pom-with-runtime-dep.golden.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ com.example
+ app
+ 1.0.0
+
+
+
+ com.example
+ one-dep
+ 1.0.0
+ runtime
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+
+
+
diff --git a/tests/integration/pom_file/pom.golden.xml b/tests/integration/pom_file/pom.golden.xml
new file mode 100755
index 00000000..e40e11f6
--- /dev/null
+++ b/tests/integration/pom_file/pom.golden.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ com.example
+ app
+ 1.0.0
+
+
+
+ com.example
+ one-dep
+ 1.0.0
+
+
+
diff --git a/tests/unit/specs_test.bzl b/tests/unit/specs_test.bzl
index 858a2375..e1e970d3 100644
--- a/tests/unit/specs_test.bzl
+++ b/tests/unit/specs_test.bzl
@@ -194,6 +194,25 @@ def _exclusion_spec_to_json_test_impl(ctx):
exclusion_spec_to_json_test = unittest.make(_exclusion_spec_to_json_test_impl)
+def _exclusion_spec_list_to_json_test_impl(ctx):
+ env = unittest.begin(ctx)
+ asserts.equals(
+ env,
+ "[" +
+ "{ \"group\": \"org.eclipse.aether\", \"artifact\": \"aether-api\" }, " +
+ "{ \"group\": \"org.eclipse.aether\", \"artifact\": \"aether-util\" }" +
+ "]",
+ json.write_exclusion_spec_list(
+ [
+ {"group": "org.eclipse.aether", "artifact": "aether-api"},
+ {"group": "org.eclipse.aether", "artifact": "aether-util"},
+ ],
+ ),
+ )
+ return unittest.end(env)
+
+exclusion_spec_list_to_json_test = unittest.make(_exclusion_spec_list_to_json_test_impl)
+
def _override_license_types_to_json_test_impl(ctx):
env = unittest.begin(ctx)
asserts.equals(
@@ -394,6 +413,7 @@ def artifact_specs_test_suite():
repository_credentials_spec_to_json_test,
repository_spec_to_json_test,
exclusion_spec_to_json_test,
+ exclusion_spec_list_to_json_test,
override_license_types_spec_to_json_test,
artifact_spec_to_json_test,
)