Fix `java_single_jar` compatibility with Bazel 7 Rule extension APIs weren't enabled by default in Bazel 7, so the rule was broken after https://github.com/bazelbuild/rules_java/commit/37b099cfd27de53495aea44763a16cd23ff929df Also add a dummy target under `test/repo` to build as a regression test. Fixes https://github.com/bazelbuild/rules_java/issues/350 PiperOrigin-RevId: 866808173 Change-Id: I594f0194be02f935154585e68d091172140301e1
diff --git a/java/BUILD b/java/BUILD index 452c765..7888dab 100644 --- a/java/BUILD +++ b/java/BUILD
@@ -65,7 +65,9 @@ name = "java_single_jar", srcs = ["java_single_jar.bzl"], visibility = ["//visibility:public"], - deps = ["//java/common"], + deps = [ + "//java/bazel/rules", # copybara-use-repo-external-label + ], ) bzl_library(
diff --git a/java/bazel/rules/bazel_java_single_jar.bzl b/java/bazel/rules/bazel_java_single_jar.bzl new file mode 100644 index 0000000..922fb63 --- /dev/null +++ b/java/bazel/rules/bazel_java_single_jar.bzl
@@ -0,0 +1,21 @@ +# Copyright 2026 The Bazel Authors. All rights reserved. +# +# 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. +"""Bazel java_single_jar rule""" + +load("//java/common/rules:java_single_jar.bzl", _bazel_java_single_jar = "bazel_java_single_jar") + +def java_single_jar(*, name, **kwargs): + if "output" not in kwargs: + kwargs["output"] = name + ".jar" + _bazel_java_single_jar(name = name, **kwargs)
diff --git a/java/common/rules/BUILD b/java/common/rules/BUILD index 5bb5c2f..f6fde9d 100644 --- a/java/common/rules/BUILD +++ b/java/common/rules/BUILD
@@ -32,6 +32,13 @@ ) bzl_library( + name = "java_single_jar_bzl", + srcs = ["java_single_jar.bzl"], + visibility = ["//java:__subpackages__"], + deps = ["//java/common"], +) + +bzl_library( name = "core_rules", srcs = [ "basic_java_library.bzl", @@ -81,6 +88,7 @@ srcs = [ "BUILD", ":core_rules", + ":java_single_jar_bzl", ":toolchain_rules", "//java/common/rules/impl:for_bazel_tests", "@rules_cc//cc/private/rules_impl:srcs",
diff --git a/java/common/rules/java_single_jar.bzl b/java/common/rules/java_single_jar.bzl new file mode 100644 index 0000000..95b7ca2 --- /dev/null +++ b/java/common/rules/java_single_jar.bzl
@@ -0,0 +1,167 @@ +# Copyright 2026 The Bazel Authors. All rights reserved. +# +# 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. +"""Definition of the java_single_jar rule.""" + +load("//java/common:java_common.bzl", "java_common") +load("//java/common:java_info.bzl", "JavaInfo") + +# copybara: default visibility + +def _single_jar_inputs(deps, deploy_env): + transitive_inputs = [] + for dep in deps: + if JavaInfo in dep: + info = dep[JavaInfo] + transitive_inputs.append(info.transitive_runtime_jars) + if hasattr(info, "compilation_info"): + compilation_info = info.compilation_info + if hasattr(compilation_info, "runtime_classpath"): + transitive_inputs.append(compilation_info.runtime_classpath) + else: + files = [] + for f in dep[DefaultInfo].files.to_list(): + if not f.extension == "jar": + 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) + + if hasattr(java_common, "JavaRuntimeClasspathInfo"): + deploy_env_jars = depset(transitive = [ + dep[java_common.JavaRuntimeClasspathInfo].runtime_classpath + for dep in deploy_env + ]) + 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 + +def _bazel_java_single_jar_impl(ctx): + 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.add_all("--deploy_manifest_lines", ctx.attr.deploy_manifest_lines) + args.add("--output", ctx.outputs.output) + args.add("--normalize") + + # Deal with limitation of singlejar flags: tool's default behavior is + # "no", but you get that behavior only by absence of compression flags. + if ctx.attr.compress == "preserve": + args.add("--dont_change_compression") + elif ctx.attr.compress == "yes": + args.add("--compression") + elif ctx.attr.compress == "no": + pass + else: + fail("\"compress\" attribute (%s) must be: yes, no, preserve." % ctx.attr.compress) + + if ctx.attr.exclude_build_data: + args.add("--exclude_build_data") + if ctx.attr.multi_release: + args.add("--multi_release") + + if ctx.attr.exclude_pattern: + args.add("--exclude_pattern", ctx.attr.exclude_pattern) + + ctx.actions.run( + inputs = inputs, + outputs = [ctx.outputs.output], + arguments = [args], + progress_message = "Merging into %s" % ctx.outputs.output.short_path, + mnemonic = "JavaSingleJar", + executable = ctx.executable._singlejar, + use_default_shell_env = True, + ) + + files = depset([ctx.outputs.output]) + providers = [DefaultInfo( + files = files, + runfiles = ctx.runfiles(transitive_files = files), + )] + if hasattr(java_common, "JavaRuntimeClasspathInfo"): + providers.append(java_common.JavaRuntimeClasspathInfo(runtime_classpath = inputs)) + return providers + +bazel_java_single_jar = rule( + attrs = { + "deps": attr.label_list( + allow_files = True, + doc = """ + The Java targets (including java_import and java_library) to collect + transitive dependencies from. Runtime dependencies are collected via + deps, exports, and runtime_deps. Resources are also collected. + Native cc_library or java_wrap_cc dependencies are not.""", + ), + "deploy_manifest_lines": attr.string_list(doc = """ + A list of lines to add to the <code>META-INF/manifest.mf</code> file."""), + "deploy_env": attr.label_list( + providers = [java_common.JavaRuntimeClasspathInfo] if hasattr(java_common, "JavaRuntimeClasspathInfo") else [], + allow_files = False, + doc = """ + A list of `java_binary` or `java_single_jar` targets which represent + the deployment environment for this binary. + + Set this attribute when building a plugin which will be loaded by another + `java_binary`. + + `deploy_env` dependencies are excluded from the jar built by this rule.""", + ), + "compress": attr.string(default = "preserve", doc = """ + Whether to always deflate ("yes"), always store ("no"), or pass + through unmodified ("preserve"). The default is "preserve", and is the + most efficient option -- no extra work is done to inflate or deflate."""), + "exclude_build_data": attr.bool(default = True, doc = """ + Whether to omit the build-data.properties file generated + by default."""), + "multi_release": attr.bool(default = True, doc = """Whether to enable Multi-Release output jars."""), + "exclude_pattern": attr.string(default = "", doc = """ + A regex pattern of files to exclude from the jar. + """), + "_singlejar": attr.label( + default = Label("//toolchains:singlejar"), + cfg = "exec", + allow_single_file = True, + executable = True, + ), + "output": attr.output(), + }, + implementation = _bazel_java_single_jar_impl, + doc = """ +Collects Java dependencies and jar files into a single jar + +`java_single_jar` collects Java dependencies and jar files into a single jar. +This is similar to java_binary with everything related to executables disabled, +and provides an alternative to the java_binary "deploy jar hack". + +## Example + +```skylark +load("//tools/build_defs/java_single_jar:java_single_jar.bzl", "java_single_jar") + +java_single_jar( + name = "my_single_jar", + deps = [ + "//java/com/google/foo", + "//java/com/google/bar", + ], +) +``` + +Outputs: + {name}.jar: A single jar containing all of the inputs. +""", +)
diff --git a/java/java_single_jar.bzl b/java/java_single_jar.bzl index bfdd21c..8477161 100644 --- a/java/java_single_jar.bzl +++ b/java/java_single_jar.bzl
@@ -1,158 +1,5 @@ -""" Definition of _java_single_jar. """ +"""The java_single_jar rule""" -load("//java/common:java_common.bzl", "java_common") -load("//java/common:java_info.bzl", "JavaInfo") +load("//java/bazel/rules:bazel_java_single_jar.bzl", _java_single_jar = "java_single_jar") -def _single_jar_inputs(deps, deploy_env): - transitive_inputs = [] - for dep in deps: - if JavaInfo in dep: - info = dep[JavaInfo] - transitive_inputs.append(info.transitive_runtime_jars) - if hasattr(info, "compilation_info"): - compilation_info = info.compilation_info - if hasattr(compilation_info, "runtime_classpath"): - transitive_inputs.append(compilation_info.runtime_classpath) - else: - files = [] - for f in dep[DefaultInfo].files.to_list(): - if not f.extension == "jar": - 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) - - if hasattr(java_common, "JavaRuntimeClasspathInfo"): - deploy_env_jars = depset(transitive = [ - dep[java_common.JavaRuntimeClasspathInfo].runtime_classpath - for dep in deploy_env - ]) - 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 - -def _java_single_jar(ctx): - 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.add_all("--deploy_manifest_lines", ctx.attr.deploy_manifest_lines) - args.add("--output", ctx.outputs.output) - args.add("--normalize") - - # Deal with limitation of singlejar flags: tool's default behavior is - # "no", but you get that behavior only by absence of compression flags. - if ctx.attr.compress == "preserve": - args.add("--dont_change_compression") - elif ctx.attr.compress == "yes": - args.add("--compression") - elif ctx.attr.compress == "no": - pass - else: - fail("\"compress\" attribute (%s) must be: yes, no, preserve." % ctx.attr.compress) - - if ctx.attr.exclude_build_data: - args.add("--exclude_build_data") - if ctx.attr.multi_release: - args.add("--multi_release") - - if ctx.attr.exclude_pattern: - args.add("--exclude_pattern", ctx.attr.exclude_pattern) - - ctx.actions.run( - inputs = inputs, - outputs = [ctx.outputs.output], - arguments = [args], - progress_message = "Merging into %s" % ctx.outputs.output.short_path, - mnemonic = "JavaSingleJar", - executable = ctx.executable._singlejar, - use_default_shell_env = True, - ) - - files = depset([ctx.outputs.output]) - providers = [DefaultInfo( - files = files, - runfiles = ctx.runfiles(transitive_files = files), - )] - if hasattr(java_common, "JavaRuntimeClasspathInfo"): - providers.append(java_common.JavaRuntimeClasspathInfo(runtime_classpath = inputs)) - return providers - -def _init(name, **kwargs): - if "output" not in kwargs: - kwargs["output"] = name + ".jar" - return kwargs - -java_single_jar = rule( - attrs = { - "deps": attr.label_list( - allow_files = True, - doc = """ - The Java targets (including java_import and java_library) to collect - transitive dependencies from. Runtime dependencies are collected via - deps, exports, and runtime_deps. Resources are also collected. - Native cc_library or java_wrap_cc dependencies are not.""", - ), - "deploy_manifest_lines": attr.string_list(doc = """ - A list of lines to add to the <code>META-INF/manifest.mf</code> file."""), - "deploy_env": attr.label_list( - providers = [java_common.JavaRuntimeClasspathInfo] if hasattr(java_common, "JavaRuntimeClasspathInfo") else [], - allow_files = False, - doc = """ - A list of `java_binary` or `java_single_jar` targets which represent - the deployment environment for this binary. - - Set this attribute when building a plugin which will be loaded by another - `java_binary`. - - `deploy_env` dependencies are excluded from the jar built by this rule.""", - ), - "compress": attr.string(default = "preserve", doc = """ - Whether to always deflate ("yes"), always store ("no"), or pass - through unmodified ("preserve"). The default is "preserve", and is the - most efficient option -- no extra work is done to inflate or deflate."""), - "exclude_build_data": attr.bool(default = True, doc = """ - Whether to omit the build-data.properties file generated - by default."""), - "multi_release": attr.bool(default = True, doc = """Whether to enable Multi-Release output jars."""), - "exclude_pattern": attr.string(default = "", doc = """ - A regex pattern of files to exclude from the jar. - """), - "_singlejar": attr.label( - default = Label("//toolchains:singlejar"), - cfg = "exec", - allow_single_file = True, - executable = True, - ), - "output": attr.output(), - }, - initializer = _init, - implementation = _java_single_jar, - doc = """ -Collects Java dependencies and jar files into a single jar - -`java_single_jar` collects Java dependencies and jar files into a single jar. -This is similar to java_binary with everything related to executables disabled, -and provides an alternative to the java_binary "deploy jar hack". - -## Example - -```skylark -load("//tools/build_defs/java_single_jar:java_single_jar.bzl", "java_single_jar") - -java_single_jar( - name = "my_single_jar", - deps = [ - "//java/com/google/foo", - "//java/com/google/bar", - ], -) -``` - -Outputs: - {name}.jar: A single jar containing all of the inputs. -""", -) +java_single_jar = _java_single_jar
diff --git a/test/repo/BUILD.bazel b/test/repo/BUILD.bazel index b0fb612..a251c7b 100644 --- a/test/repo/BUILD.bazel +++ b/test/repo/BUILD.bazel
@@ -1,4 +1,5 @@ load("@rules_java//java:defs.bzl", "java_binary", "java_library", "java_test") # copybara-use-repo-external-label +load("@rules_java//java:java_single_jar.bzl", "java_single_jar") # copybara-use-repo-external-label load("@rules_java//toolchains:default_java_toolchain.bzl", "NONPREBUILT_TOOLCHAIN_CONFIGURATION", "default_java_toolchain") # copybara-use-repo-external-label load("@rules_shell//shell:sh_test.bzl", "sh_test") @@ -27,6 +28,15 @@ ], ) +java_single_jar( + name = "uber", + testonly = True, + deps = [ + ":MyTest", + ":bin", + ], +) + genrule( name = "MakeVarGenruleTest", outs = ["MakeVarGenruleTestSuccess"],