Fix `java_single_jar` to propagate full transitive classpath and use shell param file format
`java_single_jar` previously set `runtime_classpath = inputs` in `JavaRuntimeClasspathInfo`, which only included the filtered outputs rather than the full transitive runtime dependencies (`all_inputs`). When `java_single_jar` targets were used in `deploy_env` of other deploy JAR rules, dependencies excluded from the inner JAR were not properly excluded from the outer JAR.
Also configure `args.set_param_file_format("shell")` (matching `java_binary_deploy_jar.bzl`) so arguments in `deploy_manifest_lines` containing whitespace or embedded newlines are properly quoted in param files.
Add unit tests for `deploy_manifest_lines`, transitive `deploy_env` exclusion, and `java_binary` consuming `java_single_jar` via `deploy_env`.
PiperOrigin-RevId: 971365057
Change-Id: I47df6dbe444d1e5d29fabc730ee4d68555332a25
diff --git a/java/common/rules/java_single_jar.bzl b/java/common/rules/java_single_jar.bzl
index ca76887..54e9f28 100644
--- a/java/common/rules/java_single_jar.bzl
+++ b/java/common/rules/java_single_jar.bzl
@@ -37,7 +37,8 @@
fail("unexpected file type in java_single_jar.deps: %s" % f.path)
files.append(f)
transitive_inputs.append(depset(files))
- inputs = depset(transitive = transitive_inputs)
+ all_inputs = depset(transitive = transitive_inputs)
+ inputs = all_inputs
if hasattr(java_common, "JavaRuntimeClasspathInfo"):
deploy_env_jars = depset(transitive = [
@@ -47,15 +48,15 @@
excluded_jars = {jar: None for jar in deploy_env_jars.to_list()}
if excluded_jars:
inputs = depset([jar for jar in inputs.to_list() if jar not in excluded_jars])
- return inputs
+ return inputs, all_inputs
def _bazel_java_single_jar_impl(ctx):
- inputs = _single_jar_inputs(ctx.attr.deps, ctx.attr.deploy_env)
+ inputs, all_inputs = _single_jar_inputs(ctx.attr.deps, ctx.attr.deploy_env)
args = ctx.actions.args()
args.add_all("--sources", inputs)
args.use_param_file("@%s")
- args.set_param_file_format("multiline")
+ args.set_param_file_format("shell")
args.add_all("--deploy_manifest_lines", ctx.attr.deploy_manifest_lines)
args.add("--output", ctx.outputs.output)
args.add("--normalize")
@@ -102,7 +103,7 @@
runfiles = ctx.runfiles(transitive_files = files),
)]
if hasattr(java_common, "JavaRuntimeClasspathInfo"):
- providers.append(java_common.JavaRuntimeClasspathInfo(runtime_classpath = inputs))
+ providers.append(java_common.JavaRuntimeClasspathInfo(runtime_classpath = all_inputs))
return providers
bazel_java_single_jar = rule(
diff --git a/test/java/common/rules/java_single_jar_tests.bzl b/test/java/common/rules/java_single_jar_tests.bzl
index 488aab3..7d0796a 100644
--- a/test/java/common/rules/java_single_jar_tests.bzl
+++ b/test/java/common/rules/java_single_jar_tests.bzl
@@ -3,6 +3,8 @@
load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", "util")
+load("//java:java_binary.bzl", "java_binary")
+load("//java:java_library.bzl", "java_library")
load("//java:java_single_jar.bzl", "java_single_jar")
load("//java/common:java_semantics.bzl", "semantics")
@@ -155,15 +157,131 @@
for f in targets.build_info[OutputGroupInfo].redacted_build_info_files.to_list()
])
+def _test_java_single_jar_deploy_manifest_lines(name):
+ util.helper_target(
+ java_single_jar,
+ name = name + "/jar",
+ deps = ["1.jar"],
+ deploy_manifest_lines = [
+ "Manifest-Entry-A: valueA",
+ "Manifest-Entry-B: line1,\n line2",
+ ],
+ )
+
+ analysis_test(
+ name = name,
+ impl = _test_java_single_jar_deploy_manifest_lines_impl,
+ target = name + "/jar",
+ )
+
+def _test_java_single_jar_deploy_manifest_lines_impl(env, target):
+ assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar")
+ assert_that_action.argv().contains_at_least([
+ "--deploy_manifest_lines",
+ "Manifest-Entry-A: valueA",
+ "Manifest-Entry-B: line1,\n line2",
+ ])
+
+def _test_java_single_jar_transitive_deploy_env(name):
+ util.helper_target(
+ java_library,
+ name = name + "_lib_a",
+ srcs = ["A.java"],
+ )
+ util.helper_target(
+ java_library,
+ name = name + "_lib_b",
+ srcs = ["B.java"],
+ )
+ util.helper_target(
+ java_library,
+ name = name + "_lib_c",
+ srcs = ["C.java"],
+ )
+ util.helper_target(
+ java_single_jar,
+ name = name + "_env1",
+ deps = [name + "_lib_a"],
+ )
+ util.helper_target(
+ java_single_jar,
+ name = name + "_inner",
+ deps = [name + "_lib_a", name + "_lib_b"],
+ deploy_env = [name + "_env1"],
+ )
+ util.helper_target(
+ java_single_jar,
+ name = name + "_outer",
+ deps = [name + "_lib_a", name + "_lib_b", name + "_lib_c"],
+ deploy_env = [name + "_inner"],
+ )
+
+ analysis_test(
+ name = name,
+ impl = _test_java_single_jar_transitive_deploy_env_impl,
+ target = name + "_outer",
+ )
+
+def _test_java_single_jar_transitive_deploy_env_impl(env, target):
+ assert_that_action = env.expect.that_target(target).action_named("JavaSingleJar")
+ assert_that_action.argv().contains_at_least([
+ "--sources",
+ "{bindir}/{package}/lib{test_name}_lib_c.jar",
+ "--output",
+ ])
+ assert_that_action.argv().not_contains("{bindir}/{package}/lib{test_name}_lib_a.jar")
+ assert_that_action.argv().not_contains("{bindir}/{package}/lib{test_name}_lib_b.jar")
+
+def _test_java_binary_deploy_env_with_java_single_jar(name):
+ util.helper_target(
+ java_library,
+ name = name + "_lib_a",
+ srcs = ["A.java"],
+ )
+ util.helper_target(
+ java_library,
+ name = name + "_lib_b",
+ srcs = ["B.java"],
+ )
+ util.helper_target(
+ java_single_jar,
+ name = name + "_single_jar",
+ deps = [name + "_lib_a"],
+ )
+ util.helper_target(
+ java_binary,
+ name = name + "_bin",
+ main_class = "Main",
+ runtime_deps = [name + "_lib_a", name + "_lib_b"],
+ deploy_env = [name + "_single_jar"],
+ )
+
+ analysis_test(
+ name = name,
+ attr_values = {"tags": ["min_bazel_8"]}, # the deploy jar was created by a separate rule in Bazel 7
+ impl = _test_java_binary_deploy_env_with_java_single_jar_impl,
+ target = name + "_bin",
+ )
+
+def _test_java_binary_deploy_env_with_java_single_jar_impl(env, target):
+ assert_that_action = env.expect.that_target(target).action_named("JavaDeployJar")
+ assert_that_action.inputs().contains_at_least([
+ "{package}/lib{test_name}_lib_b.jar",
+ ])
+ assert_that_action.inputs().not_contains("{package}/lib{test_name}_lib_a.jar")
+
def java_single_jar_tests(name):
test_suite(
name = name,
tests = [
_test_java_single_jar_basic,
+ _test_java_single_jar_deploy_manifest_lines,
_test_java_single_jar_force_enable_stamping,
_test_java_single_jar_force_disable_stamping,
_test_java_single_jar_stamping_enabled_build_data_excluded_fails,
_test_java_single_jar_stamp_attr_auto_stamp_flag_enabled,
_test_java_single_jar_stamp_attr_auto_stamp_flag_disabled,
+ _test_java_single_jar_transitive_deploy_env,
+ _test_java_binary_deploy_env_with_java_single_jar,
],
)