Copy the Bazel java rules out of builtins
PiperOrigin-RevId: 672907957
Change-Id: I8f14924015857a378ded08929b6966047f042d10
diff --git a/java/bazel/rules/bazel_java_binary.bzl b/java/bazel/rules/bazel_java_binary.bzl
new file mode 100644
index 0000000..9317cc2
--- /dev/null
+++ b/java/bazel/rules/bazel_java_binary.bzl
@@ -0,0 +1,576 @@
+# Copyright 2022 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_binary rule"""
+
+load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain")
+load("//java/common:java_info.bzl", "JavaInfo")
+load("//java/common:java_semantics.bzl", "semantics")
+load(
+ "//java/common/rules:android_lint.bzl",
+ "android_lint_subrule",
+)
+load("//java/common/rules:java_binary.bzl", "BASE_TEST_ATTRIBUTES", "BASIC_JAVA_BINARY_ATTRIBUTES", "basic_java_binary")
+load("//java/common/rules:java_binary_deploy_jar.bzl", "create_deploy_archives")
+load("//java/common/rules:java_helper.bzl", "helper")
+load("//java/common/rules:rule_util.bzl", "merge_attrs")
+load("//third_party/bazel_skylib/lib:paths.bzl", "paths")
+
+visibility("private")
+
+def _bazel_java_binary_impl(ctx):
+ return _bazel_base_binary_impl(ctx, is_test_rule_class = False) + helper.executable_providers(ctx)
+
+def _bazel_java_test_impl(ctx):
+ return _bazel_base_binary_impl(ctx, is_test_rule_class = True) + helper.test_providers(ctx)
+
+def _bazel_base_binary_impl(ctx, is_test_rule_class):
+ deps = _collect_all_targets_as_deps(ctx, classpath_type = "compile_only")
+ runtime_deps = _collect_all_targets_as_deps(ctx)
+
+ main_class = _check_and_get_main_class(ctx)
+ coverage_main_class = main_class
+ coverage_config = helper.get_coverage_config(ctx, _get_coverage_runner(ctx))
+ if coverage_config:
+ main_class = coverage_config.main_class
+
+ launcher_info = _get_launcher_info(ctx)
+
+ executable = _get_executable(ctx)
+
+ feature_config = helper.get_feature_config(ctx)
+ if feature_config:
+ strip_as_default = helper.should_strip_as_default(ctx, feature_config)
+ else:
+ # No C++ toolchain available.
+ strip_as_default = False
+
+ providers, default_info, jvm_flags = basic_java_binary(
+ ctx,
+ deps,
+ runtime_deps,
+ ctx.files.resources,
+ main_class,
+ coverage_main_class,
+ coverage_config,
+ launcher_info,
+ executable,
+ strip_as_default,
+ is_test_rule_class = is_test_rule_class,
+ )
+
+ if ctx.attr.use_testrunner:
+ if semantics.find_java_runtime_toolchain(ctx).version >= 17:
+ jvm_flags.append("-Djava.security.manager=allow")
+ test_class = ctx.attr.test_class if hasattr(ctx.attr, "test_class") else ""
+ if test_class == "":
+ test_class = helper.primary_class(ctx)
+ if test_class == None:
+ fail("cannot determine test class. You might want to rename the " +
+ " rule or add a 'test_class' attribute.")
+ jvm_flags.extend([
+ "-ea",
+ "-Dbazel.test_suite=" + helper.shell_escape(test_class),
+ ])
+
+ java_attrs = providers["InternalDeployJarInfo"].java_attrs
+
+ if executable:
+ _create_stub(ctx, java_attrs, launcher_info.launcher, executable, jvm_flags, main_class, coverage_main_class)
+
+ runfiles = default_info.runfiles
+
+ if executable:
+ runtime_toolchain = semantics.find_java_runtime_toolchain(ctx)
+ runfiles = runfiles.merge(ctx.runfiles(transitive_files = runtime_toolchain.files))
+
+ test_support = helper.get_test_support(ctx)
+ if test_support:
+ runfiles = runfiles.merge(test_support[DefaultInfo].default_runfiles)
+
+ providers["DefaultInfo"] = DefaultInfo(
+ files = default_info.files,
+ runfiles = runfiles,
+ executable = default_info.executable,
+ )
+
+ info = providers.pop("InternalDeployJarInfo")
+ create_deploy_archives(
+ ctx,
+ info.java_attrs,
+ launcher_info,
+ main_class,
+ coverage_main_class,
+ info.strip_as_default,
+ add_exports = info.add_exports,
+ add_opens = info.add_opens,
+ )
+
+ return providers.values()
+
+def _get_coverage_runner(ctx):
+ if ctx.configuration.coverage_enabled and ctx.attr.create_executable:
+ toolchain = semantics.find_java_toolchain(ctx)
+ runner = toolchain.jacocorunner
+ if not runner:
+ fail("jacocorunner not set in java_toolchain: %s" % toolchain.label)
+ runner_jar = runner.executable
+
+ # wrap the jar in JavaInfo so we can add it to deps for java_common.compile()
+ return JavaInfo(output_jar = runner_jar, compile_jar = runner_jar)
+
+ return None
+
+def _collect_all_targets_as_deps(ctx, classpath_type = "all"):
+ deps = helper.collect_all_targets_as_deps(ctx, classpath_type = classpath_type)
+
+ if classpath_type == "compile_only" and ctx.fragments.java.enforce_explicit_java_test_deps():
+ return deps
+
+ test_support = helper.get_test_support(ctx)
+ if test_support:
+ deps.append(test_support)
+ return deps
+
+def _check_and_get_main_class(ctx):
+ create_executable = ctx.attr.create_executable
+ main_class = _get_main_class(ctx)
+
+ if not create_executable and main_class:
+ fail("main class must not be specified when executable is not created")
+ if create_executable and not main_class:
+ if not ctx.attr.srcs:
+ fail("need at least one of 'main_class' or Java source files")
+ main_class = helper.primary_class(ctx)
+ if main_class == None:
+ fail("main_class was not provided and cannot be inferred: " +
+ "source path doesn't include a known root (java, javatests, src, testsrc)")
+
+ return _get_main_class(ctx)
+
+def _get_main_class(ctx):
+ if not ctx.attr.create_executable:
+ return None
+
+ main_class = _get_main_class_from_rule(ctx)
+
+ if main_class == "":
+ main_class = helper.primary_class(ctx)
+ return main_class
+
+def _get_main_class_from_rule(ctx):
+ main_class = ctx.attr.main_class
+ if main_class:
+ return main_class
+ if ctx.attr.use_testrunner:
+ return "com.google.testing.junit.runner.BazelTestRunner"
+ return main_class
+
+def _get_launcher_info(ctx):
+ launcher = helper.launcher_artifact_for_target(ctx)
+ return struct(
+ launcher = launcher,
+ unstripped_launcher = launcher,
+ runfiles = [],
+ runtime_jars = [],
+ jvm_flags = [],
+ classpath_resources = [],
+ )
+
+def _get_executable(ctx):
+ if not ctx.attr.create_executable:
+ return None
+ executable_name = ctx.label.name
+ if helper.is_target_platform_windows(ctx):
+ executable_name = executable_name + ".exe"
+
+ return ctx.actions.declare_file(executable_name)
+
+def _create_stub(ctx, java_attrs, launcher, executable, jvm_flags, main_class, coverage_main_class):
+ java_runtime_toolchain = semantics.find_java_runtime_toolchain(ctx)
+ java_executable = helper.get_java_executable(ctx, java_runtime_toolchain, launcher)
+ workspace_name = ctx.workspace_name
+ workspace_prefix = workspace_name + ("/" if workspace_name else "")
+ runfiles_enabled = helper.runfiles_enabled(ctx)
+ coverage_enabled = ctx.configuration.coverage_enabled
+
+ test_support = helper.get_test_support(ctx)
+ test_support_jars = test_support[JavaInfo].transitive_runtime_jars if test_support else depset()
+ classpath = depset(
+ transitive = [
+ java_attrs.runtime_classpath,
+ test_support_jars if ctx.fragments.java.enforce_explicit_java_test_deps() else depset(),
+ ],
+ )
+
+ if helper.is_target_platform_windows(ctx):
+ jvm_flags_for_launcher = []
+ for flag in jvm_flags:
+ jvm_flags_for_launcher.extend(ctx.tokenize(flag))
+ return _create_windows_exe_launcher(ctx, java_executable, classpath, main_class, jvm_flags_for_launcher, runfiles_enabled, executable)
+
+ if runfiles_enabled:
+ prefix = "" if helper.is_absolute_target_platform_path(ctx, java_executable) else "${JAVA_RUNFILES}/"
+ java_bin = "JAVABIN=${JAVABIN:-" + prefix + java_executable + "}"
+ else:
+ java_bin = "JAVABIN=${JAVABIN:-$(rlocation " + java_executable + ")}"
+
+ td = ctx.actions.template_dict()
+ td.add_joined(
+ "%classpath%",
+ classpath,
+ map_each = lambda file: _format_classpath_entry(runfiles_enabled, workspace_prefix, file),
+ join_with = ctx.configuration.host_path_separator,
+ format_joined = "\"%s\"",
+ allow_closure = True,
+ )
+
+ ctx.actions.expand_template(
+ template = ctx.file._stub_template,
+ output = executable,
+ substitutions = {
+ "%runfiles_manifest_only%": "" if runfiles_enabled else "1",
+ "%workspace_prefix%": workspace_prefix,
+ "%javabin%": java_bin,
+ "%needs_runfiles%": "0" if helper.is_absolute_target_platform_path(ctx, java_runtime_toolchain.java_executable_exec_path) else "1",
+ "%set_jacoco_metadata%": "",
+ "%set_jacoco_main_class%": "export JACOCO_MAIN_CLASS=" + coverage_main_class if coverage_enabled else "",
+ "%set_jacoco_java_runfiles_root%": "export JACOCO_JAVA_RUNFILES_ROOT=${JAVA_RUNFILES}/" + workspace_prefix if coverage_enabled else "",
+ "%java_start_class%": helper.shell_escape(main_class),
+ "%jvm_flags%": " ".join(jvm_flags),
+ },
+ computed_substitutions = td,
+ is_executable = True,
+ )
+ return executable
+
+def _format_classpath_entry(runfiles_enabled, workspace_prefix, file):
+ if runfiles_enabled:
+ return "${RUNPATH}" + file.short_path
+
+ return "$(rlocation " + paths.normalize(workspace_prefix + file.short_path) + ")"
+
+def _create_windows_exe_launcher(ctx, java_executable, classpath, main_class, jvm_flags_for_launcher, runfiles_enabled, executable):
+ launch_info = ctx.actions.args().use_param_file("%s", use_always = True).set_param_file_format("multiline")
+ launch_info.add("binary_type=Java")
+ launch_info.add(ctx.workspace_name, format = "workspace_name=%s")
+ launch_info.add("1" if runfiles_enabled else "0", format = "symlink_runfiles_enabled=%s")
+ launch_info.add(java_executable, format = "java_bin_path=%s")
+ launch_info.add(main_class, format = "java_start_class=%s")
+ launch_info.add_joined(classpath, map_each = _short_path, join_with = ";", format_joined = "classpath=%s", omit_if_empty = False)
+ launch_info.add_joined(jvm_flags_for_launcher, join_with = "\t", format_joined = "jvm_flags=%s", omit_if_empty = False)
+ launch_info.add(semantics.find_java_runtime_toolchain(ctx).java_home_runfiles_path, format = "jar_bin_path=%s/bin/jar.exe")
+
+ # TODO(b/295221112): Change to use the "launcher" attribute (only windows use a fixed _launcher attribute)
+ launcher_artifact = ctx.executable._launcher
+ ctx.actions.run(
+ executable = ctx.executable._windows_launcher_maker,
+ inputs = [launcher_artifact],
+ outputs = [executable],
+ arguments = [launcher_artifact.path, launch_info, executable.path],
+ use_default_shell_env = True,
+ )
+ return executable
+
+def _short_path(file):
+ return file.short_path
+
+def _compute_test_support(use_testrunner):
+ return Label(semantics.JAVA_TEST_RUNNER_LABEL) if use_testrunner else None
+
+def _make_binary_rule(implementation, *, doc, attrs, executable = False, test = False, initializer = None):
+ return rule(
+ implementation = implementation,
+ initializer = initializer,
+ doc = doc,
+ attrs = attrs,
+ executable = executable,
+ test = test,
+ fragments = ["cpp", "java"],
+ provides = [JavaInfo],
+ toolchains = [semantics.JAVA_TOOLCHAIN] + use_cc_toolchain() + (
+ [semantics.JAVA_RUNTIME_TOOLCHAIN] if executable or test else []
+ ),
+ # TODO(hvd): replace with filegroups?
+ outputs = {
+ "classjar": "%{name}.jar",
+ "sourcejar": "%{name}-src.jar",
+ "deploysrcjar": "%{name}_deploy-src.jar",
+ "deployjar": "%{name}_deploy.jar",
+ "unstrippeddeployjar": "%{name}_deploy.jar.unstripped",
+ },
+ exec_groups = {
+ "cpp_link": exec_group(toolchains = use_cc_toolchain()),
+ },
+ subrules = [android_lint_subrule],
+ )
+
+_BASE_BINARY_ATTRS = merge_attrs(
+ BASIC_JAVA_BINARY_ATTRIBUTES,
+ {
+ "resource_strip_prefix": attr.string(
+ doc = """
+The path prefix to strip from Java resources.
+<p>
+If specified, this path prefix is stripped from every file in the <code>resources</code>
+attribute. It is an error for a resource file not to be under this directory. If not
+specified (the default), the path of resource file is determined according to the same
+logic as the Java package of source files. For example, a source file at
+<code>stuff/java/foo/bar/a.txt</code> will be located at <code>foo/bar/a.txt</code>.
+</p>
+ """,
+ ),
+ "_test_support": attr.label(default = _compute_test_support),
+ "_launcher": attr.label(
+ cfg = "exec",
+ executable = True,
+ default = "@bazel_tools//tools/launcher:launcher",
+ ),
+ "_windows_launcher_maker": attr.label(
+ default = "@bazel_tools//tools/launcher:launcher_maker",
+ cfg = "exec",
+ executable = True,
+ ),
+ },
+)
+
+def make_java_binary(executable):
+ return _make_binary_rule(
+ _bazel_java_binary_impl,
+ doc = """
+<p>
+ Builds a Java archive ("jar file"), plus a wrapper shell script with the same name as the rule.
+ The wrapper shell script uses a classpath that includes, among other things, a jar file for each
+ library on which the binary depends. When running the wrapper shell script, any nonempty
+ <code>JAVABIN</code> environment variable will take precedence over the version specified via
+ Bazel's <code>--java_runtime_version</code> flag.
+</p>
+<p>
+ The wrapper script accepts several unique flags. Refer to
+ <code>//src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt</code>
+ for a list of configurable flags and environment variables accepted by the wrapper.
+</p>
+
+<h4 id="java_binary_implicit_outputs">Implicit output targets</h4>
+<ul>
+ <li><code><var>name</var>.jar</code>: A Java archive, containing the class files and other
+ resources corresponding to the binary's direct dependencies.</li>
+ <li><code><var>name</var>-src.jar</code>: An archive containing the sources ("source
+ jar").</li>
+ <li><code><var>name</var>_deploy.jar</code>: A Java archive suitable for deployment (only
+ built if explicitly requested).
+ <p>
+ Building the <code><<var>name</var>>_deploy.jar</code> target for your rule
+ creates a self-contained jar file with a manifest that allows it to be run with the
+ <code>java -jar</code> command or with the wrapper script's <code>--singlejar</code>
+ option. Using the wrapper script is preferred to <code>java -jar</code> because it
+ also passes the <a href="${link java_binary.jvm_flags}">JVM flags</a> and the options
+ to load native libraries.
+ </p>
+ <p>
+ The deploy jar contains all the classes that would be found by a classloader that
+ searched the classpath from the binary's wrapper script from beginning to end. It also
+ contains the native libraries needed for dependencies. These are automatically loaded
+ into the JVM at runtime.
+ </p>
+ <p>If your target specifies a <a href="#java_binary.launcher">launcher</a>
+ attribute, then instead of being a normal JAR file, the _deploy.jar will be a
+ native binary. This will contain the launcher plus any native (C++) dependencies of
+ your rule, all linked into a static binary. The actual jar file's bytes will be
+ appended to that native binary, creating a single binary blob containing both the
+ executable and the Java code. You can execute the resulting jar file directly
+ like you would execute any native binary.</p>
+ </li>
+ <li><code><var>name</var>_deploy-src.jar</code>: An archive containing the sources
+ collected from the transitive closure of the target. These will match the classes in the
+ <code>deploy.jar</code> except where jars have no matching source jar.</li>
+</ul>
+
+<p>
+It is good practice to use the name of the source file that is the main entry point of the
+application (minus the extension). For example, if your entry point is called
+<code>Main.java</code>, then your name could be <code>Main</code>.
+</p>
+
+<p>
+ A <code>deps</code> attribute is not allowed in a <code>java_binary</code> rule without
+ <a href="${link java_binary.srcs}"><code>srcs</code></a>; such a rule requires a
+ <a href="${link java_binary.main_class}"><code>main_class</code></a> provided by
+ <a href="${link java_binary.runtime_deps}"><code>runtime_deps</code></a>.
+</p>
+
+<p>The following code snippet illustrates a common mistake:</p>
+
+<pre class="code">
+<code class="lang-starlark">
+java_binary(
+ name = "DontDoThis",
+ srcs = [
+ <var>...</var>,
+ <code class="deprecated">"GeneratedJavaFile.java"</code>, # a generated .java file
+ ],
+ deps = [<code class="deprecated">":generating_rule",</code>], # rule that generates that file
+)
+</code>
+</pre>
+
+<p>Do this instead:</p>
+
+<pre class="code">
+<code class="lang-starlark">
+java_binary(
+ name = "DoThisInstead",
+ srcs = [
+ <var>...</var>,
+ ":generating_rule",
+ ],
+)
+</code>
+</pre>
+ """,
+ attrs = merge_attrs(
+ _BASE_BINARY_ATTRS,
+ ({} if executable else {
+ "args": attr.string_list(),
+ "output_licenses": attr.string_list(),
+ }),
+ ),
+ executable = executable,
+ )
+
+java_binary = make_java_binary(executable = True)
+
+def _java_test_initializer(**kwargs):
+ if "stamp" in kwargs and type(kwargs["stamp"]) == type(True):
+ kwargs["stamp"] = 1 if kwargs["stamp"] else 0
+ if "use_launcher" in kwargs and not kwargs["use_launcher"]:
+ kwargs["launcher"] = None
+ else:
+ # If launcher is not set or None, set it to config flag
+ if "launcher" not in kwargs or not kwargs["launcher"]:
+ kwargs["launcher"] = semantics.LAUNCHER_FLAG_LABEL
+ return kwargs
+
+java_test = _make_binary_rule(
+ _bazel_java_test_impl,
+ doc = """
+<p>
+A <code>java_test()</code> rule compiles a Java test. A test is a binary wrapper around your
+test code. The test runner's main method is invoked instead of the main class being compiled.
+</p>
+
+<h4 id="java_test_implicit_outputs">Implicit output targets</h4>
+<ul>
+ <li><code><var>name</var>.jar</code>: A Java archive.</li>
+ <li><code><var>name</var>_deploy.jar</code>: A Java archive suitable
+ for deployment. (Only built if explicitly requested.) See the description of the
+ <code><var>name</var>_deploy.jar</code> output from
+ <a href="#java_binary">java_binary</a> for more details.</li>
+</ul>
+
+<p>
+See the section on <code>java_binary()</code> arguments. This rule also
+supports all <a href="${link common-definitions#common-attributes-tests}">attributes common
+to all test rules (*_test)</a>.
+</p>
+
+<h4 id="java_test_examples">Examples</h4>
+
+<pre class="code">
+<code class="lang-starlark">
+
+java_library(
+ name = "tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/foo/base:testResources",
+ "//java/com/foo/testing/util",
+ ],
+)
+
+java_test(
+ name = "AllTests",
+ size = "small",
+ runtime_deps = [
+ ":tests",
+ "//util/mysql",
+ ],
+)
+</code>
+</pre>
+ """,
+ attrs = merge_attrs(
+ BASE_TEST_ATTRIBUTES,
+ _BASE_BINARY_ATTRS,
+ {
+ "_lcov_merger": attr.label(
+ cfg = "exec",
+ default = configuration_field(
+ fragment = "coverage",
+ name = "output_generator",
+ ),
+ ),
+ "_collect_cc_coverage": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ default = "@bazel_tools//tools/test:collect_cc_coverage",
+ ),
+ },
+ override_attrs = {
+ "use_testrunner": attr.bool(
+ default = True,
+ doc = semantics.DOCS.for_attribute("use_testrunner") + """
+<br/>
+You can use this to override the default
+behavior, which is to use test runner for
+<code>java_test</code> rules,
+and not use it for <code>java_binary</code> rules. It is unlikely
+you will want to do this. One use is for <code>AllTest</code>
+rules that are invoked by another rule (to set up a database
+before running the tests, for example). The <code>AllTest</code>
+rule must be declared as a <code>java_binary</code>, but should
+still use the test runner as its main entry point.
+
+The name of a test runner class can be overridden with <code>main_class</code> attribute.
+ """,
+ ),
+ "stamp": attr.int(
+ default = 0,
+ values = [-1, 0, 1],
+ doc = """
+Whether to encode build information into the binary. Possible values:
+<ul>
+<li>
+ <code>stamp = 1</code>: Always stamp the build information into the binary, even in
+ <a href="${link user-manual#flag--stamp}"><code>--nostamp</code></a> builds. <b>This
+ setting should be avoided</b>, since it potentially kills remote caching for the
+ binary and any downstream actions that depend on it.
+</li>
+<li>
+ <code>stamp = 0</code>: Always replace build information by constant values. This
+ gives good build result caching.
+</li>
+<li>
+ <code>stamp = -1</code>: Embedding of build information is controlled by the
+ <a href="${link user-manual#flag--stamp}"><code>--[no]stamp</code></a> flag.
+</li>
+</ul>
+<p>Stamped binaries are <em>not</em> rebuilt unless their dependencies change.</p>
+ """,
+ ),
+ },
+ remove_attrs = ["deploy_env"],
+ ),
+ test = True,
+ initializer = _java_test_initializer,
+)
diff --git a/java/bazel/rules/bazel_java_binary_nonexec.bzl b/java/bazel/rules/bazel_java_binary_nonexec.bzl
new file mode 100644
index 0000000..82a7643
--- /dev/null
+++ b/java/bazel/rules/bazel_java_binary_nonexec.bzl
@@ -0,0 +1,28 @@
+# Copyright 2022 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.
+
+"""Defines a java_binary rule class that is non-executable.
+
+There are three physical rule classes for java_binary and we want all of them
+to have a name string of "java_binary" because various tooling expects that.
+But we also need the rule classes to be defined in separate files. That way the
+hash of their bzl environments will be different. See http://b/226379109,
+specifically #20, for details.
+"""
+
+load(":bazel_java_binary.bzl", "make_java_binary")
+
+visibility("private")
+
+java_binary = make_java_binary(executable = False)
diff --git a/java/bazel/rules/bazel_java_binary_wrapper.bzl b/java/bazel/rules/bazel_java_binary_wrapper.bzl
new file mode 100644
index 0000000..31696bc
--- /dev/null
+++ b/java/bazel/rules/bazel_java_binary_wrapper.bzl
@@ -0,0 +1,45 @@
+# Copyright 2022 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.
+
+"""Macro encapsulating the java_binary implementation
+
+This is needed since the `executable` nature of the target must be computed from
+the supplied value of the `create_executable` attribute.
+"""
+
+load("//java/common:java_common.bzl", "java_common")
+load(
+ "//java/common/rules:java_binary_wrapper.bzl",
+ "register_java_binary_rules",
+ "register_legacy_java_binary_rules",
+)
+load(":bazel_java_binary.bzl", java_bin_exec = "java_binary")
+load(":bazel_java_binary_nonexec.bzl", java_bin_nonexec = "java_binary")
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+
+visibility("private")
+
+def java_binary(**kwargs):
+ if _java_common_internal.incompatible_disable_non_executable_java_binary():
+ register_java_binary_rules(
+ java_bin_exec,
+ **kwargs
+ )
+ else:
+ register_legacy_java_binary_rules(
+ java_bin_exec,
+ java_bin_nonexec,
+ **kwargs
+ )
diff --git a/java/common/java_semantics.bzl b/java/common/java_semantics.bzl
new file mode 100644
index 0000000..d8bf824
--- /dev/null
+++ b/java/common/java_semantics.bzl
@@ -0,0 +1,107 @@
+# Copyright 2021 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 Semantics"""
+
+load("@rules_cc//cc/common:cc_helper.bzl", "cc_helper")
+
+visibility(["//java/..."])
+
+def _find_java_toolchain(ctx):
+ return ctx.toolchains["@bazel_tools//tools/jdk:toolchain_type"].java
+
+def _find_java_runtime_toolchain(ctx):
+ return ctx.toolchains["@bazel_tools//tools/jdk:runtime_toolchain_type"].java_runtime
+
+def _get_default_resource_path(path, segment_extractor):
+ # Look for src/.../resources to match Maven repository structure.
+ segments = path.split("/")
+ for idx in range(0, len(segments) - 2):
+ if segments[idx] == "src" and segments[idx + 2] == "resources":
+ return "/".join(segments[idx + 3:])
+ java_segments = segment_extractor(path)
+ return "/".join(java_segments) if java_segments != None else path
+
+def _compatible_javac_options(*_args):
+ return depset()
+
+def _check_java_info_opens_exports():
+ pass
+
+_DOCS = struct(
+ ATTRS = {
+ "resources": """
+<p>
+If resources are specified, they will be bundled in the jar along with the usual
+<code>.class</code> files produced by compilation. The location of the resources inside
+of the jar file is determined by the project structure. Bazel first looks for Maven's
+<a href="https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html">standard directory layout</a>,
+(a "src" directory followed by a "resources" directory grandchild). If that is not
+found, Bazel then looks for the topmost directory named "java" or "javatests" (so, for
+example, if a resource is at <code><workspace root>/x/java/y/java/z</code>, the
+path of the resource will be <code>y/java/z</code>. This heuristic cannot be overridden,
+however, the <code>resource_strip_prefix</code> attribute can be used to specify a
+specific alternative directory for resource files.
+ """,
+ "use_testrunner": """
+Use the test runner (by default
+<code>com.google.testing.junit.runner.BazelTestRunner</code>) class as the
+main entry point for a Java program, and provide the test class
+to the test runner as a value of <code>bazel.test_suite</code>
+system property.
+ """,
+ },
+)
+
+tokenize_javacopts = cc_helper.tokenize
+
+PLATFORMS_ROOT = "@platforms//"
+
+semantics = struct(
+ JAVA_TOOLCHAIN_LABEL = "@bazel_tools//tools/jdk:current_java_toolchain",
+ JAVA_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:toolchain_type",
+ JAVA_TOOLCHAIN = config_common.toolchain_type("@bazel_tools//tools/jdk:toolchain_type", mandatory = True),
+ find_java_toolchain = _find_java_toolchain,
+ JAVA_RUNTIME_TOOLCHAIN_TYPE = "@bazel_tools//tools/jdk:runtime_toolchain_type",
+ JAVA_RUNTIME_TOOLCHAIN = config_common.toolchain_type("@bazel_tools//tools/jdk:runtime_toolchain_type", mandatory = True),
+ find_java_runtime_toolchain = _find_java_runtime_toolchain,
+ JAVA_PLUGINS_FLAG_ALIAS_LABEL = "@bazel_tools//tools/jdk:java_plugins_flag_alias",
+ EXTRA_SRCS_TYPES = [],
+ ALLOWED_RULES_IN_DEPS = [
+ "cc_binary", # NB: linkshared=1
+ "cc_library",
+ "genrule",
+ "genproto", # TODO(bazel-team): we should filter using providers instead (starlark rule).
+ "java_import",
+ "java_library",
+ "java_proto_library",
+ "java_lite_proto_library",
+ "proto_library",
+ "sh_binary",
+ "sh_library",
+ ],
+ ALLOWED_RULES_IN_DEPS_WITH_WARNING = [],
+ LINT_PROGRESS_MESSAGE = "Running Android Lint for: %{label}",
+ JAVA_STUB_TEMPLATE_LABEL = "@bazel_tools//tools/jdk:java_stub_template.txt",
+ BUILD_INFO_TRANSLATOR_LABEL = "@bazel_tools//tools/build_defs/build_info:java_build_info",
+ JAVA_TEST_RUNNER_LABEL = "@bazel_tools//tools/jdk:TestRunner",
+ IS_BAZEL = True,
+ get_default_resource_path = _get_default_resource_path,
+ compatible_javac_options = _compatible_javac_options,
+ LAUNCHER_FLAG_LABEL = Label("@bazel_tools//tools/jdk:launcher_flag_alias"),
+ PROGUARD_ALLOWLISTER_LABEL = "@bazel_tools//tools/jdk:proguard_whitelister",
+ check_java_info_opens_exports = _check_java_info_opens_exports,
+ DOCS = struct(
+ for_attribute = lambda name: _DOCS.ATTRS.get(name, ""),
+ ),
+)
diff --git a/java/common/rules/BUILD b/java/common/rules/BUILD
new file mode 100644
index 0000000..ffd0fb0
--- /dev/null
+++ b/java/common/rules/BUILD
@@ -0,0 +1 @@
+package(default_visibility = ["//visibility:public"])
diff --git a/java/common/rules/android_lint.bzl b/java/common/rules/android_lint.bzl
new file mode 100644
index 0000000..9d23681
--- /dev/null
+++ b/java/common/rules/android_lint.bzl
@@ -0,0 +1,145 @@
+# Copyright 2021 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.
+
+"""Creates the android lint action for java rules"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+load("//java/common/rules:java_helper.bzl", "helper")
+
+visibility(["//java/..."])
+
+def _tokenize_opts(opts_depset):
+ return helper.tokenize_javacopts(ctx = None, opts = opts_depset)
+
+def _android_lint_action(ctx, source_files, source_jars, compilation_info):
+ """
+ Creates an action that runs Android lint against Java source files.
+
+ You need to add `ANDROID_LINT_IMPLICIT_ATTRS` to any rule or aspect using this call.
+
+ To lint generated source jars (java_info.java_outputs.gen_source_jar)
+ add them to the `source_jar` parameter.
+
+ `compilation_info` parameter should supply the classpath and Javac options
+ that were used during Java compilation.
+
+ The Android lint tool is obtained from Java toolchain.
+
+ Args:
+ ctx: (RuleContext) Used to register the action.
+ source_files: (list[File]) A list of .java source files
+ source_jars: (list[File]) A list of .jar or .srcjar files containing
+ source files. It should also include generated source jars.
+ compilation_info: (struct) Information about compilation.
+
+ Returns:
+ (None|File) The Android lint output file or None if no source files were
+ present.
+ """
+
+ # assuming that linting is enabled for all java rules i.e.
+ # --experimental_limit_android_lint_to_android_constrained_java=false
+
+ # --experimental_run_android_lint_on_java_rules= is checked in basic_java_library.bzl
+
+ if not (source_files or source_jars):
+ return None
+
+ toolchain = semantics.find_java_toolchain(ctx)
+ java_runtime = toolchain.java_runtime
+ linter = toolchain._android_linter
+ if not linter:
+ # TODO(hvd): enable after enabling in tests
+ # fail("android linter not set in java_toolchain")
+ return None
+
+ args = ctx.actions.args()
+
+ executable = linter.tool.executable
+ transitive_inputs = []
+ if executable.extension != "jar":
+ tools = [linter.tool]
+ transitive_inputs.append(linter.data)
+ args_list = [args]
+ else:
+ jvm_args = ctx.actions.args()
+ jvm_args.add_all(toolchain.jvm_opt)
+ jvm_args.add_all(linter.jvm_opts)
+ jvm_args.add("-jar", executable)
+ executable = java_runtime.java_executable_exec_path
+ tools = [java_runtime.files, linter.tool.executable]
+ transitive_inputs.append(linter.data)
+ args_list = [jvm_args, args]
+
+ classpath = compilation_info.compilation_classpath
+
+ # TODO(hvd): get from toolchain if we need this - probably android only
+ bootclasspath_aux = []
+ if bootclasspath_aux:
+ classpath = depset(transitive = [classpath, bootclasspath_aux])
+ transitive_inputs.append(classpath)
+
+ bootclasspath = toolchain.bootclasspath
+ transitive_inputs.append(bootclasspath)
+
+ transitive_inputs.append(compilation_info.plugins.processor_jars)
+ transitive_inputs.append(compilation_info.plugins.processor_data)
+ args.add_all("--sources", source_files)
+ args.add_all("--source_jars", source_jars)
+ args.add_all("--bootclasspath", bootclasspath)
+ args.add_all("--classpath", classpath)
+ args.add_all("--lint_rules", compilation_info.plugins.processor_jars)
+ args.add("--target_label", ctx.label)
+
+ javac_opts = compilation_info.javac_options
+ if javac_opts:
+ # wrap in a list so that map_each passes the depset to _tokenize_opts
+ args.add_all("--javacopts", [javac_opts], map_each = _tokenize_opts)
+ args.add("--")
+
+ args.add("--lintopts")
+ args.add_all(linter.lint_opts)
+
+ for package_config in linter.package_config:
+ if package_config.matches(ctx.label):
+ # wrap in a list so that map_each passes the depset to _tokenize_opts
+ package_opts = [package_config.javac_opts]
+ args.add_all(package_opts, map_each = _tokenize_opts)
+ transitive_inputs.append(package_config.data)
+
+ android_lint_out = ctx.actions.declare_file("%s_android_lint_output.xml" % ctx.label.name)
+ args.add("--xml", android_lint_out)
+
+ args.set_param_file_format(format = "multiline")
+ args.use_param_file(param_file_arg = "@%s", use_always = True)
+ ctx.actions.run(
+ mnemonic = "AndroidLint",
+ progress_message = semantics.LINT_PROGRESS_MESSAGE,
+ executable = executable,
+ inputs = depset(
+ # TODO(b/213551463) benchmark using a transitive depset instead
+ source_files + source_jars,
+ transitive = transitive_inputs,
+ ),
+ outputs = [android_lint_out],
+ tools = tools,
+ arguments = args_list,
+ execution_requirements = {"supports-workers": "1"},
+ )
+ return android_lint_out
+
+android_lint_subrule = subrule(
+ implementation = _android_lint_action,
+ toolchains = [semantics.JAVA_TOOLCHAIN_TYPE],
+)
diff --git a/java/common/rules/basic_java_library.bzl b/java/common/rules/basic_java_library.bzl
new file mode 100644
index 0000000..221baca
--- /dev/null
+++ b/java/common/rules/basic_java_library.bzl
@@ -0,0 +1,282 @@
+# Copyright 2021 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.
+
+"""
+Common code for reuse across java_* rules
+"""
+
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//java/common:java_common.bzl", "java_common")
+load("//java/common:java_info.bzl", "JavaInfo")
+load("//java/common:java_plugin_info.bzl", "JavaPluginInfo")
+load("//java/common:java_semantics.bzl", "semantics")
+load(":android_lint.bzl", "android_lint_subrule")
+load(":compile_action.bzl", "compile_action")
+load(":proguard_validation.bzl", "validate_proguard_specs")
+load(":rule_util.bzl", "merge_attrs")
+
+visibility([
+ "//java/...",
+])
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+BootClassPathInfo = java_common.BootClassPathInfo
+target_kind = _java_common_internal.target_kind
+
+def _filter_srcs(srcs, ext):
+ return [f for f in srcs if f.extension == ext]
+
+def _filter_provider(provider, *attrs):
+ return [dep[provider] for attr in attrs for dep in attr if provider in dep]
+
+# TODO(b/11285003): disallow jar files in deps, require java_import instead
+def _filter_javainfo_and_legacy_jars(attr):
+ dep_list = []
+
+ # Native code collected data into a NestedSet, using add for legacy jars and
+ # addTransitive for JavaInfo. This resulted in legacy jars being first in the list.
+ for dep in attr:
+ kind = target_kind(dep)
+ if not JavaInfo in dep or kind == "java_binary" or kind == "java_test":
+ for file in dep[DefaultInfo].files.to_list():
+ if file.extension == "jar":
+ # Native doesn't construct JavaInfo
+ java_info = JavaInfo(output_jar = file, compile_jar = file)
+ dep_list.append(java_info)
+
+ for dep in attr:
+ if JavaInfo in dep:
+ dep_list.append(dep[JavaInfo])
+ return dep_list
+
+def basic_java_library(
+ ctx,
+ srcs,
+ deps = [],
+ runtime_deps = [],
+ plugins = [],
+ exports = [],
+ exported_plugins = [],
+ resources = [],
+ resource_jars = [],
+ classpath_resources = [],
+ javacopts = [],
+ neverlink = False,
+ enable_compile_jar_action = True,
+ coverage_config = None,
+ proguard_specs = None,
+ add_exports = [],
+ add_opens = [],
+ bootclasspath = None,
+ javabuilder_jvm_flags = None):
+ """
+ Creates actions that compile and lint Java sources, sets up coverage and returns JavaInfo, InstrumentedFilesInfo and output groups.
+
+ The call creates actions and providers needed and shared by `java_library`,
+ `java_plugin`,`java_binary`, and `java_test` rules and it is primarily
+ intended to be used in those rules.
+
+ Before compilation coverage.runner is added to the dependencies and if
+ present plugins are extended with the value of `--plugin` flag.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ srcs: (list[File]) The list of source files that are processed to create the target.
+ deps: (list[Target]) The list of other libraries to be linked in to the target.
+ runtime_deps: (list[Target]) Libraries to make available to the final binary or test at runtime only.
+ plugins: (list[Target]) Java compiler plugins to run at compile-time.
+ exports: (list[Target]) Exported libraries.
+ exported_plugins: (list[Target]) The list of `java_plugin`s (e.g. annotation
+ processors) to export to libraries that directly depend on this library.
+ resources: (list[File]) A list of data files to include in a Java jar.
+ resource_jars: (list[File]) A list of jar files to unpack and include in a
+ Java jar.
+ classpath_resources: (list[File])
+ javacopts: (list[str])
+ neverlink: (bool) Whether this library should only be used for compilation and not at runtime.
+ enable_compile_jar_action: (bool) Enables header compilation or ijar creation.
+ coverage_config: (struct{runner:JavaInfo, support_files:list[File]|depset[File], env:dict[str,str]})
+ Coverage configuration. `runner` is added to dependencies during
+ compilation, `support_files` and `env` is returned in InstrumentedFilesInfo.
+ proguard_specs: (list[File]) Files to be used as Proguard specification.
+ Proguard validation is done only when the parameter is set.
+ add_exports: (list[str]) Allow this library to access the given <module>/<package>.
+ add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
+ bootclasspath: (Target) The JDK APIs to compile this library against.
+ javabuilder_jvm_flags: (list[str]) Additional JVM flags to pass to JavaBuilder.
+ Returns:
+ (dict[str, Provider],
+ {files_to_build: list[File],
+ runfiles: list[File],
+ output_groups: dict[str,list[File]]})
+ """
+ source_files = _filter_srcs(srcs, "java")
+ source_jars = _filter_srcs(srcs, "srcjar")
+
+ plugins_javaplugininfo = _collect_plugins(plugins)
+ plugins_javaplugininfo.append(ctx.attr._java_plugins[JavaPluginInfo])
+
+ properties = _filter_srcs(srcs, "properties")
+ if properties:
+ resources = list(resources)
+ resources.extend(properties)
+
+ java_info, compilation_info = compile_action(
+ ctx,
+ ctx.outputs.classjar,
+ ctx.outputs.sourcejar,
+ source_files,
+ source_jars,
+ collect_deps(deps) + ([coverage_config.runner] if coverage_config and coverage_config.runner else []),
+ collect_deps(runtime_deps),
+ plugins_javaplugininfo,
+ collect_deps(exports),
+ _collect_plugins(exported_plugins),
+ resources,
+ resource_jars,
+ classpath_resources,
+ _collect_native_libraries(deps, runtime_deps, exports),
+ javacopts,
+ neverlink,
+ ctx.fragments.java.strict_java_deps,
+ enable_compile_jar_action,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ bootclasspath = bootclasspath[BootClassPathInfo] if bootclasspath else None,
+ javabuilder_jvm_flags = javabuilder_jvm_flags,
+ )
+ target = {"JavaInfo": java_info}
+
+ output_groups = dict(
+ compilation_outputs = compilation_info.files_to_build,
+ _source_jars = java_info.transitive_source_jars,
+ _direct_source_jars = java_info.source_jars,
+ )
+
+ if ctx.fragments.java.run_android_lint:
+ generated_source_jars = [
+ output.generated_source_jar
+ for output in java_info.java_outputs
+ if output.generated_source_jar != None
+ ]
+ lint_output = android_lint_subrule(
+ source_files,
+ source_jars + generated_source_jars,
+ compilation_info,
+ )
+ if lint_output:
+ output_groups["_validation"] = [lint_output]
+
+ target["InstrumentedFilesInfo"] = coverage_common.instrumented_files_info(
+ ctx,
+ source_attributes = ["srcs"],
+ dependency_attributes = ["deps", "data", "resources", "resource_jars", "exports", "runtime_deps", "jars"],
+ coverage_support_files = coverage_config.support_files if coverage_config else depset(),
+ coverage_environment = coverage_config.env if coverage_config else {},
+ )
+
+ if proguard_specs != None:
+ target["ProguardSpecProvider"] = validate_proguard_specs(
+ ctx,
+ proguard_specs,
+ [deps, runtime_deps, exports],
+ )
+ output_groups["_hidden_top_level_INTERNAL_"] = target["ProguardSpecProvider"].specs
+
+ return target, struct(
+ files_to_build = compilation_info.files_to_build,
+ runfiles = compilation_info.runfiles,
+ output_groups = output_groups,
+ )
+
+def _collect_plugins(plugins):
+ """Collects plugins from an attribute.
+
+ Use this call to collect plugins from `plugins` or `exported_plugins` attribute.
+
+ The call simply extracts JavaPluginInfo provider.
+
+ Args:
+ plugins: (list[Target]) Attribute to collect plugins from.
+ Returns:
+ (list[JavaPluginInfo]) The plugins.
+ """
+ return _filter_provider(JavaPluginInfo, plugins)
+
+def collect_deps(deps):
+ """Collects dependencies from an attribute.
+
+ Use this call to collect plugins from `deps`, `runtime_deps`, or `exports` attribute.
+
+ The call extracts JavaInfo and additionaly also "legacy jars". "legacy jars"
+ are wrapped into a JavaInfo.
+
+ Args:
+ deps: (list[Target]) Attribute to collect dependencies from.
+ Returns:
+ (list[JavaInfo]) The dependencies.
+ """
+ return _filter_javainfo_and_legacy_jars(deps)
+
+def _collect_native_libraries(*attrs):
+ """Collects native libraries from a list of attributes.
+
+ Use this call to collect native libraries from `deps`, `runtime_deps`, or `exports` attributes.
+
+ The call simply extracts CcInfo provider.
+ Args:
+ *attrs: (*list[Target]) Attribute to collect native libraries from.
+ Returns:
+ (list[CcInfo]) The native library dependencies.
+ """
+ return _filter_provider(CcInfo, *attrs)
+
+def construct_defaultinfo(ctx, files_to_build, files, neverlink, *extra_attrs):
+ """Constructs DefaultInfo for Java library like rule.
+
+ Args:
+ ctx: (RuleContext) Used to construct the runfiles.
+ files_to_build: (list[File]) List of the files built by the rule.
+ files: (list[File]) List of the files include in runfiles.
+ neverlink: (bool) When true empty runfiles are constructed.
+ *extra_attrs: (list[Target]) Extra attributes to merge runfiles from.
+
+ Returns:
+ (DefaultInfo) DefaultInfo provider.
+ """
+ if neverlink:
+ runfiles = None
+ else:
+ runfiles = ctx.runfiles(files = files, collect_default = True)
+ runfiles = runfiles.merge_all([dep[DefaultInfo].default_runfiles for attr in extra_attrs for dep in attr])
+ default_info = DefaultInfo(
+ files = depset(files_to_build),
+ runfiles = runfiles,
+ )
+ return default_info
+
+BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS = merge_attrs(
+ {
+ "_java_plugins": attr.label(
+ default = semantics.JAVA_PLUGINS_FLAG_ALIAS_LABEL,
+ providers = [JavaPluginInfo],
+ ),
+ # TODO(b/245144242): Used by IDE integration, remove when toolchains are used
+ "_java_toolchain": attr.label(
+ default = semantics.JAVA_TOOLCHAIN_LABEL,
+ providers = [java_common.JavaToolchainInfo],
+ ),
+ "_use_auto_exec_groups": attr.bool(default = True),
+ },
+)
diff --git a/java/common/rules/compile_action.bzl b/java/common/rules/compile_action.bzl
new file mode 100644
index 0000000..d1c7f0e
--- /dev/null
+++ b/java/common/rules/compile_action.bzl
@@ -0,0 +1,176 @@
+# Copyright 2021 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.
+
+"""
+Java compile action
+"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+
+visibility("private")
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+_compile_private_for_builtins = _java_common_internal.compile
+
+def _filter_strict_deps(mode):
+ return "error" if mode in ["strict", "default"] else mode
+
+def _collect_plugins(deps, plugins):
+ transitive_processor_jars = []
+ transitive_processor_data = []
+ for plugin in plugins:
+ transitive_processor_jars.append(plugin.plugins.processor_jars)
+ transitive_processor_data.append(plugin.plugins.processor_data)
+ for dep in deps:
+ transitive_processor_jars.append(dep.plugins.processor_jars)
+ transitive_processor_data.append(dep.plugins.processor_data)
+ return struct(
+ processor_jars = depset(transitive = transitive_processor_jars),
+ processor_data = depset(transitive = transitive_processor_data),
+ )
+
+def compile_action(
+ ctx,
+ output_class_jar,
+ output_source_jar,
+ source_files = [],
+ source_jars = [],
+ deps = [],
+ runtime_deps = [],
+ plugins = [],
+ exports = [],
+ exported_plugins = [],
+ resources = [],
+ resource_jars = [],
+ classpath_resources = [],
+ native_libraries = [],
+ javacopts = [],
+ neverlink = False,
+ strict_deps = "ERROR",
+ enable_compile_jar_action = True,
+ add_exports = [],
+ add_opens = [],
+ bootclasspath = None,
+ javabuilder_jvm_flags = None):
+ """
+ Creates actions that compile Java sources, produce source jar, and produce header jar and returns JavaInfo.
+
+ Use this call when you need the most basic and consistent Java compilation.
+
+ Most parameters correspond to attributes on a java_library (srcs, deps,
+ plugins, resources ...) except they are more strict, for example:
+
+ - Where java_library's srcs attribute allows mixing of .java, .srcjar, and
+ .properties files the arguments accepted by this call should be strictly
+ separated into source_files, source_jars, and resources parameter.
+ - deps parameter accepts only JavaInfo providers and plugins parameter only
+ JavaPluginInfo
+
+ The call creates following actions and files:
+ - compiling Java sources to a class jar (output_class_jar parameter)
+ - a source jar (output_source_jar parameter)
+ - optionally a jar containing plugin generated classes when plugins are present
+ - optionally a jar containing plugin generated sources
+ - jdeps file containing dependencies used during compilation
+ - other files used to speed up incremental builds:
+ - a header jar - a jar containing only method signatures without implementation
+ - compile jdeps - dependencies used during header compilation
+
+ The returned JavaInfo provider may be used as a "fully-qualified" dependency
+ to a java_library.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ output_class_jar: (File) Output class .jar file. The file needs to be declared.
+ output_source_jar: (File) Output source .jar file. The file needs to be declared.
+ source_files: (list[File]) A list of .java source files to compile.
+ At least one of source_files or source_jars parameter must be specified.
+ source_jars: (list[File]) A list of .jar or .srcjar files containing
+ source files to compile.
+ At least one of source_files or source_jars parameter must be specified.
+ deps: (list[JavaInfo]) A list of dependencies.
+ runtime_deps: (list[JavaInfo]) A list of runtime dependencies.
+ plugins: (list[JavaPluginInfo]) A list of plugins.
+ exports: (list[JavaInfo]) A list of exports.
+ exported_plugins: (list[JavaInfo]) A list of exported plugins.
+ resources: (list[File]) A list of resources.
+ resource_jars: (list[File]) A list of jars to unpack.
+ classpath_resources: (list[File]) A list of classpath resources.
+ native_libraries: (list[CcInfo]) C++ native library dependencies that are
+ needed for this library.
+ javacopts: (list[str]) A list of the desired javac options. The options
+ may contain `$(location ..)` templates that will be expanded.
+ neverlink: (bool) Whether or not this library should be used only for
+ compilation and not at runtime.
+ strict_deps: (str) A string that specifies how to handle strict deps.
+ Possible values: 'OFF', 'ERROR', 'WARN' and 'DEFAULT'. For more details
+ see https://bazel.build/docs/user-manual#strict-java-deps.
+ By default 'ERROR'.
+ enable_compile_jar_action: (bool) Enables header compilation or ijar
+ creation. If set to False, it forces use of the full class jar in the
+ compilation classpaths of any dependants. Doing so is intended for use
+ by non-library targets such as binaries that do not have dependants.
+ add_exports: (list[str]) Allow this library to access the given <module>/<package>.
+ add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
+ bootclasspath: (BootClassPathInfo) The set of JDK APIs to compile this library against.
+ javabuilder_jvm_flags: (list[str]) Additional JVM flags to pass to JavaBuilder.
+
+ Returns:
+ ((JavaInfo, {files_to_build: list[File],
+ runfiles: list[File],
+ compilation_classpath: list[File],
+ plugins: {processor_jars,
+ processor_data: depset[File]}}))
+ A tuple with JavaInfo provider and additional compilation info.
+
+ Files_to_build may include an empty .jar file when there are no sources
+ or resources present, whereas runfiles in this case are empty.
+ """
+
+ java_info = _compile_private_for_builtins(
+ ctx,
+ output = output_class_jar,
+ java_toolchain = semantics.find_java_toolchain(ctx),
+ source_files = source_files,
+ source_jars = source_jars,
+ resources = resources,
+ resource_jars = resource_jars,
+ classpath_resources = classpath_resources,
+ plugins = plugins,
+ deps = deps,
+ native_libraries = native_libraries,
+ runtime_deps = runtime_deps,
+ exports = exports,
+ exported_plugins = exported_plugins,
+ javac_opts = [ctx.expand_location(opt) for opt in javacopts],
+ neverlink = neverlink,
+ output_source_jar = output_source_jar,
+ strict_deps = _filter_strict_deps(strict_deps),
+ enable_compile_jar_action = enable_compile_jar_action,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ bootclasspath = bootclasspath,
+ javabuilder_jvm_flags = javabuilder_jvm_flags,
+ )
+
+ compilation_info = struct(
+ files_to_build = [output_class_jar],
+ runfiles = [output_class_jar] if source_files or source_jars or resources else [],
+ # TODO(ilist): collect compile_jars from JavaInfo in deps & exports
+ compilation_classpath = java_info.compilation_info.compilation_classpath,
+ javac_options = java_info.compilation_info.javac_options,
+ plugins = _collect_plugins(deps, plugins),
+ )
+
+ return java_info, compilation_info
diff --git a/java/common/rules/import_deps_check.bzl b/java/common/rules/import_deps_check.bzl
new file mode 100644
index 0000000..1981f13
--- /dev/null
+++ b/java/common/rules/import_deps_check.bzl
@@ -0,0 +1,81 @@
+# Copyright 2022 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.
+
+"""Creates the import deps checker for java rules"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+
+visibility("private")
+
+def import_deps_check(
+ ctx,
+ jars_to_check,
+ declared_deps,
+ transitive_deps,
+ rule_class):
+ """
+ Creates actions that checks import deps for java rules.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ jars_to_check: (list[File]) A list of jars files to check.
+ declared_deps: (list[File]) A list of direct dependencies.
+ transitive_deps: (list[File]) A list of transitive dependencies.
+ rule_class: (String) Rule class.
+
+ Returns:
+ (File) Output file of the created action.
+ """
+ java_toolchain = semantics.find_java_toolchain(ctx)
+ deps_checker = java_toolchain._deps_checker
+ if deps_checker == None:
+ return None
+
+ jdeps_output = ctx.actions.declare_file("_%s/%s/jdeps.proto" % (rule_class, ctx.label.name))
+
+ args = ctx.actions.args()
+ args.add("-jar", deps_checker)
+ args.add_all(jars_to_check, before_each = "--input")
+ args.add_all(declared_deps, before_each = "--directdep")
+ args.add_all(
+ depset(order = "preorder", transitive = [declared_deps, transitive_deps]),
+ before_each = "--classpath_entry",
+ )
+ args.add_all(java_toolchain.bootclasspath, before_each = "--bootclasspath_entry")
+ args.add("--checking_mode=error")
+ args.add("--jdeps_output", jdeps_output)
+ args.add("--rule_label", ctx.label)
+
+ inputs = depset(
+ jars_to_check,
+ transitive = [
+ declared_deps,
+ transitive_deps,
+ java_toolchain.bootclasspath,
+ ],
+ )
+ tools = [deps_checker, java_toolchain.java_runtime.files]
+
+ ctx.actions.run(
+ mnemonic = "ImportDepsChecker",
+ progress_message = "Checking the completeness of the deps for %s" % jars_to_check,
+ executable = java_toolchain.java_runtime.java_executable_exec_path,
+ arguments = [args],
+ inputs = inputs,
+ outputs = [jdeps_output],
+ tools = tools,
+ toolchain = semantics.JAVA_TOOLCHAIN_TYPE,
+ )
+
+ return jdeps_output
diff --git a/java/common/rules/java_binary.bzl b/java/common/rules/java_binary.bzl
new file mode 100644
index 0000000..74a5ab9
--- /dev/null
+++ b/java/common/rules/java_binary.bzl
@@ -0,0 +1,821 @@
+# Copyright 2022 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.
+
+""" Implementation of java_binary for bazel """
+
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//java/common:java_common.bzl", "java_common")
+load("//java/common:java_info.bzl", "JavaInfo")
+load("//java/common:java_plugin_info.bzl", "JavaPluginInfo")
+load("//java/common:java_semantics.bzl", "PLATFORMS_ROOT", "semantics")
+load("//third_party/bazel_skylib/lib:paths.bzl", "paths")
+load("//third_party/protobuf/bazel/common:proto_info.bzl", "ProtoInfo")
+load(":basic_java_library.bzl", "BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS", "basic_java_library", "collect_deps")
+load(":java_binary_deploy_jar.bzl", "create_deploy_archive")
+load(":java_helper.bzl", "helper")
+load(":rule_util.bzl", "merge_attrs")
+
+visibility(["//java/..."])
+
+BootClassPathInfo = java_common.BootClassPathInfo
+CcLauncherInfo = cc_common.launcher_provider
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+JavaCompilationInfo = _java_common_internal.JavaCompilationInfo
+collect_native_deps_dirs = _java_common_internal.collect_native_deps_dirs
+get_runtime_classpath_for_archive = _java_common_internal.get_runtime_classpath_for_archive
+to_java_binary_info = _java_common_internal.to_java_binary_info
+
+InternalDeployJarInfo = provider(
+ "Provider for passing info to deploy jar rule",
+ fields = [
+ "java_attrs",
+ "strip_as_default",
+ "add_exports",
+ "add_opens",
+ ],
+)
+
+def basic_java_binary(
+ ctx,
+ deps,
+ runtime_deps,
+ resources,
+ main_class,
+ coverage_main_class,
+ coverage_config,
+ launcher_info,
+ executable,
+ strip_as_default,
+ extension_registry_provider = None,
+ is_test_rule_class = False):
+ """Creates actions for compiling and linting java sources, coverage support, and sources jar (_deploy-src.jar).
+
+ Args:
+ ctx: (RuleContext) The rule context
+ deps: (list[Target]) The list of other targets to be compiled with
+ runtime_deps: (list[Target]) The list of other targets to be linked in
+ resources: (list[File]) The list of data files to be included in the class jar
+ main_class: (String) FQN of the java main class
+ coverage_main_class: (String) FQN of the actual main class if coverage is enabled
+ coverage_config: (Struct|None) If coverage is enabled, a struct with fields (runner, manifest, env, support_files), None otherwise
+ launcher_info: (Struct) Structure with fields (launcher, unstripped_launcher, runfiles, runtime_jars, jvm_flags, classpath_resources)
+ executable: (File) The executable output of the rule
+ strip_as_default: (bool) Whether this target outputs a stripped launcher and deploy jar
+ extension_registry_provider: (GeneratedExtensionRegistryProvider) internal param, do not use
+ is_test_rule_class: (bool) Whether this rule is a test rule
+
+ Returns:
+ Tuple(
+ dict[str, Provider], // providers
+ Struct( // default info
+ files_to_build: depset(File),
+ runfiles: Runfiles,
+ executable: File
+ ),
+ list[String] // jvm flags
+ )
+
+ """
+ if not ctx.attr.create_executable and (ctx.attr.launcher and cc_common.launcher_provider in ctx.attr.launcher):
+ fail("launcher specified but create_executable is false")
+ if not ctx.attr.use_launcher and (ctx.attr.launcher and ctx.attr.launcher.label != semantics.LAUNCHER_FLAG_LABEL):
+ fail("launcher specified but use_launcher is false")
+
+ if not ctx.attr.srcs and ctx.attr.deps:
+ fail("deps not allowed without srcs; move to runtime_deps?")
+
+ module_flags = [dep[JavaInfo].module_flags_info for dep in runtime_deps if JavaInfo in dep]
+ add_exports = depset(ctx.attr.add_exports, transitive = [m.add_exports for m in module_flags])
+ add_opens = depset(ctx.attr.add_opens, transitive = [m.add_opens for m in module_flags])
+
+ classpath_resources = []
+ classpath_resources.extend(launcher_info.classpath_resources)
+ if hasattr(ctx.files, "classpath_resources"):
+ classpath_resources.extend(ctx.files.classpath_resources)
+
+ toolchain = semantics.find_java_toolchain(ctx)
+ timezone_data = [toolchain._timezone_data] if toolchain._timezone_data else []
+ target, common_info = basic_java_library(
+ ctx,
+ srcs = ctx.files.srcs,
+ deps = deps,
+ runtime_deps = runtime_deps,
+ plugins = ctx.attr.plugins,
+ resources = resources,
+ resource_jars = timezone_data,
+ classpath_resources = classpath_resources,
+ javacopts = ctx.attr.javacopts,
+ neverlink = ctx.attr.neverlink,
+ enable_compile_jar_action = False,
+ coverage_config = coverage_config,
+ add_exports = ctx.attr.add_exports,
+ add_opens = ctx.attr.add_opens,
+ bootclasspath = ctx.attr.bootclasspath,
+ )
+ java_info = target["JavaInfo"]
+ compilation_info = java_info.compilation_info
+ runtime_classpath = depset(
+ order = "preorder",
+ transitive = [
+ java_info.transitive_runtime_jars
+ for java_info in (
+ collect_deps(ctx.attr.runtime_deps + deps) +
+ ([coverage_config.runner] if coverage_config and coverage_config.runner else [])
+ )
+ ],
+ )
+ if extension_registry_provider:
+ runtime_classpath = depset(order = "preorder", direct = [extension_registry_provider.class_jar], transitive = [runtime_classpath])
+ java_info = java_common.merge(
+ [
+ java_info,
+ JavaInfo(
+ output_jar = extension_registry_provider.class_jar,
+ compile_jar = None,
+ source_jar = extension_registry_provider.src_jar,
+ ),
+ ],
+ )
+ compilation_info = JavaCompilationInfo(
+ compilation_classpath = compilation_info.compilation_classpath,
+ runtime_classpath = runtime_classpath,
+ boot_classpath = compilation_info.boot_classpath,
+ javac_options = compilation_info.javac_options,
+ )
+
+ java_attrs = _collect_attrs(ctx, runtime_classpath, classpath_resources)
+
+ jvm_flags = []
+
+ jvm_flags.extend(launcher_info.jvm_flags)
+
+ native_libs_depsets = []
+ for dep in runtime_deps:
+ if JavaInfo in dep:
+ native_libs_depsets.append(dep[JavaInfo].transitive_native_libraries)
+ if CcInfo in dep:
+ native_libs_depsets.append(dep[CcInfo].transitive_native_libraries())
+ native_libs_dirs = collect_native_deps_dirs(depset(transitive = native_libs_depsets))
+ if native_libs_dirs:
+ prefix = "${JAVA_RUNFILES}/" + ctx.workspace_name + "/"
+ jvm_flags.append("-Djava.library.path=%s" % (
+ ":".join([prefix + d for d in native_libs_dirs])
+ ))
+
+ jvm_flags.extend(ctx.fragments.java.default_jvm_opts)
+ jvm_flags.extend([ctx.expand_make_variables(
+ "jvm_flags",
+ ctx.expand_location(flag, ctx.attr.data, short_paths = True),
+ {},
+ ) for flag in ctx.attr.jvm_flags])
+
+ # TODO(cushon): make string formatting lazier once extend_template support is added
+ # https://github.com/bazelbuild/proposals#:~:text=2022%2D04%2D25,Starlark
+ jvm_flags.extend(["--add-exports=%s=ALL-UNNAMED" % x for x in add_exports.to_list()])
+ jvm_flags.extend(["--add-opens=%s=ALL-UNNAMED" % x for x in add_opens.to_list()])
+
+ files_to_build = []
+
+ if executable:
+ files_to_build.append(executable)
+
+ output_groups = common_info.output_groups
+
+ if coverage_config:
+ _generate_coverage_manifest(ctx, coverage_config.manifest, java_attrs.runtime_classpath)
+ files_to_build.append(coverage_config.manifest)
+
+ if extension_registry_provider:
+ files_to_build.append(extension_registry_provider.class_jar)
+ output_groups["_direct_source_jars"] = (
+ output_groups["_direct_source_jars"] + [extension_registry_provider.src_jar]
+ )
+ output_groups["_source_jars"] = depset(
+ direct = [extension_registry_provider.src_jar],
+ transitive = [output_groups["_source_jars"]],
+ )
+
+ if (ctx.fragments.java.one_version_enforcement_on_java_tests or not is_test_rule_class):
+ one_version_output = _create_one_version_check(ctx, java_attrs.runtime_classpath, is_test_rule_class)
+ else:
+ one_version_output = None
+
+ validation_outputs = [one_version_output] if one_version_output else []
+
+ _create_deploy_sources_jar(ctx, output_groups["_source_jars"])
+
+ files = depset(files_to_build + common_info.files_to_build)
+
+ transitive_runfiles_artifacts = depset(transitive = [
+ files,
+ java_attrs.runtime_classpath,
+ depset(transitive = launcher_info.runfiles),
+ ])
+
+ runfiles = ctx.runfiles(
+ transitive_files = transitive_runfiles_artifacts,
+ collect_default = True,
+ )
+
+ if launcher_info.launcher:
+ default_launcher = helper.filter_launcher_for_target(ctx)
+ default_launcher_artifact = helper.launcher_artifact_for_target(ctx)
+ default_launcher_runfiles = default_launcher[DefaultInfo].default_runfiles
+ if default_launcher_artifact == launcher_info.launcher:
+ runfiles = runfiles.merge(default_launcher_runfiles)
+ else:
+ # N.B. The "default launcher" referred to here is the launcher target specified through
+ # an attribute or flag. We wish to retain the runfiles of the default launcher, *except*
+ # for the original cc_binary artifact, because we've swapped it out with our custom
+ # launcher. Hence, instead of calling builder.addTarget(), or adding an odd method
+ # to Runfiles.Builder, we "unravel" the call and manually add things to the builder.
+ # Because the NestedSet representing each target's launcher runfiles is re-built here,
+ # we may see increased memory consumption for representing the target's runfiles.
+ runfiles = runfiles.merge(
+ ctx.runfiles(
+ files = [launcher_info.launcher],
+ transitive_files = depset([
+ file
+ for file in default_launcher_runfiles.files.to_list()
+ if file != default_launcher_artifact
+ ]),
+ symlinks = default_launcher_runfiles.symlinks,
+ root_symlinks = default_launcher_runfiles.root_symlinks,
+ ),
+ )
+
+ runfiles = runfiles.merge_all([
+ dep[DefaultInfo].default_runfiles
+ for dep in ctx.attr.runtime_deps
+ if DefaultInfo in dep
+ ])
+
+ if validation_outputs:
+ output_groups["_validation"] = output_groups.get("_validation", []) + validation_outputs
+
+ _filter_validation_output_group(ctx, output_groups)
+
+ java_binary_info = to_java_binary_info(java_info, compilation_info)
+
+ internal_deploy_jar_info = InternalDeployJarInfo(
+ java_attrs = java_attrs,
+ strip_as_default = strip_as_default,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ )
+
+ # "temporary" workaround for https://github.com/bazelbuild/intellij/issues/5845
+ extra_files = []
+ if is_test_rule_class and ctx.fragments.java.auto_create_java_test_deploy_jars():
+ extra_files.append(_auto_create_deploy_jar(ctx, internal_deploy_jar_info, launcher_info, main_class, coverage_main_class))
+
+ default_info = struct(
+ files = depset(extra_files, transitive = [files]),
+ runfiles = runfiles,
+ executable = executable,
+ )
+
+ return {
+ "OutputGroupInfo": OutputGroupInfo(**output_groups),
+ "JavaInfo": java_binary_info,
+ "InstrumentedFilesInfo": target["InstrumentedFilesInfo"],
+ "JavaRuntimeClasspathInfo": java_common.JavaRuntimeClasspathInfo(runtime_classpath = java_info.transitive_runtime_jars),
+ "InternalDeployJarInfo": internal_deploy_jar_info,
+ }, default_info, jvm_flags
+
+def _collect_attrs(ctx, runtime_classpath, classpath_resources):
+ deploy_env_jars = depset(transitive = [
+ dep[java_common.JavaRuntimeClasspathInfo].runtime_classpath
+ for dep in ctx.attr.deploy_env
+ ]) if hasattr(ctx.attr, "deploy_env") else depset()
+
+ runtime_classpath_for_archive = get_runtime_classpath_for_archive(runtime_classpath, deploy_env_jars)
+ runtime_jars = [ctx.outputs.classjar]
+
+ resources = [p for p in ctx.files.srcs if p.extension == "properties"]
+ transitive_resources = []
+ for r in ctx.attr.resources:
+ transitive_resources.append(
+ r[ProtoInfo].transitive_sources if ProtoInfo in r else r.files,
+ )
+
+ resource_names = dict()
+ for r in classpath_resources:
+ if r.basename in resource_names:
+ fail("entries must have different file names (duplicate: %s)" % r.basename)
+ resource_names[r.basename] = None
+
+ return struct(
+ runtime_jars = depset(runtime_jars),
+ runtime_classpath_for_archive = runtime_classpath_for_archive,
+ classpath_resources = depset(classpath_resources),
+ runtime_classpath = depset(order = "preorder", direct = runtime_jars, transitive = [runtime_classpath]),
+ resources = depset(resources, transitive = transitive_resources),
+ )
+
+def _generate_coverage_manifest(ctx, output, runtime_classpath):
+ ctx.actions.write(
+ output = output,
+ content = "\n".join([file.short_path for file in runtime_classpath.to_list()]),
+ )
+
+def _create_one_version_check(ctx, inputs, is_test_rule_class):
+ one_version_level = ctx.fragments.java.one_version_enforcement_level
+ if one_version_level == "OFF":
+ return None
+ tool = helper.check_and_get_one_version_attribute(ctx, "_one_version_tool")
+
+ if is_test_rule_class:
+ toolchain = semantics.find_java_toolchain(ctx)
+ allowlist = toolchain._one_version_allowlist_for_tests
+ else:
+ allowlist = helper.check_and_get_one_version_attribute(ctx, "_one_version_allowlist")
+
+ if not tool: # On Mac oneversion tool is not available
+ return None
+
+ output = ctx.actions.declare_file("%s-one-version.txt" % ctx.label.name)
+
+ args = ctx.actions.args()
+ args.set_param_file_format("shell").use_param_file("@%s", use_always = True)
+
+ one_version_inputs = []
+ args.add("--output", output)
+ if allowlist:
+ args.add("--whitelist", allowlist)
+ one_version_inputs.append(allowlist)
+ if one_version_level == "WARNING":
+ args.add("--succeed_on_found_violations")
+ args.add_all(
+ "--inputs",
+ inputs,
+ map_each = helper.jar_and_target_arg_mapper,
+ )
+
+ ctx.actions.run(
+ mnemonic = "JavaOneVersion",
+ progress_message = "Checking for one-version violations in %{label}",
+ executable = tool,
+ toolchain = semantics.JAVA_TOOLCHAIN_TYPE,
+ inputs = depset(one_version_inputs, transitive = [inputs]),
+ tools = [tool],
+ outputs = [output],
+ arguments = [args],
+ )
+
+ return output
+
+def _create_deploy_sources_jar(ctx, sources):
+ helper.create_single_jar(
+ ctx.actions,
+ toolchain = semantics.find_java_toolchain(ctx),
+ output = ctx.outputs.deploysrcjar,
+ sources = sources,
+ )
+
+def _filter_validation_output_group(ctx, output_group):
+ to_exclude = depset(transitive = [
+ dep[OutputGroupInfo]._validation
+ for dep in ctx.attr.deploy_env
+ if OutputGroupInfo in dep and hasattr(dep[OutputGroupInfo], "_validation")
+ ]) if hasattr(ctx.attr, "deploy_env") else depset()
+ if to_exclude:
+ transitive_validations = depset(transitive = [
+ _get_validations_from_attr(ctx, attr_name)
+ for attr_name in dir(ctx.attr)
+ # we also exclude implicit, cfg=host/exec and tool attributes
+ if not attr_name.startswith("_") and
+ attr_name not in [
+ "deploy_env",
+ "applicable_licenses",
+ "package_metadata",
+ "plugins",
+ "translations",
+ # special ignored attributes
+ "compatible_with",
+ "restricted_to",
+ "exec_compatible_with",
+ "target_compatible_with",
+ ]
+ ])
+ if not ctx.attr.create_executable:
+ excluded_set = {x: None for x in to_exclude.to_list()}
+ transitive_validations = [
+ x
+ for x in transitive_validations.to_list()
+ if x not in excluded_set
+ ]
+ output_group["_validation_transitive"] = transitive_validations
+
+def _get_validations_from_attr(ctx, attr_name):
+ attr = getattr(ctx.attr, attr_name)
+ if type(attr) == "list":
+ return depset(transitive = [_get_validations_from_target(t) for t in attr])
+ else:
+ return _get_validations_from_target(attr)
+
+def _get_validations_from_target(target):
+ if (
+ type(target) == "Target" and
+ OutputGroupInfo in target and
+ hasattr(target[OutputGroupInfo], "_validation")
+ ):
+ return target[OutputGroupInfo]._validation
+ else:
+ return depset()
+
+# TODO: bazelbuild/intellij/issues/5845 - remove this once no longer required
+# this need not be completely identical to the regular deploy jar since we only
+# care about packaging the classpath
+def _auto_create_deploy_jar(ctx, info, launcher_info, main_class, coverage_main_class):
+ output = ctx.actions.declare_file(ctx.label.name + "_auto_deploy.jar")
+ java_attrs = info.java_attrs
+ runtime_classpath = depset(
+ direct = launcher_info.runtime_jars,
+ transitive = [
+ java_attrs.runtime_jars,
+ java_attrs.runtime_classpath_for_archive,
+ ],
+ order = "preorder",
+ )
+ create_deploy_archive(
+ ctx,
+ launcher = launcher_info.launcher,
+ main_class = main_class,
+ coverage_main_class = coverage_main_class,
+ resources = java_attrs.resources,
+ classpath_resources = java_attrs.classpath_resources,
+ runtime_classpath = runtime_classpath,
+ manifest_lines = info.manifest_lines,
+ build_info_files = [],
+ build_target = str(ctx.label),
+ output = output,
+ one_version_level = ctx.fragments.java.one_version_enforcement_level,
+ one_version_allowlist = helper.check_and_get_one_version_attribute(ctx, "_one_version_allowlist"),
+ multi_release = ctx.fragments.java.multi_release_deploy_jars,
+ hermetic = hasattr(ctx.attr, "hermetic") and ctx.attr.hermetic,
+ add_exports = info.add_exports,
+ add_opens = info.add_opens,
+ )
+ return output
+
+BASIC_JAVA_BINARY_ATTRIBUTES = merge_attrs(
+ BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS,
+ # buildifier: disable=attr-licenses
+ {
+ "srcs": attr.label_list(
+ allow_files = [".java", ".srcjar", ".properties"] + semantics.EXTRA_SRCS_TYPES,
+ flags = ["DIRECT_COMPILE_TIME_INPUT", "ORDER_INDEPENDENT"],
+ doc = """
+The list of source files that are processed to create the target.
+This attribute is almost always required; see exceptions below.
+<p>
+Source files of type <code>.java</code> are compiled. In case of generated
+<code>.java</code> files it is generally advisable to put the generating rule's name
+here instead of the name of the file itself. This not only improves readability but
+makes the rule more resilient to future changes: if the generating rule generates
+different files in the future, you only need to fix one place: the <code>outs</code> of
+the generating rule. You should not list the generating rule in <code>deps</code>
+because it is a no-op.
+</p>
+<p>
+Source files of type <code>.srcjar</code> are unpacked and compiled. (This is useful if
+you need to generate a set of <code>.java</code> files with a genrule.)
+</p>
+<p>
+Rules: if the rule (typically <code>genrule</code> or <code>filegroup</code>) generates
+any of the files listed above, they will be used the same way as described for source
+files.
+</p>
+
+<p>
+This argument is almost always required, except if a
+<a href="#java_binary.main_class"><code>main_class</code></a> attribute specifies a
+class on the runtime classpath or you specify the <code>runtime_deps</code> argument.
+</p>
+ """,
+ ),
+ "deps": attr.label_list(
+ allow_files = [".jar"],
+ allow_rules = semantics.ALLOWED_RULES_IN_DEPS + semantics.ALLOWED_RULES_IN_DEPS_WITH_WARNING,
+ providers = [
+ [CcInfo],
+ [JavaInfo],
+ ],
+ flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
+ doc = """
+The list of other libraries to be linked in to the target.
+See general comments about <code>deps</code> at
+<a href="common-definitions.html#typical-attributes">Typical attributes defined by
+most build rules</a>.
+ """,
+ ),
+ "resources": attr.label_list(
+ allow_files = True,
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE", "ORDER_INDEPENDENT"],
+ doc = """
+A list of data files to include in a Java jar.
+
+<p>
+Resources may be source files or generated files.
+</p>
+ """ + semantics.DOCS.for_attribute("resources"),
+ ),
+ "runtime_deps": attr.label_list(
+ allow_files = [".jar"],
+ allow_rules = semantics.ALLOWED_RULES_IN_DEPS,
+ providers = [[CcInfo], [JavaInfo]],
+ flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
+ doc = """
+Libraries to make available to the final binary or test at runtime only.
+Like ordinary <code>deps</code>, these will appear on the runtime classpath, but unlike
+them, not on the compile-time classpath. Dependencies needed only at runtime should be
+listed here. Dependency-analysis tools should ignore targets that appear in both
+<code>runtime_deps</code> and <code>deps</code>.
+ """,
+ ),
+ "data": attr.label_list(
+ allow_files = True,
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
+ doc = """
+The list of files needed by this library at runtime.
+See general comments about <code>data</code>
+at <a href="${link common-definitions#typical-attributes}">Typical attributes defined by
+most build rules</a>.
+ """ + semantics.DOCS.for_attribute("data"),
+ ),
+ "plugins": attr.label_list(
+ providers = [JavaPluginInfo],
+ allow_files = True,
+ cfg = "exec",
+ doc = """
+Java compiler plugins to run at compile-time.
+Every <code>java_plugin</code> specified in this attribute will be run whenever this rule
+is built. A library may also inherit plugins from dependencies that use
+<code><a href="#java_library.exported_plugins">exported_plugins</a></code>. Resources
+generated by the plugin will be included in the resulting jar of this rule.
+ """,
+ ),
+ "deploy_env": attr.label_list(
+ providers = [java_common.JavaRuntimeClasspathInfo],
+ allow_files = False,
+ doc = """
+A list of other <code>java_binary</code> targets which represent the deployment
+environment for this binary.
+Set this attribute when building a plugin which will be loaded by another
+<code>java_binary</code>.<br/> Setting this attribute excludes all dependencies from
+the runtime classpath (and the deploy jar) of this binary that are shared between this
+binary and the targets specified in <code>deploy_env</code>.
+ """,
+ ),
+ "launcher": attr.label(
+ # TODO(b/295221112): add back CcLauncherInfo
+ allow_files = False,
+ doc = """
+Specify a binary that will be used to run your Java program instead of the
+normal <code>bin/java</code> program included with the JDK.
+The target must be a <code>cc_binary</code>. Any <code>cc_binary</code> that
+implements the
+<a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html">
+Java Invocation API</a> can be specified as a value for this attribute.
+
+<p>By default, Bazel will use the normal JDK launcher (bin/java or java.exe).</p>
+
+<p>The related <a href="${link user-manual#flag--java_launcher}"><code>
+--java_launcher</code></a> Bazel flag affects only those
+<code>java_binary</code> and <code>java_test</code> targets that have
+<i>not</i> specified a <code>launcher</code> attribute.</p>
+
+<p>Note that your native (C++, SWIG, JNI) dependencies will be built differently
+depending on whether you are using the JDK launcher or another launcher:</p>
+
+<ul>
+<li>If you are using the normal JDK launcher (the default), native dependencies are
+built as a shared library named <code>{name}_nativedeps.so</code>, where
+<code>{name}</code> is the <code>name</code> attribute of this java_binary rule.
+Unused code is <em>not</em> removed by the linker in this configuration.</li>
+
+<li>If you are using any other launcher, native (C++) dependencies are statically
+linked into a binary named <code>{name}_nativedeps</code>, where <code>{name}</code>
+is the <code>name</code> attribute of this java_binary rule. In this case,
+the linker will remove any code it thinks is unused from the resulting binary,
+which means any C++ code accessed only via JNI may not be linked in unless
+that <code>cc_library</code> target specifies <code>alwayslink = 1</code>.</li>
+</ul>
+
+<p>When using any launcher other than the default JDK launcher, the format
+of the <code>*_deploy.jar</code> output changes. See the main
+<a href="#java_binary">java_binary</a> docs for details.</p>
+ """,
+ ),
+ "bootclasspath": attr.label(
+ providers = [BootClassPathInfo],
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
+ doc = "Restricted API, do not use!",
+ ),
+ "neverlink": attr.bool(),
+ "javacopts": attr.string_list(
+ doc = """
+Extra compiler options for this binary.
+Subject to <a href="make-variables.html">"Make variable"</a> substitution and
+<a href="common-definitions.html#sh-tokenization">Bourne shell tokenization</a>.
+<p>These compiler options are passed to javac after the global compiler options.</p>
+ """,
+ ),
+ "add_exports": attr.string_list(
+ doc = """
+Allow this library to access the given <code>module</code> or <code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-exports= flags.
+ """,
+ ),
+ "add_opens": attr.string_list(
+ doc = """
+Allow this library to reflectively access the given <code>module</code> or
+<code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-opens= flags.
+ """,
+ ),
+ "main_class": attr.string(
+ doc = """
+Name of class with <code>main()</code> method to use as entry point.
+If a rule uses this option, it does not need a <code>srcs=[...]</code> list.
+Thus, with this attribute one can make an executable from a Java library that already
+contains one or more <code>main()</code> methods.
+<p>
+The value of this attribute is a class name, not a source file. The class must be
+available at runtime: it may be compiled by this rule (from <code>srcs</code>) or
+provided by direct or transitive dependencies (through <code>runtime_deps</code> or
+<code>deps</code>). If the class is unavailable, the binary will fail at runtime; there
+is no build-time check.
+</p>
+ """,
+ ),
+ "jvm_flags": attr.string_list(
+ doc = """
+A list of flags to embed in the wrapper script generated for running this binary.
+Subject to <a href="${link make-variables#location}">$(location)</a> and
+<a href="make-variables.html">"Make variable"</a> substitution, and
+<a href="common-definitions.html#sh-tokenization">Bourne shell tokenization</a>.
+
+<p>The wrapper script for a Java binary includes a CLASSPATH definition
+(to find all the dependent jars) and invokes the right Java interpreter.
+The command line generated by the wrapper script includes the name of
+the main class followed by a <code>"$@"</code> so you can pass along other
+arguments after the classname. However, arguments intended for parsing
+by the JVM must be specified <i>before</i> the classname on the command
+line. The contents of <code>jvm_flags</code> are added to the wrapper
+script before the classname is listed.</p>
+
+<p>Note that this attribute has <em>no effect</em> on <code>*_deploy.jar</code>
+outputs.</p>
+ """,
+ ),
+ "deploy_manifest_lines": attr.string_list(
+ doc = """
+A list of lines to add to the <code>META-INF/manifest.mf</code> file generated for the
+<code>*_deploy.jar</code> target. The contents of this attribute are <em>not</em> subject
+to <a href="make-variables.html">"Make variable"</a> substitution.
+ """,
+ ),
+ "stamp": attr.int(
+ default = -1,
+ values = [-1, 0, 1],
+ doc = """
+Whether to encode build information into the binary. Possible values:
+<ul>
+<li>
+ <code>stamp = 1</code>: Always stamp the build information into the binary, even in
+ <a href="${link user-manual#flag--stamp}"><code>--nostamp</code></a> builds. <b>This
+ setting should be avoided</b>, since it potentially kills remote caching for the
+ binary and any downstream actions that depend on it.
+</li>
+<li>
+ <code>stamp = 0</code>: Always replace build information by constant values. This
+ gives good build result caching.
+</li>
+<li>
+ <code>stamp = -1</code>: Embedding of build information is controlled by the
+ <a href="${link user-manual#flag--stamp}"><code>--[no]stamp</code></a> flag.
+</li>
+</ul>
+<p>Stamped binaries are <em>not</em> rebuilt unless their dependencies change.</p>
+ """,
+ ),
+ "use_testrunner": attr.bool(
+ default = False,
+ doc = semantics.DOCS.for_attribute("use_testrunner") + """
+<br/>
+You can use this to override the default
+behavior, which is to use test runner for
+<code>java_test</code> rules,
+and not use it for <code>java_binary</code> rules. It is unlikely
+you will want to do this. One use is for <code>AllTest</code>
+rules that are invoked by another rule (to set up a database
+before running the tests, for example). The <code>AllTest</code>
+rule must be declared as a <code>java_binary</code>, but should
+still use the test runner as its main entry point.
+
+The name of a test runner class can be overridden with <code>main_class</code> attribute.
+ """,
+ ),
+ "use_launcher": attr.bool(
+ default = True,
+ doc = """
+Whether the binary should use a custom launcher.
+
+<p>If this attribute is set to false, the
+<a href="${link java_binary.launcher}">launcher</a> attribute and the related
+<a href="${link user-manual#flag--java_launcher}"><code>--java_launcher</code></a> flag
+will be ignored for this target.
+ """,
+ ),
+ "env": attr.string_dict(),
+ "classpath_resources": attr.label_list(
+ allow_files = True,
+ doc = """
+<em class="harmful">DO NOT USE THIS OPTION UNLESS THERE IS NO OTHER WAY)</em>
+<p>
+A list of resources that must be located at the root of the java tree. This attribute's
+only purpose is to support third-party libraries that require that their resources be
+found on the classpath as exactly <code>"myconfig.xml"</code>. It is only allowed on
+binaries and not libraries, due to the danger of namespace conflicts.
+</p>
+ """,
+ ),
+ "licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ "_stub_template": attr.label(
+ default = semantics.JAVA_STUB_TEMPLATE_LABEL,
+ allow_single_file = True,
+ ),
+ "_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE),
+ "_windows_constraints": attr.label_list(
+ default = [paths.join(PLATFORMS_ROOT, "os:windows")],
+ ),
+ "_build_info_translator": attr.label(default = semantics.BUILD_INFO_TRANSLATOR_LABEL),
+ } | ({} if _java_common_internal.incompatible_disable_non_executable_java_binary() else {"create_executable": attr.bool(default = True, doc = "Deprecated, use <code>java_single_jar</code> instead.")}),
+)
+
+BASE_TEST_ATTRIBUTES = {
+ "test_class": attr.string(
+ doc = """
+The Java class to be loaded by the test runner.<br/>
+<p>
+ By default, if this argument is not defined then the legacy mode is used and the
+ test arguments are used instead. Set the <code>--nolegacy_bazel_java_test</code> flag
+ to not fallback on the first argument.
+</p>
+<p>
+ This attribute specifies the name of a Java class to be run by
+ this test. It is rare to need to set this. If this argument is omitted,
+ it will be inferred using the target's <code>name</code> and its
+ source-root-relative path. If the test is located outside a known
+ source root, Bazel will report an error if <code>test_class</code>
+ is unset.
+</p>
+<p>
+ For JUnit3, the test class needs to either be a subclass of
+ <code>junit.framework.TestCase</code> or it needs to have a public
+ static <code>suite()</code> method that returns a
+ <code>junit.framework.Test</code> (or a subclass of <code>Test</code>).
+ For JUnit4, the class needs to be annotated with
+ <code>org.junit.runner.RunWith</code>.
+</p>
+<p>
+ This attribute allows several <code>java_test</code> rules to
+ share the same <code>Test</code>
+ (<code>TestCase</code>, <code>TestSuite</code>, ...). Typically
+ additional information is passed to it
+ (e.g. via <code>jvm_flags=['-Dkey=value']</code>) so that its
+ behavior differs in each case, such as running a different
+ subset of the tests. This attribute also enables the use of
+ Java tests outside the <code>javatests</code> tree.
+</p>
+ """,
+ ),
+ "env_inherit": attr.string_list(),
+ "_apple_constraints": attr.label_list(
+ default = [
+ paths.join(PLATFORMS_ROOT, "os:ios"),
+ paths.join(PLATFORMS_ROOT, "os:macos"),
+ paths.join(PLATFORMS_ROOT, "os:tvos"),
+ paths.join(PLATFORMS_ROOT, "os:visionos"),
+ paths.join(PLATFORMS_ROOT, "os:watchos"),
+ ],
+ ),
+ "_legacy_any_type_attrs": attr.string_list(default = ["stamp"]),
+}
diff --git a/java/common/rules/java_binary_deploy_jar.bzl b/java/common/rules/java_binary_deploy_jar.bzl
new file mode 100644
index 0000000..183be45
--- /dev/null
+++ b/java/common/rules/java_binary_deploy_jar.bzl
@@ -0,0 +1,248 @@
+# Copyright 2022 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.
+
+"""Auxiliary rule to create the deploy archives for java_binary"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+load(":java_helper.bzl", "helper")
+
+visibility(["//java/..."])
+
+def _get_build_info(ctx, stamp):
+ if helper.is_stamping_enabled(ctx, stamp):
+ # Makes the target depend on BUILD_INFO_KEY, which helps to discover stamped targets
+ # See b/326620485 for more details.
+ ctx.version_file # buildifier: disable=no-effect
+ return ctx.attr._build_info_translator[OutputGroupInfo].non_redacted_build_info_files.to_list()
+ else:
+ return ctx.attr._build_info_translator[OutputGroupInfo].redacted_build_info_files.to_list()
+
+def create_deploy_archives(
+ ctx,
+ java_attrs,
+ launcher_info,
+ main_class,
+ coverage_main_class,
+ strip_as_default,
+ hermetic = False,
+ add_exports = depset(),
+ add_opens = depset(),
+ shared_archive = None,
+ one_version_level = "OFF",
+ one_version_allowlist = None,
+ extra_args = [],
+ extra_manifest_lines = []):
+ """ Registers actions for _deploy.jar and _deploy.jar.unstripped
+
+ Args:
+ ctx: (RuleContext) The rule context
+ java_attrs: (Struct) Struct of (classpath_resources, runtime_jars, runtime_classpath_for_archive, resources)
+ launcher_info: (Struct) Struct of (runtime_jars, launcher, unstripped_launcher)
+ main_class: (String) FQN of the entry point for execution
+ coverage_main_class: (String) FQN of the entry point for coverage collection
+ strip_as_default: (bool) Whether to create unstripped deploy jar
+ hermetic: (bool)
+ add_exports: (depset)
+ add_opens: (depset)
+ shared_archive: (File) Optional .jsa artifact
+ one_version_level: (String) Optional one version check level, default OFF
+ one_version_allowlist: (File) Optional allowlist for one version check
+ extra_args: (list[Args]) Optional arguments for the deploy jar action
+ extra_manifest_lines: (list[String]) Optional lines added to the jar manifest
+ """
+ classpath_resources = java_attrs.classpath_resources
+
+ runtime_classpath = depset(
+ direct = launcher_info.runtime_jars,
+ transitive = [
+ java_attrs.runtime_jars,
+ java_attrs.runtime_classpath_for_archive,
+ ],
+ order = "preorder",
+ )
+ multi_release = ctx.fragments.java.multi_release_deploy_jars
+ build_info_files = _get_build_info(ctx, ctx.attr.stamp)
+ build_target = str(ctx.label)
+ manifest_lines = ctx.attr.deploy_manifest_lines + extra_manifest_lines
+ create_deploy_archive(
+ ctx,
+ launcher_info.launcher,
+ main_class,
+ coverage_main_class,
+ java_attrs.resources,
+ classpath_resources,
+ runtime_classpath,
+ manifest_lines,
+ build_info_files,
+ build_target,
+ output = ctx.outputs.deployjar,
+ shared_archive = shared_archive,
+ one_version_level = one_version_level,
+ one_version_allowlist = one_version_allowlist,
+ multi_release = multi_release,
+ hermetic = hermetic,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ extra_args = extra_args,
+ )
+
+ if strip_as_default:
+ create_deploy_archive(
+ ctx,
+ launcher_info.unstripped_launcher,
+ main_class,
+ coverage_main_class,
+ java_attrs.resources,
+ classpath_resources,
+ runtime_classpath,
+ manifest_lines,
+ build_info_files,
+ build_target,
+ output = ctx.outputs.unstrippeddeployjar,
+ multi_release = multi_release,
+ hermetic = hermetic,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ extra_args = extra_args,
+ )
+ else:
+ ctx.actions.write(ctx.outputs.unstrippeddeployjar, "")
+
+def create_deploy_archive(
+ ctx,
+ launcher,
+ main_class,
+ coverage_main_class,
+ resources,
+ classpath_resources,
+ runtime_classpath,
+ manifest_lines,
+ build_info_files,
+ build_target,
+ output,
+ shared_archive = None,
+ one_version_level = "OFF",
+ one_version_allowlist = None,
+ multi_release = False,
+ hermetic = False,
+ add_exports = [],
+ add_opens = [],
+ extra_args = []):
+ """ Creates a deploy jar
+
+ Requires a Java runtime toolchain if and only if hermetic is True.
+
+ Args:
+ ctx: (RuleContext) The rule context
+ launcher: (File) the launcher artifact
+ main_class: (String) FQN of the entry point for execution
+ coverage_main_class: (String) FQN of the entry point for coverage collection
+ resources: (Depset) resource inputs
+ classpath_resources: (Depset) classpath resource inputs
+ runtime_classpath: (Depset) source files to add to the jar
+ build_target: (String) Name of the build target for stamping
+ manifest_lines: (list[String]) Optional lines added to the jar manifest
+ build_info_files: (list[File]) build info files for stamping
+ build_target: (String) the owner build target label name string
+ output: (File) the output jar artifact
+ shared_archive: (File) Optional .jsa artifact
+ one_version_level: (String) Optional one version check level, default OFF
+ one_version_allowlist: (File) Optional allowlist for one version check
+ multi_release: (bool)
+ hermetic: (bool)
+ add_exports: (depset)
+ add_opens: (depset)
+ extra_args: (list[Args]) Optional arguments for the deploy jar action
+ """
+ input_files = []
+ input_files.extend(build_info_files)
+
+ transitive_input_files = [
+ resources,
+ classpath_resources,
+ runtime_classpath,
+ ]
+
+ single_jar = semantics.find_java_toolchain(ctx).single_jar
+
+ manifest_lines = list(manifest_lines)
+ if ctx.configuration.coverage_enabled:
+ manifest_lines.append("Coverage-Main-Class: %s" % coverage_main_class)
+
+ args = ctx.actions.args()
+ args.set_param_file_format("shell").use_param_file("@%s", use_always = True)
+
+ args.add("--output", output)
+ args.add("--build_target", build_target)
+ args.add("--normalize")
+ args.add("--compression")
+ if main_class:
+ args.add("--main_class", main_class)
+ args.add_all("--deploy_manifest_lines", manifest_lines)
+ args.add_all(build_info_files, before_each = "--build_info_file")
+ if launcher:
+ input_files.append(launcher)
+ args.add("--java_launcher", launcher)
+ args.add_all("--classpath_resources", classpath_resources)
+ args.add_all(
+ "--sources",
+ runtime_classpath,
+ map_each = helper.jar_and_target_arg_mapper,
+ )
+
+ if one_version_level != "OFF" and one_version_allowlist:
+ input_files.append(one_version_allowlist)
+ args.add("--enforce_one_version")
+ args.add("--one_version_whitelist", one_version_allowlist)
+ if one_version_level == "WARNING":
+ args.add("--succeed_on_found_violations")
+
+ if multi_release:
+ args.add("--multi_release")
+
+ if hermetic:
+ runtime = ctx.toolchains["@//tools/jdk/hermetic:hermetic_runtime_toolchain_type"].java_runtime
+ if runtime.lib_modules != None:
+ java_home = runtime.java_home
+ lib_modules = runtime.lib_modules
+ hermetic_files = runtime.hermetic_files
+ args.add("--hermetic_java_home", java_home)
+ args.add("--jdk_lib_modules", lib_modules)
+ args.add_all("--resources", hermetic_files)
+ input_files.append(lib_modules)
+ transitive_input_files.append(hermetic_files)
+
+ if shared_archive == None:
+ shared_archive = runtime.default_cds
+
+ if shared_archive:
+ input_files.append(shared_archive)
+ args.add("--cds_archive", shared_archive)
+
+ args.add_all("--add_exports", add_exports)
+ args.add_all("--add_opens", add_opens)
+
+ inputs = depset(input_files, transitive = transitive_input_files)
+
+ ctx.actions.run(
+ mnemonic = "JavaDeployJar",
+ progress_message = "Building deploy jar %s" % output.short_path,
+ executable = single_jar,
+ inputs = inputs,
+ tools = [single_jar],
+ outputs = [output],
+ arguments = [args] + extra_args,
+ use_default_shell_env = True,
+ toolchain = semantics.JAVA_TOOLCHAIN_TYPE,
+ )
diff --git a/java/common/rules/java_binary_wrapper.bzl b/java/common/rules/java_binary_wrapper.bzl
new file mode 100644
index 0000000..3d94dcd
--- /dev/null
+++ b/java/common/rules/java_binary_wrapper.bzl
@@ -0,0 +1,73 @@
+# Copyright 2022 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.
+
+"""Macro encapsulating the java_binary implementation
+
+This is needed since the `executable` nature of the target must be computed from
+the supplied value of the `create_executable` attribute.
+"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+
+visibility(["//java/..."])
+
+def register_legacy_java_binary_rules(
+ rule_exec,
+ rule_nonexec,
+ **kwargs):
+ """Registers the correct java_binary rule and deploy jar rule
+
+ Args:
+ rule_exec: (Rule) The executable java_binary rule
+ rule_nonexec: (Rule) The non-executable java_binary rule
+ **kwargs: Actual args to instantiate the rule
+ """
+
+ create_executable = "create_executable" not in kwargs or kwargs["create_executable"]
+
+ # TODO(hvd): migrate depot to integers / maybe use decompose_select_list()
+ if "stamp" in kwargs and type(kwargs["stamp"]) == type(True):
+ kwargs["stamp"] = 1 if kwargs["stamp"] else 0
+ if not create_executable:
+ rule_nonexec(**kwargs)
+ else:
+ if "use_launcher" in kwargs and not kwargs["use_launcher"]:
+ kwargs["launcher"] = None
+ else:
+ # If launcher is not set or None, set it to config flag
+ if "launcher" not in kwargs or not kwargs["launcher"]:
+ kwargs["launcher"] = semantics.LAUNCHER_FLAG_LABEL
+ rule_exec(**kwargs)
+
+def register_java_binary_rules(
+ java_binary,
+ **kwargs):
+ """Creates a java_binary rule and a deploy jar rule
+
+ Args:
+ java_binary: (Rule) The executable java_binary rule
+ **kwargs: Actual args to instantiate the rule
+ """
+
+ # TODO(hvd): migrate depot to integers / maybe use decompose_select_list()
+ if "stamp" in kwargs and type(kwargs["stamp"]) == type(True):
+ kwargs["stamp"] = 1 if kwargs["stamp"] else 0
+
+ if "use_launcher" in kwargs and not kwargs["use_launcher"]:
+ kwargs["launcher"] = None
+ else:
+ # If launcher is not set or None, set it to config flag
+ if "launcher" not in kwargs or not kwargs["launcher"]:
+ kwargs["launcher"] = semantics.LAUNCHER_FLAG_LABEL
+ java_binary(**kwargs)
diff --git a/java/common/rules/java_helper.bzl b/java/common/rules/java_helper.bzl
new file mode 100644
index 0000000..c0aa231
--- /dev/null
+++ b/java/common/rules/java_helper.bzl
@@ -0,0 +1,507 @@
+# Copyright 2022 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.
+
+"""Common util functions for java_* rules"""
+
+load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
+load("@rules_cc//cc/common:cc_helper.bzl", "cc_helper")
+load(
+ "//java/common:java_semantics.bzl",
+ "semantics",
+ _semantics_tokenize_javacopts = "tokenize_javacopts",
+)
+load("//third_party/bazel_skylib/lib:paths.bzl", "paths")
+
+visibility(["//java/..."])
+
+def _collect_all_targets_as_deps(ctx, classpath_type = "all"):
+ deps = []
+ if not classpath_type == "compile_only":
+ if hasattr(ctx.attr, "runtime_deps"):
+ deps.extend(ctx.attr.runtime_deps)
+ if hasattr(ctx.attr, "exports"):
+ deps.extend(ctx.attr.exports)
+
+ deps.extend(ctx.attr.deps or [])
+
+ launcher = _filter_launcher_for_target(ctx)
+ if launcher:
+ deps.append(launcher)
+
+ return deps
+
+def _filter_launcher_for_target(ctx):
+ # create_executable=0 disables the launcher
+ if hasattr(ctx.attr, "create_executable") and not ctx.attr.create_executable:
+ return None
+
+ # use_launcher=False disables the launcher
+ if hasattr(ctx.attr, "use_launcher") and not ctx.attr.use_launcher:
+ return None
+
+ # BUILD rule "launcher" attribute
+ if ctx.attr.launcher and cc_common.launcher_provider in ctx.attr.launcher:
+ return ctx.attr.launcher
+
+ return None
+
+def _launcher_artifact_for_target(ctx):
+ launcher = _filter_launcher_for_target(ctx)
+ if not launcher:
+ return None
+ files = launcher[DefaultInfo].files.to_list()
+ if len(files) != 1:
+ fail("%s expected a single artifact in %s" % (ctx.label, launcher))
+ return files[0]
+
+def _check_and_get_main_class(ctx):
+ create_executable = ctx.attr.create_executable
+ use_testrunner = ctx.attr.use_testrunner
+ main_class = ctx.attr.main_class
+
+ if not create_executable and use_testrunner:
+ fail("cannot have use_testrunner without creating an executable")
+ if not create_executable and main_class:
+ fail("main class must not be specified when executable is not created")
+ if create_executable and not use_testrunner:
+ if not main_class:
+ if not ctx.attr.srcs:
+ fail("need at least one of 'main_class', 'use_testrunner' or Java source files")
+ main_class = _primary_class(ctx)
+ if main_class == None:
+ fail("main_class was not provided and cannot be inferred: " +
+ "source path doesn't include a known root (java, javatests, src, testsrc)")
+ if not create_executable:
+ return None
+ if not main_class:
+ if use_testrunner:
+ main_class = "com.google.testing.junit.runner.GoogleTestRunner"
+ else:
+ main_class = _primary_class(ctx)
+ return main_class
+
+def _primary_class(ctx):
+ if ctx.attr.srcs:
+ main = ctx.label.name + ".java"
+ for src in ctx.files.srcs:
+ if src.basename == main:
+ return _full_classname(_strip_extension(src))
+ return _full_classname(paths.get_relative(ctx.label.package, ctx.label.name))
+
+def _strip_extension(file):
+ return file.dirname + "/" + (
+ file.basename[:-(1 + len(file.extension))] if file.extension else file.basename
+ )
+
+# TODO(b/193629418): once out of builtins, create a canonical implementation and remove duplicates in depot
+def _full_classname(path):
+ java_segments = _java_segments(path)
+ return ".".join(java_segments) if java_segments != None else None
+
+def _java_segments(path):
+ if path.startswith("/"):
+ fail("path must not be absolute: '%s'" % path)
+ segments = path.split("/")
+ root_idx = -1
+ for idx, segment in enumerate(segments):
+ if segment in ["java", "javatests", "src", "testsrc"]:
+ root_idx = idx
+ break
+ if root_idx < 0:
+ return None
+ is_src = "src" == segments[root_idx]
+ check_mvn_idx = root_idx if is_src else -1
+ if (root_idx == 0 or is_src):
+ for i in range(root_idx + 1, len(segments) - 1):
+ segment = segments[i]
+ if "src" == segment or (is_src and (segment in ["java", "javatests"])):
+ next = segments[i + 1]
+ if next in ["com", "org", "net"]:
+ root_idx = i
+ elif "src" == segment:
+ check_mvn_idx = i
+ break
+
+ if check_mvn_idx >= 0 and check_mvn_idx < len(segments) - 2:
+ next = segments[check_mvn_idx + 1]
+ if next in ["main", "test"]:
+ next = segments[check_mvn_idx + 2]
+ if next in ["java", "resources"]:
+ root_idx = check_mvn_idx + 2
+ return segments[(root_idx + 1):]
+
+def _concat(*lists):
+ result = []
+ for list in lists:
+ result.extend(list)
+ return result
+
+def _get_shared_native_deps_path(
+ linker_inputs,
+ link_opts,
+ linkstamps,
+ build_info_artifacts,
+ features,
+ is_test_target_partially_disabled_thin_lto):
+ """
+ Returns the path of the shared native library.
+
+ The name must be generated based on the rule-specific inputs to the link actions. At this point
+ this includes order-sensitive list of linker inputs and options collected from the transitive
+ closure and linkstamp-related artifacts that are compiled during linking. All those inputs can
+ be affected by modifying target attributes (srcs/deps/stamp/etc). However, target build
+ configuration can be ignored since it will either change output directory (in case of different
+ configuration instances) or will not affect anything (if two targets use same configuration).
+ Final goal is for all native libraries that use identical linker command to use same output
+ name.
+
+ <p>TODO(bazel-team): (2010) Currently process of identifying parameters that can affect native
+ library name is manual and should be kept in sync with the code in the
+ CppLinkAction.Builder/CppLinkAction/Link classes which are responsible for generating linker
+ command line. Ideally we should reuse generated command line for both purposes - selecting a
+ name of the native library and using it as link action payload. For now, correctness of the
+ method below is only ensured by validations in the CppLinkAction.Builder.build() method.
+ """
+
+ fp = ""
+ for artifact in linker_inputs:
+ fp += artifact.short_path
+ fp += str(len(link_opts))
+ for opt in link_opts:
+ fp += opt
+ for artifact in linkstamps:
+ fp += artifact.short_path
+ for artifact in build_info_artifacts:
+ fp += artifact.short_path
+ for feature in features:
+ fp += feature
+
+ # Sharing of native dependencies may cause an ActionConflictException when ThinLTO is
+ # disabled for test and test-only targets that are statically linked, but enabled for other
+ # statically linked targets. This happens in case the artifacts for the shared native
+ # dependency are output by actions owned by the non-test and test targets both. To fix
+ # this, we allow creation of multiple artifacts for the shared native library - one shared
+ # among the test and test-only targets where ThinLTO is disabled, and the other shared among
+ # other targets where ThinLTO is enabled.
+ fp += "1" if is_test_target_partially_disabled_thin_lto else "0"
+
+ fingerprint = "%x" % hash(fp)
+ return "_nativedeps/" + fingerprint
+
+def _check_and_get_one_version_attribute(ctx, attr):
+ value = getattr(semantics.find_java_toolchain(ctx), attr)
+ return value
+
+def _jar_and_target_arg_mapper(jar):
+ # Emit pretty labels for targets in the main repository.
+ label = str(jar.owner)
+ if label.startswith("@@//"):
+ label = label.lstrip("@")
+ return jar.path + "," + label
+
+def _get_feature_config(ctx):
+ cc_toolchain = find_cc_toolchain(ctx, mandatory = False)
+ if not cc_toolchain:
+ return None
+ feature_config = cc_common.configure_features(
+ ctx = ctx,
+ cc_toolchain = cc_toolchain,
+ requested_features = ctx.features + ["java_launcher_link", "static_linking_mode"],
+ unsupported_features = ctx.disabled_features,
+ )
+ return feature_config
+
+def _should_strip_as_default(ctx, feature_config):
+ fission_is_active = ctx.fragments.cpp.fission_active_for_current_compilation_mode()
+ create_per_obj_debug_info = fission_is_active and cc_common.is_enabled(
+ feature_name = "per_object_debug_info",
+ feature_configuration = feature_config,
+ )
+ compilation_mode = ctx.var["COMPILATION_MODE"]
+ strip_as_default = create_per_obj_debug_info and compilation_mode == "opt"
+
+ return strip_as_default
+
+def _get_coverage_config(ctx, runner):
+ toolchain = semantics.find_java_toolchain(ctx)
+ if not ctx.configuration.coverage_enabled:
+ return None
+ runner = runner if ctx.attr.create_executable else None
+ manifest = ctx.actions.declare_file("runtime_classpath_for_coverage/%s/runtime_classpath.txt" % ctx.label.name)
+ singlejar = toolchain.single_jar
+ return struct(
+ runner = runner,
+ main_class = "com.google.testing.coverage.JacocoCoverageRunner",
+ manifest = manifest,
+ env = {
+ "JAVA_RUNTIME_CLASSPATH_FOR_COVERAGE": manifest.path,
+ "SINGLE_JAR_TOOL": singlejar.executable.path,
+ },
+ support_files = [manifest, singlejar.executable],
+ )
+
+def _get_java_executable(ctx, java_runtime_toolchain, launcher):
+ java_executable = launcher.short_path if launcher else java_runtime_toolchain.java_executable_runfiles_path
+ if not _is_absolute_target_platform_path(ctx, java_executable):
+ java_executable = ctx.workspace_name + "/" + java_executable
+ return paths.normalize(java_executable)
+
+def _has_target_constraints(ctx, constraints):
+ # Constraints is a label_list.
+ for constraint in constraints:
+ constraint_value = constraint[platform_common.ConstraintValueInfo]
+ if ctx.target_platform_has_constraint(constraint_value):
+ return True
+ return False
+
+def _is_target_platform_windows(ctx):
+ return _has_target_constraints(ctx, ctx.attr._windows_constraints)
+
+def _is_absolute_target_platform_path(ctx, path):
+ if _is_target_platform_windows(ctx):
+ return len(path) > 2 and path[1] == ":"
+ return path.startswith("/")
+
+def _runfiles_enabled(ctx):
+ return ctx.configuration.runfiles_enabled()
+
+def _get_test_support(ctx):
+ if ctx.attr.create_executable and ctx.attr.use_testrunner:
+ return ctx.attr._test_support
+ return None
+
+def _test_providers(ctx):
+ test_providers = []
+ if _has_target_constraints(ctx, ctx.attr._apple_constraints):
+ test_providers.append(testing.ExecutionInfo({"requires-darwin": ""}))
+
+ test_env = {}
+ test_env.update(cc_helper.get_expanded_env(ctx, {}))
+
+ coverage_config = _get_coverage_config(
+ ctx,
+ runner = None, # we only need the environment
+ )
+ if coverage_config:
+ test_env.update(coverage_config.env)
+ test_providers.append(testing.TestEnvironment(
+ environment = test_env,
+ inherited_environment = ctx.attr.env_inherit,
+ ))
+
+ return test_providers
+
+def _executable_providers(ctx):
+ if ctx.attr.create_executable:
+ return [RunEnvironmentInfo(cc_helper.get_expanded_env(ctx, {}))]
+ return []
+
+def _resource_mapper(file):
+ root_relative_path = paths.relativize(
+ path = file.path,
+ start = paths.join(file.root.path, file.owner.workspace_root),
+ )
+ return "%s:%s" % (
+ file.path,
+ semantics.get_default_resource_path(root_relative_path, segment_extractor = _java_segments),
+ )
+
+def _create_single_jar(
+ actions,
+ toolchain,
+ output,
+ sources = depset(),
+ resources = depset(),
+ mnemonic = "JavaSingleJar",
+ progress_message = "Building singlejar jar %{output}",
+ build_target = None,
+ output_creator = None):
+ """Register singlejar action for the output jar.
+
+ Args:
+ actions: (actions) ctx.actions
+ toolchain: (JavaToolchainInfo) The java toolchain
+ output: (File) Output file of the action.
+ sources: (depset[File]) The jar files to merge into the output jar.
+ resources: (depset[File]) The files to add to the output jar.
+ mnemonic: (str) The action identifier
+ progress_message: (str) The action progress message
+ build_target: (Label) The target label to stamp in the manifest. Optional.
+ output_creator: (str) The name of the tool to stamp in the manifest. Optional,
+ defaults to 'singlejar'
+ Returns:
+ (File) Output file which was used for registering the action.
+ """
+ args = actions.args()
+ args.set_param_file_format("shell").use_param_file("@%s", use_always = True)
+ args.add("--output", output)
+ args.add_all(
+ [
+ "--compression",
+ "--normalize",
+ "--exclude_build_data",
+ "--warn_duplicate_resources",
+ ],
+ )
+ args.add_all("--sources", sources)
+ args.add_all("--resources", resources, map_each = _resource_mapper)
+
+ args.add("--build_target", build_target)
+ args.add("--output_jar_creator", output_creator)
+
+ actions.run(
+ mnemonic = mnemonic,
+ progress_message = progress_message,
+ executable = toolchain.single_jar,
+ toolchain = semantics.JAVA_TOOLCHAIN_TYPE,
+ inputs = depset(transitive = [resources, sources]),
+ tools = [toolchain.single_jar],
+ outputs = [output],
+ arguments = [args],
+ )
+ return output
+
+# TODO(hvd): use skylib shell.quote()
+def _shell_escape(s):
+ """Shell-escape a string
+
+ Quotes a word so that it can be used, without further quoting, as an argument
+ (or part of an argument) in a shell command.
+
+ Args:
+ s: (str) the string to escape
+
+ Returns:
+ (str) the shell-escaped string
+ """
+ if not s:
+ # Empty string is a special case: needs to be quoted to ensure that it
+ # gets treated as a separate argument.
+ return "''"
+ for c in s.elems():
+ # We do this positively so as to be sure we don't inadvertently forget
+ # any unsafe characters.
+ if not c.isalnum() and c not in "@%-_+:,./":
+ return "'" + s.replace("'", "'\\''") + "'"
+ return s
+
+def _tokenize_javacopts(ctx = None, opts = []):
+ """Tokenizes a list or depset of options to a list.
+
+ Iff opts is a depset, we reverse the flattened list to ensure right-most
+ duplicates are preserved in their correct position.
+
+ If the ctx parameter is omitted, a slow, but pure Starlark, implementation
+ of shell tokenization is used. Otherwise, tokenization is performed using
+ ctx.tokenize() which has significantly better performance (up to 100x for
+ large options lists).
+
+ Args:
+ ctx: (RuleContext|None) the rule context
+ opts: (depset[str]|[str]) the javac options to tokenize
+ Returns:
+ [str] list of tokenized options
+ """
+ if hasattr(opts, "to_list"):
+ opts = reversed(opts.to_list())
+ if ctx:
+ return [
+ token
+ for opt in opts
+ for token in ctx.tokenize(opt)
+ ]
+ else:
+ return _semantics_tokenize_javacopts(opts)
+
+def _detokenize_javacopts(opts):
+ """Detokenizes a list of options to a depset.
+
+ Args:
+ opts: ([str]) the javac options to detokenize
+
+ Returns:
+ (depset[str]) depset of detokenized options
+ """
+ return depset(
+ [" ".join([_shell_escape(opt) for opt in opts])],
+ order = "preorder",
+ )
+
+def _derive_output_file(ctx, base_file, *, name_suffix = "", extension = None, extension_suffix = ""):
+ """Declares a new file whose name is derived from the given file
+
+ This method allows appending a suffix to the name (before extension), changing
+ the extension or appending a suffix after the extension. The new file is declared
+ as a sibling of the given base file. At least one of the three options must be
+ specified. It is an error to specify both `extension` and `extension_suffix`.
+
+ Args:
+ ctx: (RuleContext) the rule context.
+ base_file: (File) the file from which to derive the resultant file.
+ name_suffix: (str) Optional. The suffix to append to the name before the
+ extension.
+ extension: (str) Optional. The new extension to use (without '.'). By default,
+ the base_file's extension is used.
+ extension_suffix: (str) Optional. The suffix to append to the base_file's extension
+
+ Returns:
+ (File) the derived file
+ """
+ if not name_suffix and not extension_suffix and not extension:
+ fail("At least one of name_suffix, extension or extension_suffix is required")
+ if extension and extension_suffix:
+ fail("only one of extension or extension_suffix can be specified")
+ if extension == None:
+ extension = base_file.extension
+ new_basename = paths.replace_extension(base_file.basename, name_suffix + "." + extension + extension_suffix)
+ return ctx.actions.declare_file(new_basename, sibling = base_file)
+
+def _is_stamping_enabled(ctx, stamp):
+ if ctx.configuration.is_tool_configuration():
+ return 0
+ if stamp == 1 or stamp == 0:
+ return stamp
+
+ # stamp == -1 / auto
+ return int(ctx.configuration.stamp_binaries())
+
+helper = struct(
+ collect_all_targets_as_deps = _collect_all_targets_as_deps,
+ filter_launcher_for_target = _filter_launcher_for_target,
+ launcher_artifact_for_target = _launcher_artifact_for_target,
+ check_and_get_main_class = _check_and_get_main_class,
+ primary_class = _primary_class,
+ strip_extension = _strip_extension,
+ concat = _concat,
+ get_shared_native_deps_path = _get_shared_native_deps_path,
+ check_and_get_one_version_attribute = _check_and_get_one_version_attribute,
+ jar_and_target_arg_mapper = _jar_and_target_arg_mapper,
+ get_feature_config = _get_feature_config,
+ should_strip_as_default = _should_strip_as_default,
+ get_coverage_config = _get_coverage_config,
+ get_java_executable = _get_java_executable,
+ is_absolute_target_platform_path = _is_absolute_target_platform_path,
+ is_target_platform_windows = _is_target_platform_windows,
+ runfiles_enabled = _runfiles_enabled,
+ get_test_support = _get_test_support,
+ test_providers = _test_providers,
+ executable_providers = _executable_providers,
+ create_single_jar = _create_single_jar,
+ shell_escape = _shell_escape,
+ tokenize_javacopts = _tokenize_javacopts,
+ detokenize_javacopts = _detokenize_javacopts,
+ derive_output_file = _derive_output_file,
+ is_stamping_enabled = _is_stamping_enabled,
+)
diff --git a/java/common/rules/java_import.bzl b/java/common/rules/java_import.bzl
new file mode 100644
index 0000000..7ba8065
--- /dev/null
+++ b/java/common/rules/java_import.bzl
@@ -0,0 +1,356 @@
+# Copyright 2021 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 java_import rule.
+"""
+
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//java/common:java_common.bzl", "java_common")
+load("//java/common:java_info.bzl", "JavaInfo")
+load("//java/common:java_semantics.bzl", "semantics")
+load(":basic_java_library.bzl", "construct_defaultinfo")
+load(":import_deps_check.bzl", "import_deps_check")
+load(":proguard_validation.bzl", "validate_proguard_specs")
+
+visibility(["//java/..."])
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+_run_ijar_private_for_builtins = _java_common_internal.run_ijar_private_for_builtins
+
+def _filter_provider(provider, *attrs):
+ return [dep[provider] for attr in attrs for dep in attr if provider in dep]
+
+def _collect_jars(ctx, jars):
+ jars_dict = {}
+ for info in jars:
+ if JavaInfo in info:
+ fail("'jars' attribute cannot contain labels of Java targets")
+ for jar in info.files.to_list():
+ jar_path = jar.dirname + jar.basename
+ if jars_dict.get(jar_path) != None:
+ fail("in jars attribute of java_import rule //" + ctx.label.package + ":" + ctx.attr.name + ": " + jar.basename + " is a duplicate")
+ jars_dict[jar_path] = jar
+ return [jar_tuple[1] for jar_tuple in jars_dict.items()] if len(jars_dict.items()) > 0 else []
+
+def _process_with_ijars_if_needed(jars, ctx):
+ file_dict = {}
+ use_ijars = ctx.fragments.java.use_ijars()
+ for jar in jars:
+ interface_jar = jar
+ if use_ijars:
+ ijar_basename = jar.short_path.removeprefix("../").removesuffix("." + jar.extension) + "-ijar.jar"
+ interface_jar_directory = "_ijar/" + ctx.label.name + "/" + ijar_basename
+
+ interface_jar = ctx.actions.declare_file(interface_jar_directory)
+ _run_ijar_private_for_builtins(
+ ctx.actions,
+ target_label = ctx.label,
+ jar = jar,
+ output = interface_jar,
+ java_toolchain = semantics.find_java_toolchain(ctx),
+ )
+ file_dict[jar] = interface_jar
+
+ return file_dict
+
+def _check_export_error(ctx, exports):
+ not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_exports") and not getattr(ctx.attr, "_allowlist_java_import_exports")[PackageSpecificationInfo].contains(ctx.label)
+ disallow_java_import_exports = ctx.fragments.java.disallow_java_import_exports()
+
+ if len(exports) != 0 and (disallow_java_import_exports or not_in_allowlist):
+ fail("java_import.exports is no longer supported; use java_import.deps instead")
+
+def _check_empty_jars_error(ctx, jars):
+ # TODO(kotlaja): Remove temporary incompatible flag [disallow_java_import_empty_jars] once migration is done.
+ not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_empty_jars") and not getattr(ctx.attr, "_allowlist_java_import_empty_jars")[PackageSpecificationInfo].contains(ctx.label)
+ disallow_java_import_empty_jars = ctx.fragments.java.disallow_java_import_empty_jars()
+
+ if len(jars) == 0 and disallow_java_import_empty_jars and not_in_allowlist:
+ fail("empty java_import.jars is no longer supported " + ctx.label.package)
+
+def _create_java_info_with_dummy_output_file(ctx, srcjar, all_deps, exports, runtime_deps_list, neverlink, cc_info_list, add_exports, add_opens):
+ dummy_jar = ctx.actions.declare_file(ctx.label.name + "_dummy.jar")
+ dummy_src_jar = srcjar
+ if dummy_src_jar == None:
+ dummy_src_jar = ctx.actions.declare_file(ctx.label.name + "_src_dummy.java")
+ ctx.actions.write(dummy_src_jar, "")
+ return java_common.compile(
+ ctx,
+ output = dummy_jar,
+ java_toolchain = semantics.find_java_toolchain(ctx),
+ source_files = [dummy_src_jar],
+ deps = all_deps,
+ runtime_deps = runtime_deps_list,
+ neverlink = neverlink,
+ exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually.
+ native_libraries = cc_info_list,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ )
+
+def bazel_java_import_rule(
+ ctx,
+ jars = [],
+ srcjar = None,
+ deps = [],
+ runtime_deps = [],
+ exports = [],
+ neverlink = False,
+ proguard_specs = [],
+ add_exports = [],
+ add_opens = []):
+ """Implements java_import.
+
+ This rule allows the use of precompiled .jar files as libraries in other Java rules.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ jars: (list[Artifact]) List of output jars.
+ srcjar: (Artifact) The jar containing the sources.
+ deps: (list[Target]) The list of dependent libraries.
+ runtime_deps: (list[Target]) Runtime dependencies to attach to the rule.
+ exports: (list[Target]) The list of exported libraries.
+ neverlink: (bool) Whether this rule should only be used for compilation and not at runtime.
+ proguard_specs: (list[File]) Files to be used as Proguard specification.
+ add_exports: (list[str]) Allow this library to access the given <module>/<package>.
+ add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
+
+ Returns:
+ (list[provider]) A list containing DefaultInfo, JavaInfo,
+ OutputGroupsInfo, ProguardSpecProvider providers.
+ """
+
+ _check_empty_jars_error(ctx, jars)
+ _check_export_error(ctx, exports)
+
+ collected_jars = _collect_jars(ctx, jars)
+ all_deps = _filter_provider(JavaInfo, deps, exports)
+
+ jdeps_artifact = None
+ merged_java_info = java_common.merge(all_deps)
+ not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_deps_checking") and not ctx.attr._allowlist_java_import_deps_checking[PackageSpecificationInfo].contains(ctx.label)
+ if len(collected_jars) > 0 and not_in_allowlist and "incomplete-deps" not in ctx.attr.tags:
+ jdeps_artifact = import_deps_check(
+ ctx,
+ collected_jars,
+ merged_java_info.compile_jars,
+ merged_java_info.transitive_compile_time_jars,
+ "java_import",
+ )
+
+ compilation_to_runtime_jar_map = _process_with_ijars_if_needed(collected_jars, ctx)
+ runtime_deps_list = [runtime_dep[JavaInfo] for runtime_dep in runtime_deps if JavaInfo in runtime_dep]
+ cc_info_list = [dep[CcInfo] for dep in deps if CcInfo in dep]
+ java_info = None
+ if len(collected_jars) > 0:
+ java_infos = []
+ for jar in collected_jars:
+ java_infos.append(JavaInfo(
+ output_jar = jar,
+ compile_jar = compilation_to_runtime_jar_map[jar],
+ deps = all_deps,
+ runtime_deps = runtime_deps_list,
+ neverlink = neverlink,
+ source_jar = srcjar,
+ exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually.
+ native_libraries = cc_info_list,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ ))
+ java_info = java_common.merge(java_infos)
+ else:
+ # TODO(kotlaja): Remove next line once all java_import targets with empty jars attribute are cleaned from depot (b/246559727).
+ java_info = _create_java_info_with_dummy_output_file(ctx, srcjar, all_deps, exports, runtime_deps_list, neverlink, cc_info_list, add_exports, add_opens)
+
+ target = {"JavaInfo": java_info}
+
+ target["ProguardSpecProvider"] = validate_proguard_specs(
+ ctx,
+ proguard_specs,
+ [deps, runtime_deps, exports],
+ )
+
+ # TODO(kotlaja): Revise if collected_runtimes can be added into construct_defaultinfo directly.
+ collected_runtimes = []
+ for runtime_dep in ctx.attr.runtime_deps:
+ collected_runtimes.extend(runtime_dep.files.to_list())
+
+ target["DefaultInfo"] = construct_defaultinfo(
+ ctx,
+ collected_jars,
+ collected_jars + collected_runtimes,
+ neverlink,
+ exports,
+ )
+
+ output_group_src_jars = depset() if srcjar == None else depset([srcjar])
+ target["OutputGroupInfo"] = OutputGroupInfo(
+ **{
+ "_source_jars": output_group_src_jars,
+ "_direct_source_jars": output_group_src_jars,
+ "_validation": depset() if jdeps_artifact == None else depset([jdeps_artifact]),
+ "_hidden_top_level_INTERNAL_": target["ProguardSpecProvider"].specs,
+ }
+ )
+ return target
+
+def _proxy(ctx):
+ return bazel_java_import_rule(
+ ctx,
+ ctx.attr.jars,
+ ctx.file.srcjar,
+ ctx.attr.deps,
+ ctx.attr.runtime_deps,
+ ctx.attr.exports,
+ ctx.attr.neverlink,
+ ctx.files.proguard_specs,
+ ctx.attr.add_exports,
+ ctx.attr.add_opens,
+ ).values()
+
+_ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT = [
+ "java_library",
+ "java_import",
+ "cc_library",
+ "cc_binary",
+]
+
+# buildifier: disable=attr-licenses
+JAVA_IMPORT_ATTRS = {
+ "data": attr.label_list(
+ allow_files = True,
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
+ doc = """
+The list of files needed by this rule at runtime.
+ """,
+ ),
+ "deps": attr.label_list(
+ providers = [JavaInfo],
+ allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
+ doc = """
+The list of other libraries to be linked in to the target.
+See <a href="${link java_library.deps}">java_library.deps</a>.
+ """,
+ ),
+ "exports": attr.label_list(
+ providers = [JavaInfo],
+ allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
+ doc = """
+Targets to make available to users of this rule.
+See <a href="${link java_library.exports}">java_library.exports</a>.
+ """,
+ ),
+ "runtime_deps": attr.label_list(
+ allow_files = [".jar"],
+ allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
+ providers = [[CcInfo], [JavaInfo]],
+ flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
+ doc = """
+Libraries to make available to the final binary or test at runtime only.
+See <a href="${link java_library.runtime_deps}">java_library.runtime_deps</a>.
+ """,
+ ),
+ # JavaImportBazeRule attr
+ "jars": attr.label_list(
+ allow_files = [".jar"],
+ mandatory = True,
+ doc = """
+The list of JAR files provided to Java targets that depend on this target.
+ """,
+ ),
+ "srcjar": attr.label(
+ allow_single_file = [".srcjar", ".jar"],
+ flags = ["DIRECT_COMPILE_TIME_INPUT"],
+ doc = """
+A JAR file that contains source code for the compiled JAR files.
+ """,
+ ),
+ "neverlink": attr.bool(
+ default = False,
+ doc = """
+Only use this library for compilation and not at runtime.
+Useful if the library will be provided by the runtime environment
+during execution. Examples of libraries like this are IDE APIs
+for IDE plug-ins or <code>tools.jar</code> for anything running on
+a standard JDK.
+ """,
+ ),
+ "constraints": attr.string_list(
+ doc = """
+Extra constraints imposed on this rule as a Java library.
+ """,
+ ),
+ # ProguardLibraryRule attr
+ "proguard_specs": attr.label_list(
+ allow_files = True,
+ doc = """
+Files to be used as Proguard specification.
+These will describe the set of specifications to be used by Proguard. If specified,
+they will be added to any <code>android_binary</code> target depending on this library.
+
+The files included here must only have idempotent rules, namely -dontnote, -dontwarn,
+assumenosideeffects, and rules that start with -keep. Other options can only appear in
+<code>android_binary</code>'s proguard_specs, to ensure non-tautological merges.
+ """,
+ ),
+ # Additional attrs
+ "add_exports": attr.string_list(
+ doc = """
+Allow this library to access the given <code>module</code> or <code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-exports= flags.
+ """,
+ ),
+ "add_opens": attr.string_list(
+ doc = """
+Allow this library to reflectively access the given <code>module</code> or
+<code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-opens= flags.
+ """,
+ ),
+ "licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ "_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE),
+}
+
+java_import = rule(
+ _proxy,
+ doc = """
+<p>
+ This rule allows the use of precompiled <code>.jar</code> files as
+ libraries for <code><a href="${link java_library}">java_library</a></code> and
+ <code>java_binary</code> rules.
+</p>
+
+<h4 id="java_import_examples">Examples</h4>
+
+<pre class="code">
+<code class="lang-starlark">
+ java_import(
+ name = "maven_model",
+ jars = [
+ "maven_model/maven-aether-provider-3.2.3.jar",
+ "maven_model/maven-model-3.2.3.jar",
+ "maven_model/maven-model-builder-3.2.3.jar",
+ ],
+ )
+</code>
+</pre>
+ """,
+ attrs = JAVA_IMPORT_ATTRS,
+ provides = [JavaInfo],
+ fragments = ["java", "cpp"],
+ toolchains = [semantics.JAVA_TOOLCHAIN],
+)
diff --git a/java/common/rules/java_library.bzl b/java/common/rules/java_library.bzl
new file mode 100644
index 0000000..1ca788d
--- /dev/null
+++ b/java/common/rules/java_library.bzl
@@ -0,0 +1,392 @@
+# Copyright 2021 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 java_library rule.
+"""
+
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//java/common:java_info.bzl", "JavaInfo")
+load("//java/common:java_plugin_info.bzl", "JavaPluginInfo")
+load("//java/common:java_semantics.bzl", "semantics")
+load(":android_lint.bzl", "android_lint_subrule")
+load(":basic_java_library.bzl", "BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS", "basic_java_library", "construct_defaultinfo")
+load(":rule_util.bzl", "merge_attrs")
+
+visibility(["//java/..."])
+
+BootClassPathInfo = java_common.BootClassPathInfo
+
+def bazel_java_library_rule(
+ ctx,
+ srcs = [],
+ deps = [],
+ runtime_deps = [],
+ plugins = [],
+ exports = [],
+ exported_plugins = [],
+ resources = [],
+ javacopts = [],
+ neverlink = False,
+ proguard_specs = [],
+ add_exports = [],
+ add_opens = [],
+ bootclasspath = None,
+ javabuilder_jvm_flags = None):
+ """Implements java_library.
+
+ Use this call when you need to produce a fully fledged java_library from
+ another rule's implementation.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ srcs: (list[File]) The list of source files that are processed to create the target.
+ deps: (list[Target]) The list of other libraries to be linked in to the target.
+ runtime_deps: (list[Target]) Libraries to make available to the final binary or test at runtime only.
+ plugins: (list[Target]) Java compiler plugins to run at compile-time.
+ exports: (list[Target]) Exported libraries.
+ exported_plugins: (list[Target]) The list of `java_plugin`s (e.g. annotation
+ processors) to export to libraries that directly depend on this library.
+ resources: (list[File]) A list of data files to include in a Java jar.
+ javacopts: (list[str]) Extra compiler options for this library.
+ neverlink: (bool) Whether this library should only be used for compilation and not at runtime.
+ proguard_specs: (list[File]) Files to be used as Proguard specification.
+ add_exports: (list[str]) Allow this library to access the given <module>/<package>.
+ add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
+ bootclasspath: (Target) The JDK APIs to compile this library against.
+ javabuilder_jvm_flags: (list[str]) Additional JVM flags to pass to JavaBuilder.
+ Returns:
+ (dict[str, provider]) A list containing DefaultInfo, JavaInfo,
+ InstrumentedFilesInfo, OutputGroupsInfo, ProguardSpecProvider providers.
+ """
+ if not srcs and deps:
+ fail("deps not allowed without srcs; move to runtime_deps?")
+
+ target, base_info = basic_java_library(
+ ctx,
+ srcs,
+ deps,
+ runtime_deps,
+ plugins,
+ exports,
+ exported_plugins,
+ resources,
+ [], # resource_jars
+ [], # class_pathresources
+ javacopts,
+ neverlink,
+ proguard_specs = proguard_specs,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ bootclasspath = bootclasspath,
+ javabuilder_jvm_flags = javabuilder_jvm_flags,
+ )
+
+ target["DefaultInfo"] = construct_defaultinfo(
+ ctx,
+ base_info.files_to_build,
+ base_info.runfiles,
+ neverlink,
+ exports,
+ runtime_deps,
+ )
+ target["OutputGroupInfo"] = OutputGroupInfo(**base_info.output_groups)
+
+ return target
+
+def _proxy(ctx):
+ return bazel_java_library_rule(
+ ctx,
+ ctx.files.srcs,
+ ctx.attr.deps,
+ ctx.attr.runtime_deps,
+ ctx.attr.plugins,
+ ctx.attr.exports,
+ ctx.attr.exported_plugins,
+ ctx.files.resources,
+ ctx.attr.javacopts,
+ ctx.attr.neverlink,
+ ctx.files.proguard_specs,
+ ctx.attr.add_exports,
+ ctx.attr.add_opens,
+ ctx.attr.bootclasspath,
+ ctx.attr.javabuilder_jvm_flags,
+ ).values()
+
+JAVA_LIBRARY_IMPLICIT_ATTRS = BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS
+
+JAVA_LIBRARY_ATTRS = merge_attrs(
+ JAVA_LIBRARY_IMPLICIT_ATTRS,
+ # buildifier: disable=attr-licenses
+ {
+ "srcs": attr.label_list(
+ allow_files = [".java", ".srcjar", ".properties"] + semantics.EXTRA_SRCS_TYPES,
+ flags = ["DIRECT_COMPILE_TIME_INPUT", "ORDER_INDEPENDENT"],
+ doc = """
+The list of source files that are processed to create the target.
+This attribute is almost always required; see exceptions below.
+<p>
+Source files of type <code>.java</code> are compiled. In case of generated
+<code>.java</code> files it is generally advisable to put the generating rule's name
+here instead of the name of the file itself. This not only improves readability but
+makes the rule more resilient to future changes: if the generating rule generates
+different files in the future, you only need to fix one place: the <code>outs</code> of
+the generating rule. You should not list the generating rule in <code>deps</code>
+because it is a no-op.
+</p>
+<p>
+Source files of type <code>.srcjar</code> are unpacked and compiled. (This is useful if
+you need to generate a set of <code>.java</code> files with a genrule.)
+</p>
+<p>
+Rules: if the rule (typically <code>genrule</code> or <code>filegroup</code>) generates
+any of the files listed above, they will be used the same way as described for source
+files.
+</p>
+<p>
+Source files of type <code>.properties</code> are treated as resources.
+</p>
+
+<p>All other files are ignored, as long as there is at least one file of a
+file type described above. Otherwise an error is raised.</p>
+
+<p>
+This argument is almost always required, except if you specify the <code>runtime_deps</code> argument.
+</p>
+ """,
+ ),
+ "data": attr.label_list(
+ allow_files = True,
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
+ doc = """
+The list of files needed by this library at runtime.
+See general comments about <code>data</code> at
+<a href="${link common-definitions#typical-attributes}">Typical attributes defined by
+most build rules</a>.
+<p>
+ When building a <code>java_library</code>, Bazel doesn't put these files anywhere; if the
+ <code>data</code> files are generated files then Bazel generates them. When building a
+ test that depends on this <code>java_library</code> Bazel copies or links the
+ <code>data</code> files into the runfiles area.
+</p>
+ """ + semantics.DOCS.for_attribute("data"),
+ ),
+ "resources": attr.label_list(
+ allow_files = True,
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE", "ORDER_INDEPENDENT"],
+ doc = """
+A list of data files to include in a Java jar.
+<p>
+Resources may be source files or generated files.
+</p>
+ """ + semantics.DOCS.for_attribute("resources"),
+ ),
+ "plugins": attr.label_list(
+ providers = [JavaPluginInfo],
+ allow_files = True,
+ cfg = "exec",
+ doc = """
+Java compiler plugins to run at compile-time.
+Every <code>java_plugin</code> specified in this attribute will be run whenever this rule
+is built. A library may also inherit plugins from dependencies that use
+<code><a href="#java_library.exported_plugins">exported_plugins</a></code>. Resources
+generated by the plugin will be included in the resulting jar of this rule.
+ """,
+ ),
+ "deps": attr.label_list(
+ allow_files = [".jar"],
+ allow_rules = semantics.ALLOWED_RULES_IN_DEPS + semantics.ALLOWED_RULES_IN_DEPS_WITH_WARNING,
+ providers = [
+ [CcInfo],
+ [JavaInfo],
+ ],
+ flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
+ doc = """
+The list of libraries to link into this library.
+See general comments about <code>deps</code> at
+<a href="${link common-definitions#typical-attributes}">Typical attributes defined by
+most build rules</a>.
+<p>
+ The jars built by <code>java_library</code> rules listed in <code>deps</code> will be on
+ the compile-time classpath of this rule. Furthermore the transitive closure of their
+ <code>deps</code>, <code>runtime_deps</code> and <code>exports</code> will be on the
+ runtime classpath.
+</p>
+<p>
+ By contrast, targets in the <code>data</code> attribute are included in the runfiles but
+ on neither the compile-time nor runtime classpath.
+</p>
+ """,
+ ),
+ "runtime_deps": attr.label_list(
+ allow_files = [".jar"],
+ allow_rules = semantics.ALLOWED_RULES_IN_DEPS,
+ providers = [[CcInfo], [JavaInfo]],
+ flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
+ doc = """
+Libraries to make available to the final binary or test at runtime only.
+Like ordinary <code>deps</code>, these will appear on the runtime classpath, but unlike
+them, not on the compile-time classpath. Dependencies needed only at runtime should be
+listed here. Dependency-analysis tools should ignore targets that appear in both
+<code>runtime_deps</code> and <code>deps</code>.
+ """,
+ ),
+ "exports": attr.label_list(
+ allow_rules = semantics.ALLOWED_RULES_IN_DEPS,
+ providers = [[JavaInfo], [CcInfo]],
+ doc = """
+Exported libraries.
+<p>
+ Listing rules here will make them available to parent rules, as if the parents explicitly
+ depended on these rules. This is not true for regular (non-exported) <code>deps</code>.
+</p>
+<p>
+ Summary: a rule <i>X</i> can access the code in <i>Y</i> if there exists a dependency
+ path between them that begins with a <code>deps</code> edge followed by zero or more
+ <code>exports</code> edges. Let's see some examples to illustrate this.
+</p>
+<p>
+ Assume <i>A</i> depends on <i>B</i> and <i>B</i> depends on <i>C</i>. In this case
+ C is a <em>transitive</em> dependency of A, so changing C's sources and rebuilding A will
+ correctly rebuild everything. However A will not be able to use classes in C. To allow
+ that, either A has to declare C in its <code>deps</code>, or B can make it easier for A
+ (and anything that may depend on A) by declaring C in its (B's) <code>exports</code>
+ attribute.
+</p>
+<p>
+ The closure of exported libraries is available to all direct parent rules. Take a slightly
+ different example: A depends on B, B depends on C and D, and also exports C but not D.
+ Now A has access to C but not to D. Now, if C and D exported some libraries, C' and D'
+ respectively, A could only access C' but not D'.
+</p>
+<p>
+ Important: an exported rule is not a regular dependency. Sticking to the previous example,
+ if B exports C and wants to also use C, it has to also list it in its own
+ <code>deps</code>.
+</p>
+ """,
+ ),
+ "exported_plugins": attr.label_list(
+ providers = [JavaPluginInfo],
+ cfg = "exec",
+ doc = """
+The list of <code><a href="#${link java_plugin}">java_plugin</a></code>s (e.g. annotation
+processors) to export to libraries that directly depend on this library.
+<p>
+ The specified list of <code>java_plugin</code>s will be applied to any library which
+ directly depends on this library, just as if that library had explicitly declared these
+ labels in <code><a href="${link java_library.plugins}">plugins</a></code>.
+</p>
+ """,
+ ),
+ "bootclasspath": attr.label(
+ providers = [BootClassPathInfo],
+ flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
+ doc = """Restricted API, do not use!""",
+ ),
+ "javabuilder_jvm_flags": attr.string_list(doc = """Restricted API, do not use!"""),
+ "javacopts": attr.string_list(
+ doc = """
+Extra compiler options for this library.
+Subject to <a href="make-variables.html">"Make variable"</a> substitution and
+<a href="common-definitions.html#sh-tokenization">Bourne shell tokenization</a>.
+<p>These compiler options are passed to javac after the global compiler options.</p>
+ """,
+ ),
+ "neverlink": attr.bool(
+ doc = """
+Whether this library should only be used for compilation and not at runtime.
+Useful if the library will be provided by the runtime environment during execution. Examples
+of such libraries are the IDE APIs for IDE plug-ins or <code>tools.jar</code> for anything
+running on a standard JDK.
+<p>
+ Note that <code>neverlink = 1</code> does not prevent the compiler from inlining material
+ from this library into compilation targets that depend on it, as permitted by the Java
+ Language Specification (e.g., <code>static final</code> constants of <code>String</code>
+ or of primitive types). The preferred use case is therefore when the runtime library is
+ identical to the compilation library.
+</p>
+<p>
+ If the runtime library differs from the compilation library then you must ensure that it
+ differs only in places that the JLS forbids compilers to inline (and that must hold for
+ all future versions of the JLS).
+</p>
+ """,
+ ),
+ "resource_strip_prefix": attr.string(
+ doc = """
+The path prefix to strip from Java resources.
+<p>
+If specified, this path prefix is stripped from every file in the <code>resources</code>
+attribute. It is an error for a resource file not to be under this directory. If not
+specified (the default), the path of resource file is determined according to the same
+logic as the Java package of source files. For example, a source file at
+<code>stuff/java/foo/bar/a.txt</code> will be located at <code>foo/bar/a.txt</code>.
+</p>
+ """,
+ ),
+ "proguard_specs": attr.label_list(
+ allow_files = True,
+ doc = """
+Files to be used as Proguard specification.
+These will describe the set of specifications to be used by Proguard. If specified,
+they will be added to any <code>android_binary</code> target depending on this library.
+
+The files included here must only have idempotent rules, namely -dontnote, -dontwarn,
+assumenosideeffects, and rules that start with -keep. Other options can only appear in
+<code>android_binary</code>'s proguard_specs, to ensure non-tautological merges.
+ """,
+ ),
+ "add_exports": attr.string_list(
+ doc = """
+Allow this library to access the given <code>module</code> or <code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-exports= flags.
+ """,
+ ),
+ "add_opens": attr.string_list(
+ doc = """
+Allow this library to reflectively access the given <code>module</code> or
+<code>package</code>.
+<p>
+This corresponds to the javac and JVM --add-opens= flags.
+ """,
+ ),
+ "licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ "_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE),
+ },
+)
+
+java_library = rule(
+ _proxy,
+ doc = """
+<p>This rule compiles and links sources into a <code>.jar</code> file.</p>
+
+<h4>Implicit outputs</h4>
+<ul>
+ <li><code>lib<var>name</var>.jar</code>: A Java archive containing the class files.</li>
+ <li><code>lib<var>name</var>-src.jar</code>: An archive containing the sources ("source
+ jar").</li>
+</ul>
+ """,
+ attrs = JAVA_LIBRARY_ATTRS,
+ provides = [JavaInfo],
+ outputs = {
+ "classjar": "lib%{name}.jar",
+ "sourcejar": "lib%{name}-src.jar",
+ },
+ fragments = ["java", "cpp"],
+ toolchains = [semantics.JAVA_TOOLCHAIN],
+ subrules = [android_lint_subrule],
+)
diff --git a/java/common/rules/java_package_configuration.bzl b/java/common/rules/java_package_configuration.bzl
new file mode 100644
index 0000000..32e3abd
--- /dev/null
+++ b/java/common/rules/java_package_configuration.bzl
@@ -0,0 +1,115 @@
+# Copyright 2023 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.
+
+"""Implementation for the java_package_configuration rule"""
+
+load("//java/common:java_common.bzl", "java_common")
+load(":java_helper.bzl", "helper")
+
+visibility(["//java/..."])
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+
+JavaPackageConfigurationInfo = provider(
+ "A provider for Java per-package configuration",
+ fields = [
+ "data",
+ "javac_opts",
+ "matches",
+ "package_specs",
+ ],
+)
+
+def _matches(package_specs, label):
+ for spec in package_specs:
+ if spec.contains(label):
+ return True
+ return False
+
+def _rule_impl(ctx):
+ javacopts = _java_common_internal.expand_java_opts(ctx, "javacopts", tokenize = True)
+ javacopts_depset = helper.detokenize_javacopts(javacopts)
+ package_specs = [package[PackageSpecificationInfo] for package in ctx.attr.packages]
+ return [
+ DefaultInfo(),
+ JavaPackageConfigurationInfo(
+ data = depset(ctx.files.data),
+ javac_opts = javacopts_depset,
+ matches = lambda label: _matches(package_specs, label),
+ package_specs = package_specs,
+ ),
+ ]
+
+java_package_configuration = rule(
+ implementation = _rule_impl,
+ doc = """
+<p>
+Configuration to apply to a set of packages.
+Configurations can be added to
+<code><a href="${link java_toolchain.javacopts}">java_toolchain.javacopts</a></code>s.
+</p>
+
+<h4 id="java_package_configuration_example">Example:</h4>
+
+<pre class="code">
+<code class="lang-starlark">
+
+java_package_configuration(
+ name = "my_configuration",
+ packages = [":my_packages"],
+ javacopts = ["-Werror"],
+)
+
+package_group(
+ name = "my_packages",
+ packages = [
+ "//com/my/project/...",
+ "-//com/my/project/testing/...",
+ ],
+)
+
+java_toolchain(
+ ...,
+ package_configuration = [
+ ":my_configuration",
+ ]
+)
+
+</code>
+</pre>
+ """,
+ attrs = {
+ "packages": attr.label_list(
+ cfg = "exec",
+ providers = [PackageSpecificationInfo],
+ doc = """
+The set of <code><a href="${link package_group}">package_group</a></code>s
+the configuration should be applied to.
+ """,
+ ),
+ "javacopts": attr.string_list(
+ doc = """
+Java compiler flags.
+ """,
+ ),
+ "data": attr.label_list(
+ allow_files = True,
+ doc = """
+The list of files needed by this configuration at runtime.
+ """,
+ ),
+ # buildifier: disable=attr-licenses
+ "output_licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ },
+)
diff --git a/java/common/rules/java_plugin.bzl b/java/common/rules/java_plugin.bzl
new file mode 100644
index 0000000..4dbf4e9
--- /dev/null
+++ b/java/common/rules/java_plugin.bzl
@@ -0,0 +1,185 @@
+# Copyright 2021 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 java_plugin rule.
+"""
+
+load("//java/common:java_plugin_info.bzl", "JavaPluginInfo")
+load("//java/common:java_semantics.bzl", "semantics")
+load(":android_lint.bzl", "android_lint_subrule")
+load(":basic_java_library.bzl", "basic_java_library", "construct_defaultinfo")
+load(":java_library.bzl", "JAVA_LIBRARY_ATTRS", "JAVA_LIBRARY_IMPLICIT_ATTRS")
+load(":rule_util.bzl", "merge_attrs")
+
+visibility(["//java/..."])
+
+def bazel_java_plugin_rule(
+ ctx,
+ srcs = [],
+ data = [],
+ generates_api = False,
+ processor_class = "",
+ deps = [],
+ plugins = [],
+ resources = [],
+ javacopts = [],
+ neverlink = False,
+ proguard_specs = [],
+ add_exports = [],
+ add_opens = []):
+ """Implements java_plugin rule.
+
+ Use this call when you need to produce a fully fledged java_plugin from
+ another rule's implementation.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ srcs: (list[File]) The list of source files that are processed to create the target.
+ data: (list[File]) The list of files needed by this plugin at runtime.
+ generates_api: (bool) This attribute marks annotation processors that generate API code.
+ processor_class: (str) The processor class is the fully qualified type of
+ the class that the Java compiler should use as entry point to the annotation processor.
+ deps: (list[Target]) The list of other libraries to be linked in to the target.
+ plugins: (list[Target]) Java compiler plugins to run at compile-time.
+ resources: (list[File]) A list of data files to include in a Java jar.
+ javacopts: (list[str]) Extra compiler options for this library.
+ neverlink: (bool) Whether this library should only be used for compilation and not at runtime.
+ proguard_specs: (list[File]) Files to be used as Proguard specification.
+ add_exports: (list[str]) Allow this library to access the given <module>/<package>.
+ add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>.
+ Returns:
+ (list[provider]) A list containing DefaultInfo, JavaInfo,
+ InstrumentedFilesInfo, OutputGroupsInfo, ProguardSpecProvider providers.
+ """
+ target, base_info = basic_java_library(
+ ctx,
+ srcs,
+ deps,
+ [], # runtime_deps
+ plugins,
+ [], # exports
+ [], # exported_plugins
+ resources,
+ [], # resource_jars
+ [], # classpath_resources
+ javacopts,
+ neverlink,
+ proguard_specs = proguard_specs,
+ add_exports = add_exports,
+ add_opens = add_opens,
+ )
+ java_info = target.pop("JavaInfo")
+
+ # Replace JavaInfo with JavaPluginInfo
+ target["JavaPluginInfo"] = JavaPluginInfo(
+ runtime_deps = [java_info],
+ processor_class = processor_class if processor_class else None, # ignore empty string (default)
+ data = data,
+ generates_api = generates_api,
+ )
+ target["DefaultInfo"] = construct_defaultinfo(
+ ctx,
+ base_info.files_to_build,
+ base_info.runfiles,
+ neverlink,
+ )
+ target["OutputGroupInfo"] = OutputGroupInfo(**base_info.output_groups)
+
+ return target
+
+def _proxy(ctx):
+ return bazel_java_plugin_rule(
+ ctx,
+ ctx.files.srcs,
+ ctx.files.data,
+ ctx.attr.generates_api,
+ ctx.attr.processor_class,
+ ctx.attr.deps,
+ ctx.attr.plugins,
+ ctx.files.resources,
+ ctx.attr.javacopts,
+ ctx.attr.neverlink,
+ ctx.files.proguard_specs,
+ ctx.attr.add_exports,
+ ctx.attr.add_opens,
+ ).values()
+
+JAVA_PLUGIN_ATTRS = merge_attrs(
+ JAVA_LIBRARY_ATTRS,
+ {
+ "generates_api": attr.bool(doc = """
+This attribute marks annotation processors that generate API code.
+<p>If a rule uses an API-generating annotation processor, other rules
+depending on it can refer to the generated code only if their
+compilation actions are scheduled after the generating rule. This
+attribute instructs Bazel to introduce scheduling constraints when
+--java_header_compilation is enabled.
+<p><em class="harmful">WARNING: This attribute affects build
+performance, use it only if necessary.</em></p>
+ """),
+ "processor_class": attr.string(doc = """
+The processor class is the fully qualified type of the class that the Java compiler should
+use as entry point to the annotation processor. If not specified, this rule will not
+contribute an annotation processor to the Java compiler's annotation processing, but its
+runtime classpath will still be included on the compiler's annotation processor path. (This
+is primarily intended for use by
+<a href="https://errorprone.info/docs/plugins">Error Prone plugins</a>, which are loaded
+from the annotation processor path using
+<a href="https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html">
+java.util.ServiceLoader</a>.)
+ """),
+ # buildifier: disable=attr-licenses
+ "output_licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ },
+ remove_attrs = ["runtime_deps", "exports", "exported_plugins"],
+)
+
+JAVA_PLUGIN_IMPLICIT_ATTRS = JAVA_LIBRARY_IMPLICIT_ATTRS
+
+java_plugin = rule(
+ _proxy,
+ doc = """
+<p>
+ <code>java_plugin</code> defines plugins for the Java compiler run by Bazel. The
+ only supported kind of plugins are annotation processors. A <code>java_library</code> or
+ <code>java_binary</code> rule can run plugins by depending on them via the <code>plugins</code>
+ attribute. A <code>java_library</code> can also automatically export plugins to libraries that
+ directly depend on it using
+ <code><a href="${link java_library.exported_plugins}">exported_plugins</a></code>.
+</p>
+
+<h4 id="java_plugin_implicit_outputs">Implicit output targets</h4>
+ <ul>
+ <li><code><var>libname</var>.jar</code>: A Java archive.</li>
+ </ul>
+
+<p>
+ Arguments are identical to <a href="${link java_library}"><code>java_library</code></a>, except
+ for the addition of the <code>processor_class</code> argument.
+</p>
+ """,
+ attrs = merge_attrs(
+ JAVA_PLUGIN_ATTRS,
+ JAVA_PLUGIN_IMPLICIT_ATTRS,
+ ),
+ provides = [JavaPluginInfo],
+ outputs = {
+ "classjar": "lib%{name}.jar",
+ "sourcejar": "lib%{name}-src.jar",
+ },
+ fragments = ["java", "cpp"],
+ toolchains = [semantics.JAVA_TOOLCHAIN],
+ subrules = [android_lint_subrule],
+)
diff --git a/java/common/rules/java_runtime.bzl b/java/common/rules/java_runtime.bzl
new file mode 100644
index 0000000..af76f49
--- /dev/null
+++ b/java/common/rules/java_runtime.bzl
@@ -0,0 +1,260 @@
+# Copyright 2023 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 java_runtime rule and JavaRuntimeInfo provider.
+"""
+
+load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
+load("//java/common:java_semantics.bzl", "PLATFORMS_ROOT")
+load("//third_party/bazel_skylib/lib:paths.bzl", "paths")
+load(":java_helper.bzl", "helper")
+
+visibility(["//java/..."])
+
+ToolchainInfo = platform_common.ToolchainInfo
+
+def _init_java_runtime_info(**_kwargs):
+ fail("instantiating JavaRuntimeInfo is a private API")
+
+JavaRuntimeInfo, _new_javaruntimeinfo = provider(
+ doc = "Information about the Java runtime used by the java rules.",
+ fields = {
+ "default_cds": "Returns the JDK default CDS archive.",
+ "files": "Returns the files in the Java runtime.",
+ "hermetic_files": "Returns the files in the Java runtime needed for hermetic deployments.",
+ "hermetic_static_libs": "Returns the JDK static libraries.",
+ "java_executable_exec_path": "Returns the execpath of the Java executable.",
+ "java_executable_runfiles_path": """Returns the path of the Java executable in
+ runfiles trees. This should only be used when one needs to access the
+ JVM during the execution of a binary or a test built by Bazel. In particular,
+ when one needs to invoke the JVM during an action, java_executable_exec_path
+ should be used instead.""",
+ "java_home": "Returns the execpath of the root of the Java installation.",
+ "java_home_runfiles_path": """Returns the path of the Java installation in runfiles trees.
+ This should only be used when one needs to access the JDK during the execution
+ of a binary or a test built by Bazel. In particular, when one needs the JDK
+ during an action, java_home should be used instead.""",
+ "lib_ct_sym": "Returns the lib/ct.sym file.",
+ "lib_modules": "Returns the lib/modules file.",
+ "version": "The Java feature version of the runtime. This is 0 if the version is unknown.",
+ },
+ init = _init_java_runtime_info,
+)
+
+def _is_main_repo(label):
+ return label.workspace_name == ""
+
+def _default_java_home(label):
+ if _is_main_repo(label):
+ return label.package
+ else:
+ return paths.get_relative(label.workspace_root, label.package)
+
+def _get_bin_java(ctx):
+ is_windows = helper.is_target_platform_windows(ctx)
+ return "bin/java.exe" if is_windows else "bin/java"
+
+def _get_runfiles_java_executable(ctx, java_home, label):
+ if paths.is_absolute(java_home) or _is_main_repo(label):
+ return paths.get_relative(java_home, _get_bin_java(ctx))
+ else:
+ repo_runfiles_path = "" if _is_main_repo(label) else paths.get_relative("..", label.workspace_name)
+ return paths.get_relative(repo_runfiles_path, _get_bin_java(ctx))
+
+def _is_java_binary(path):
+ return path.endswith("bin/java") or path.endswith("bin/java.exe")
+
+def _get_lib_ct_sym(srcs, explicit_lib_ct_sym):
+ if explicit_lib_ct_sym:
+ return explicit_lib_ct_sym
+ candidates = [src for src in srcs if src.path.endswith("/lib/ct.sym")]
+ if len(candidates) == 1:
+ return candidates[0]
+ else:
+ return None
+
+def _java_runtime_rule_impl(ctx):
+ all_files = [] # [depset[File]]
+ all_files.append(depset(ctx.files.srcs))
+
+ java_home = _default_java_home(ctx.label)
+ if ctx.attr.java_home:
+ java_home_attr = ctx.expand_make_variables("java_home", ctx.attr.java_home, {})
+ if ctx.files.srcs and paths.is_absolute(java_home_attr):
+ fail("'java_home' with an absolute path requires 'srcs' to be empty.")
+ java_home = paths.get_relative(java_home, java_home_attr)
+
+ java_binary_exec_path = paths.get_relative(java_home, _get_bin_java(ctx))
+ java_binary_runfiles_path = _get_runfiles_java_executable(ctx, java_home, ctx.label)
+
+ java = ctx.file.java
+ if java:
+ if paths.is_absolute(java_home):
+ fail("'java_home' with an absolute path requires 'java' to be empty.")
+ java_binary_exec_path = java.path
+ java_binary_runfiles_path = java.short_path
+ if not _is_java_binary(java_binary_exec_path):
+ fail("the path to 'java' must end in 'bin/java'.")
+ java_home = paths.dirname(paths.dirname(java_binary_exec_path))
+ all_files.append(depset([java]))
+
+ java_home_runfiles_path = paths.dirname(paths.dirname(java_binary_runfiles_path))
+
+ hermetic_inputs = depset(ctx.files.hermetic_srcs)
+ all_files.append(hermetic_inputs)
+
+ lib_ct_sym = _get_lib_ct_sym(ctx.files.srcs, ctx.file.lib_ct_sym)
+ lib_modules = ctx.file.lib_modules
+ hermetic_static_libs = [dep[CcInfo] for dep in ctx.attr.hermetic_static_libs]
+
+ # If a runtime does not set default_cds in hermetic mode, it is not fatal.
+ # We can skip the default CDS in the check below.
+ default_cds = ctx.file.default_cds
+
+ if (hermetic_inputs or lib_modules or hermetic_static_libs) and (
+ not hermetic_inputs or not lib_modules or not hermetic_static_libs
+ ):
+ fail("hermetic specified, all of java_runtime.lib_modules, java_runtime.hermetic_srcs and java_runtime.hermetic_static_libs must be specified")
+
+ files = depset(transitive = all_files)
+
+ java_runtime_info = _new_javaruntimeinfo(
+ default_cds = default_cds,
+ files = files,
+ hermetic_files = hermetic_inputs,
+ hermetic_static_libs = hermetic_static_libs,
+ java_executable_exec_path = java_binary_exec_path,
+ java_executable_runfiles_path = java_binary_runfiles_path,
+ java_home = java_home,
+ java_home_runfiles_path = java_home_runfiles_path,
+ lib_ct_sym = lib_ct_sym,
+ lib_modules = lib_modules,
+ version = ctx.attr.version,
+ )
+ return [
+ DefaultInfo(
+ files = files,
+ runfiles = ctx.runfiles(transitive_files = files),
+ ),
+ java_runtime_info,
+ platform_common.TemplateVariableInfo({
+ "JAVA": java_binary_exec_path,
+ "JAVABASE": java_home,
+ }),
+ ToolchainInfo(java_runtime = java_runtime_info),
+ ]
+
+java_runtime = rule(
+ implementation = _java_runtime_rule_impl,
+ doc = """
+<p>
+Specifies the configuration for a Java runtime.
+</p>
+
+<h4 id="java_runtime_example">Example:</h4>
+
+<pre class="code">
+<code class="lang-starlark">
+
+java_runtime(
+ name = "jdk-9-ea+153",
+ srcs = glob(["jdk9-ea+153/**"]),
+ java_home = "jdk9-ea+153",
+)
+
+</code>
+</pre>
+ """,
+ attrs = {
+ "default_cds": attr.label(
+ allow_single_file = True,
+ executable = True,
+ cfg = "target",
+ doc = """
+Default CDS archive for hermetic <code>java_runtime</code>. When hermetic
+is enabled for a <code>java_binary</code> target and if the target does not
+provide its own CDS archive by specifying the
+<a href="${link java_binary.classlist}"><code>classlist</code></a> attribute,
+the <code>java_runtime</code> default CDS is packaged in the hermetic deploy JAR.
+ """,
+ ),
+ "hermetic_srcs": attr.label_list(
+ allow_files = True,
+ doc = """
+Files in the runtime needed for hermetic deployments.
+ """,
+ ),
+ "hermetic_static_libs": attr.label_list(
+ providers = [CcInfo],
+ doc = """
+The libraries that are statically linked with the launcher for hermetic deployments
+ """,
+ ),
+ "java": attr.label(
+ allow_single_file = True,
+ executable = True,
+ cfg = "target",
+ doc = """
+The path to the java executable.
+ """,
+ ),
+ "java_home": attr.string(
+ doc = """
+The path to the root of the runtime.
+Subject to <a href="${link make-variables}">"Make" variable</a> substitution.
+If this path is absolute, the rule denotes a non-hermetic Java runtime with a well-known
+path. In that case, the <code>srcs</code> and <code>java</code> attributes must be empty.
+ """,
+ ),
+ "lib_ct_sym": attr.label(
+ allow_single_file = True,
+ doc = """
+The lib/ct.sym file needed for compilation with <code>--release</code>. If not specified and
+there is exactly one file in <code>srcs</code> whose path ends with
+<code>/lib/ct.sym</code>, that file is used.
+ """,
+ ),
+ "lib_modules": attr.label(
+ allow_single_file = True,
+ executable = True,
+ cfg = "target",
+ doc = """
+The lib/modules file needed for hermetic deployments.
+ """,
+ ),
+ "srcs": attr.label_list(
+ allow_files = True,
+ doc = """
+All files in the runtime.
+ """,
+ ),
+ "version": attr.int(
+ doc = """
+The feature version of the Java runtime. I.e., the integer returned by
+<code>Runtime.version().feature()</code>.
+ """,
+ ),
+ # buildifier: disable=attr-licenses
+ "output_licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ "_windows_constraints": attr.label_list(
+ default = [paths.join(PLATFORMS_ROOT, "os:windows")],
+ ),
+ },
+ fragments = ["java"],
+ provides = [
+ JavaRuntimeInfo,
+ platform_common.TemplateVariableInfo,
+ ],
+)
diff --git a/java/common/rules/java_toolchain.bzl b/java/common/rules/java_toolchain.bzl
new file mode 100644
index 0000000..8e0f583
--- /dev/null
+++ b/java/common/rules/java_toolchain.bzl
@@ -0,0 +1,604 @@
+# Copyright 2023 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 java_toolchain rule and JavaToolchainInfo provider.
+"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+load(":java_helper.bzl", "helper")
+load(":java_package_configuration.bzl", "JavaPackageConfigurationInfo")
+load(":java_runtime.bzl", "JavaRuntimeInfo")
+
+visibility(["//java/..."])
+
+_java_common_internal = java_common.internal_DO_NOT_USE()
+ToolchainInfo = platform_common.ToolchainInfo
+BootClassPathInfo = java_common.BootClassPathInfo
+JavaPluginDataInfo = _java_common_internal.JavaPluginDataInfo
+
+def _java_toolchain_info_init(**_kwargs):
+ fail("JavaToolchainInfo instantiation is a private API")
+
+_PRIVATE_API_DOC_STRING = "internal API, DO NOT USE!"
+
+JavaToolchainInfo, _new_javatoolchaininfo = provider(
+ doc = "Information about the JDK used by the <code>java_*</code> rules.",
+ fields = {
+ "bootclasspath": "(depset[File]) The Java target bootclasspath entries. Corresponds to javac's -bootclasspath flag.",
+ "ijar": "(FilesToRunProvider) The ijar executable.",
+ "jacocorunner": "(FilesToRunProvider) The jacocorunner used by the toolchain.",
+ "java_runtime": "(JavaRuntimeInfo) The java runtime information.",
+ "jvm_opt": "(depset[str]) The default options for the JVM running the java compiler and associated tools.",
+ "label": "(label) The toolchain label.",
+ "proguard_allowlister": "(FilesToRunProvider) The binary to validate proguard configuration.",
+ "single_jar": "(FilesToRunProvider) The SingleJar deploy jar.",
+ "source_version": "(str) The java source version.",
+ "target_version": "(str) The java target version.",
+ "tools": "(depset[File]) The compilation tools.",
+ # private
+ "_android_linter": _PRIVATE_API_DOC_STRING,
+ "_bootclasspath_info": _PRIVATE_API_DOC_STRING,
+ "_bytecode_optimizer": _PRIVATE_API_DOC_STRING,
+ "_compatible_javacopts": _PRIVATE_API_DOC_STRING,
+ "_deps_checker": _PRIVATE_API_DOC_STRING,
+ "_forcibly_disable_header_compilation": _PRIVATE_API_DOC_STRING,
+ "_gen_class": _PRIVATE_API_DOC_STRING,
+ "_header_compiler": _PRIVATE_API_DOC_STRING,
+ "_header_compiler_builtin_processors": _PRIVATE_API_DOC_STRING,
+ "_header_compiler_direct": _PRIVATE_API_DOC_STRING,
+ "_javabuilder": _PRIVATE_API_DOC_STRING,
+ "_javacopts": _PRIVATE_API_DOC_STRING,
+ "_javacopts_list": _PRIVATE_API_DOC_STRING,
+ "_javac_supports_workers": _PRIVATE_API_DOC_STRING,
+ "_javac_supports_multiplex_workers": _PRIVATE_API_DOC_STRING,
+ "_javac_supports_worker_cancellation": _PRIVATE_API_DOC_STRING,
+ "_javac_supports_worker_multiplex_sandboxing": _PRIVATE_API_DOC_STRING,
+ "_jspecify_info": _PRIVATE_API_DOC_STRING,
+ "_local_java_optimization_config": _PRIVATE_API_DOC_STRING,
+ "_one_version_tool": _PRIVATE_API_DOC_STRING,
+ "_one_version_allowlist": _PRIVATE_API_DOC_STRING,
+ "_one_version_allowlist_for_tests": _PRIVATE_API_DOC_STRING,
+ "_package_configuration": _PRIVATE_API_DOC_STRING,
+ "_reduced_classpath_incompatible_processors": _PRIVATE_API_DOC_STRING,
+ "_timezone_data": _PRIVATE_API_DOC_STRING,
+ },
+ init = _java_toolchain_info_init,
+)
+
+def _java_toolchain_impl(ctx):
+ javac_opts_list = _get_javac_opts(ctx)
+ bootclasspath_info = _get_bootclasspath_info(ctx)
+ java_runtime = _get_java_runtime(ctx)
+ if java_runtime and java_runtime.lib_ct_sym:
+ header_compiler_direct_data = [java_runtime.lib_ct_sym]
+ header_compiler_direct_jvm_opts = ["-Dturbine.ctSymPath=" + java_runtime.lib_ct_sym.path]
+ elif java_runtime and java_runtime.java_home:
+ # Turbine finds ct.sym relative to java.home.
+ header_compiler_direct_data = []
+ header_compiler_direct_jvm_opts = ["-Djava.home=" + java_runtime.java_home]
+ else:
+ header_compiler_direct_data = []
+ header_compiler_direct_jvm_opts = []
+ java_toolchain_info = _new_javatoolchaininfo(
+ bootclasspath = bootclasspath_info.bootclasspath,
+ ijar = ctx.attr.ijar.files_to_run if ctx.attr.ijar else None,
+ jacocorunner = ctx.attr.jacocorunner.files_to_run if ctx.attr.jacocorunner else None,
+ java_runtime = java_runtime,
+ jvm_opt = depset(_java_common_internal.expand_java_opts(ctx, "jvm_opts", tokenize = False, exec_paths = True)),
+ label = ctx.label,
+ proguard_allowlister = ctx.attr.proguard_allowlister.files_to_run if ctx.attr.proguard_allowlister else None,
+ single_jar = ctx.attr.singlejar.files_to_run,
+ source_version = ctx.attr.source_version,
+ target_version = ctx.attr.target_version,
+ tools = depset(ctx.files.tools),
+ # private
+ _android_linter = _get_android_lint_tool(ctx),
+ _bootclasspath_info = bootclasspath_info,
+ _bytecode_optimizer = _get_tool_from_executable(ctx, "_bytecode_optimizer"),
+ _compatible_javacopts = _get_compatible_javacopts(ctx),
+ _deps_checker = ctx.file.deps_checker,
+ _forcibly_disable_header_compilation = ctx.attr.forcibly_disable_header_compilation,
+ _gen_class = ctx.file.genclass,
+ _header_compiler = _get_tool_from_ctx(ctx, "header_compiler", "turbine_data", "turbine_jvm_opts"),
+ _header_compiler_builtin_processors = depset(ctx.attr.header_compiler_builtin_processors),
+ _header_compiler_direct = _get_tool_from_executable(
+ ctx,
+ "header_compiler_direct",
+ data = header_compiler_direct_data,
+ jvm_opts = header_compiler_direct_jvm_opts,
+ ),
+ _javabuilder = _get_tool_from_ctx(ctx, "javabuilder", "javabuilder_data", "javabuilder_jvm_opts"),
+ _javacopts = helper.detokenize_javacopts(javac_opts_list),
+ _javacopts_list = javac_opts_list,
+ _javac_supports_workers = ctx.attr.javac_supports_workers,
+ _javac_supports_multiplex_workers = ctx.attr.javac_supports_multiplex_workers,
+ _javac_supports_worker_cancellation = ctx.attr.javac_supports_worker_cancellation,
+ _javac_supports_worker_multiplex_sandboxing = ctx.attr.javac_supports_worker_multiplex_sandboxing,
+ _jspecify_info = _get_jspecify_info(ctx),
+ _local_java_optimization_config = ctx.files._local_java_optimization_configuration,
+ _one_version_tool = ctx.attr.oneversion.files_to_run if ctx.attr.oneversion else None,
+ _one_version_allowlist = ctx.file.oneversion_whitelist,
+ _one_version_allowlist_for_tests = ctx.file.oneversion_allowlist_for_tests,
+ _package_configuration = [dep[JavaPackageConfigurationInfo] for dep in ctx.attr.package_configuration],
+ _reduced_classpath_incompatible_processors = depset(ctx.attr.reduced_classpath_incompatible_processors, order = "preorder"),
+ _timezone_data = ctx.file.timezone_data,
+ )
+ toolchain_info = ToolchainInfo(java = java_toolchain_info)
+ return [java_toolchain_info, toolchain_info, DefaultInfo()]
+
+def _get_bootclasspath_info(ctx):
+ bootclasspath_infos = [dep[BootClassPathInfo] for dep in ctx.attr.bootclasspath if BootClassPathInfo in dep]
+ if bootclasspath_infos:
+ if len(bootclasspath_infos) != 1:
+ fail("in attribute 'bootclasspath': expected exactly one entry with a BootClassPathInfo provider")
+ else:
+ return bootclasspath_infos[0]
+ else:
+ return BootClassPathInfo(bootclasspath = ctx.files.bootclasspath)
+
+def _get_java_runtime(ctx):
+ if not ctx.attr.java_runtime:
+ return None
+ return ctx.attr.java_runtime[ToolchainInfo].java_runtime
+
+def _get_javac_opts(ctx):
+ opts = []
+ if ctx.attr.source_version:
+ opts.extend(["-source", ctx.attr.source_version])
+ if ctx.attr.target_version:
+ opts.extend(["-target", ctx.attr.target_version])
+ if ctx.attr.xlint:
+ opts.append("-Xlint:" + ",".join(ctx.attr.xlint))
+ opts.extend(_java_common_internal.expand_java_opts(ctx, "misc", tokenize = True))
+ opts.extend(_java_common_internal.expand_java_opts(ctx, "javacopts", tokenize = True))
+ return opts
+
+def _get_android_lint_tool(ctx):
+ if not ctx.attr.android_lint_runner:
+ return None
+ files_to_run = ctx.attr.android_lint_runner.files_to_run
+ if not files_to_run or not files_to_run.executable:
+ fail(ctx.attr.android_lint_runner.label, "does not refer to a valid executable target")
+ return struct(
+ tool = files_to_run,
+ data = depset(ctx.files.android_lint_data),
+ jvm_opts = depset([ctx.expand_location(opt, ctx.attr.android_lint_data) for opt in ctx.attr.android_lint_jvm_opts]),
+ lint_opts = [ctx.expand_location(opt, ctx.attr.android_lint_data) for opt in ctx.attr.android_lint_opts],
+ package_config = [dep[JavaPackageConfigurationInfo] for dep in ctx.attr.android_lint_package_configuration],
+ )
+
+def _get_tool_from_ctx(ctx, tool_attr, data_attr, opts_attr):
+ dep = getattr(ctx.attr, tool_attr)
+ if not dep:
+ return None
+ files_to_run = dep.files_to_run
+ if not files_to_run or not files_to_run.executable:
+ fail(dep.label, "does not refer to a valid executable target")
+ data = getattr(ctx.attr, data_attr)
+ return struct(
+ tool = files_to_run,
+ data = depset(getattr(ctx.files, data_attr)),
+ jvm_opts = depset([ctx.expand_location(opt, data) for opt in getattr(ctx.attr, opts_attr)]),
+ )
+
+def _get_tool_from_executable(ctx, attr_name, data = [], jvm_opts = []):
+ dep = getattr(ctx.attr, attr_name)
+ if not dep:
+ return None
+ files_to_run = dep.files_to_run
+ if not files_to_run or not files_to_run.executable:
+ fail(dep.label, "does not refer to a valid executable target")
+ return struct(tool = files_to_run, data = depset(data), jvm_opts = depset(jvm_opts))
+
+def _get_compatible_javacopts(ctx):
+ result = {}
+ for key, opt_list in ctx.attr.compatible_javacopts.items():
+ result[key] = helper.detokenize_javacopts([token for opt in opt_list for token in ctx.tokenize(opt)])
+ return result
+
+def _get_jspecify_info(ctx):
+ if not ctx.attr.jspecify_processor_class:
+ return None
+ stubs = ctx.files.jspecify_stubs
+ javacopts = []
+ javacopts.extend(ctx.attr.jspecify_javacopts)
+ if stubs:
+ javacopts.append("-Astubs=" + ":".join([file.path for file in stubs]))
+ return struct(
+ processor = JavaPluginDataInfo(
+ processor_classes = depset([ctx.attr.jspecify_processor_class]),
+ processor_jars = depset([ctx.file.jspecify_processor]),
+ processor_data = depset(stubs),
+ ),
+ implicit_deps = depset([ctx.file.jspecify_implicit_deps]),
+ javacopts = javacopts,
+ packages = [target[PackageSpecificationInfo] for target in ctx.attr.jspecify_packages],
+ )
+
+def _extract_singleton_list_value(dict, key):
+ if key in dict and type(dict[key]) == type([]):
+ list = dict[key]
+ if len(list) > 1:
+ fail("expected a single value for:", key, "got: ", list)
+ elif len(list) == 1:
+ dict[key] = dict[key][0]
+ else:
+ dict[key] = None
+
+_LEGACY_ANY_TYPE_ATTRS = [
+ "genclass",
+ "deps_checker",
+ "header_compiler",
+ "header_compiler_direct",
+ "ijar",
+ "javabuilder",
+ "singlejar",
+]
+
+def _java_toolchain_initializer(**kwargs):
+ # these attributes are defined as executable `label_list`s in native but are
+ # expected to be singleton values. Since this is not supported in Starlark,
+ # we just inline the value from the list (if present) before invoking the
+ # rule.
+ for attr in _LEGACY_ANY_TYPE_ATTRS:
+ _extract_singleton_list_value(kwargs, attr)
+
+ return kwargs
+
+java_toolchain = rule(
+ implementation = _java_toolchain_impl,
+ initializer = _java_toolchain_initializer,
+ doc = """
+<p>
+Specifies the configuration for the Java compiler. Which toolchain to be used can be changed through
+the --java_toolchain argument. Normally you should not write those kind of rules unless you want to
+tune your Java compiler.
+</p>
+
+<h4>Examples</h4>
+
+<p>A simple example would be:
+</p>
+
+<pre class="code">
+<code class="lang-starlark">
+
+java_toolchain(
+ name = "toolchain",
+ source_version = "7",
+ target_version = "7",
+ bootclasspath = ["//tools/jdk:bootclasspath"],
+ xlint = [ "classfile", "divzero", "empty", "options", "path" ],
+ javacopts = [ "-g" ],
+ javabuilder = ":JavaBuilder_deploy.jar",
+)
+</code>
+</pre>
+ """,
+ # buildifier: disable=attr-licenses
+ attrs = {
+ "android_lint_data": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """
+Labels of tools available for label-expansion in android_lint_jvm_opts.
+ """,
+ ),
+ "android_lint_opts": attr.string_list(
+ default = [],
+ doc = """
+The list of Android Lint arguments.
+ """,
+ ),
+ "android_lint_jvm_opts": attr.string_list(
+ default = [],
+ doc = """
+The list of arguments for the JVM when invoking Android Lint.
+ """,
+ ),
+ "android_lint_package_configuration": attr.label_list(
+ cfg = "exec",
+ providers = [JavaPackageConfigurationInfo],
+ allow_files = True,
+ doc = """
+Android Lint Configuration that should be applied to the specified package groups.
+ """,
+ ),
+ "android_lint_runner": attr.label(
+ cfg = "exec",
+ executable = True,
+ allow_single_file = True,
+ doc = """
+Label of the Android Lint runner, if any.
+ """,
+ ),
+ "bootclasspath": attr.label_list(
+ default = [],
+ allow_files = True,
+ doc = """
+The Java target bootclasspath entries. Corresponds to javac's -bootclasspath flag.
+ """,
+ ),
+ "compatible_javacopts": attr.string_list_dict(
+ doc = """Internal API, do not use!""",
+ ),
+ "deps_checker": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ executable = True,
+ doc = """
+Label of the ImportDepsChecker deploy jar.
+ """,
+ ),
+ "forcibly_disable_header_compilation": attr.bool(
+ default = False,
+ doc = """
+Overrides --java_header_compilation to disable header compilation on platforms that do not
+support it, e.g. JDK 7 Bazel.
+ """,
+ ),
+ "genclass": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ executable = True,
+ doc = """
+Label of the GenClass deploy jar.
+ """,
+ ),
+ "header_compiler": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ executable = True,
+ doc = """
+Label of the header compiler. Required if --java_header_compilation is enabled.
+ """,
+ ),
+ "header_compiler_direct": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ executable = True,
+ doc = """
+Optional label of the header compiler to use for direct classpath actions that do not
+include any API-generating annotation processors.
+
+<p>This tool does not support annotation processing.
+ """,
+ ),
+ "header_compiler_builtin_processors": attr.string_list(
+ doc = """Internal API, do not use!""",
+ ),
+ "ijar": attr.label(
+ cfg = "exec",
+ allow_files = True,
+ executable = True,
+ doc = """
+Label of the ijar executable.
+ """,
+ ),
+ "jacocorunner": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ executable = True,
+ doc = """
+Label of the JacocoCoverageRunner deploy jar.
+ """,
+ ),
+ "javabuilder": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ executable = True,
+ doc = """
+Label of the JavaBuilder deploy jar.
+ """,
+ ),
+ "javabuilder_data": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """
+Labels of data available for label-expansion in javabuilder_jvm_opts.
+ """,
+ ),
+ "javabuilder_jvm_opts": attr.string_list(
+ doc = """
+The list of arguments for the JVM when invoking JavaBuilder.
+ """,
+ ),
+ "java_runtime": attr.label(
+ cfg = "exec",
+ providers = [JavaRuntimeInfo],
+ doc = """
+The java_runtime to use with this toolchain. It defaults to java_runtime
+in execution configuration.
+ """,
+ ),
+ "javac_supports_workers": attr.bool(
+ default = True,
+ doc = """
+True if JavaBuilder supports running as a persistent worker, false if it doesn't.
+ """,
+ ),
+ "javac_supports_multiplex_workers": attr.bool(
+ default = True,
+ doc = """
+True if JavaBuilder supports running as a multiplex persistent worker, false if it doesn't.
+ """,
+ ),
+ "javac_supports_worker_cancellation": attr.bool(
+ default = True,
+ doc = """
+True if JavaBuilder supports cancellation of persistent workers, false if it doesn't.
+ """,
+ ),
+ "javac_supports_worker_multiplex_sandboxing": attr.bool(
+ default = False,
+ doc = """
+True if JavaBuilder supports running as a multiplex persistent worker with sandboxing, false if it doesn't.
+ """,
+ ),
+ "javacopts": attr.string_list(
+ default = [],
+ doc = """
+The list of extra arguments for the Java compiler. Please refer to the Java compiler
+documentation for the extensive list of possible Java compiler flags.
+ """,
+ ),
+ "jspecify_implicit_deps": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ executable = True,
+ doc = """Experimental, do not use!""",
+ ),
+ "jspecify_javacopts": attr.string_list(
+ doc = """Experimental, do not use!""",
+ ),
+ "jspecify_packages": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ providers = [PackageSpecificationInfo],
+ doc = """Experimental, do not use!""",
+ ),
+ "jspecify_processor": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ executable = True,
+ doc = """Experimental, do not use!""",
+ ),
+ "jspecify_processor_class": attr.string(
+ doc = """Experimental, do not use!""",
+ ),
+ "jspecify_stubs": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """Experimental, do not use!""",
+ ),
+ "jvm_opts": attr.string_list(
+ default = [],
+ doc = """
+The list of arguments for the JVM when invoking the Java compiler. Please refer to the Java
+virtual machine documentation for the extensive list of possible flags for this option.
+ """,
+ ),
+ "misc": attr.string_list(
+ default = [],
+ doc = """Deprecated: use javacopts instead""",
+ ),
+ "oneversion": attr.label(
+ cfg = "exec",
+ allow_files = True,
+ executable = True,
+ doc = """
+Label of the one-version enforcement binary.
+ """,
+ ),
+ "oneversion_whitelist": attr.label(
+ allow_single_file = True,
+ doc = """
+Label of the one-version allowlist.
+ """,
+ ),
+ "oneversion_allowlist_for_tests": attr.label(
+ allow_single_file = True,
+ doc = """
+Label of the one-version allowlist for tests.
+ """,
+ ),
+ "package_configuration": attr.label_list(
+ cfg = "exec",
+ providers = [JavaPackageConfigurationInfo],
+ doc = """
+Configuration that should be applied to the specified package groups.
+ """,
+ ),
+ "proguard_allowlister": attr.label(
+ cfg = "exec",
+ executable = True,
+ allow_files = True,
+ default = semantics.PROGUARD_ALLOWLISTER_LABEL,
+ doc = """
+Label of the Proguard allowlister.
+ """,
+ ),
+ "reduced_classpath_incompatible_processors": attr.string_list(
+ doc = """Internal API, do not use!""",
+ ),
+ "singlejar": attr.label(
+ cfg = "exec",
+ allow_files = True,
+ executable = True,
+ doc = """
+Label of the SingleJar deploy jar.
+ """,
+ ),
+ "source_version": attr.string(
+ doc = """
+The Java source version (e.g., '6' or '7'). It specifies which set of code structures
+are allowed in the Java source code.
+ """,
+ ),
+ "target_version": attr.string(
+ doc = """
+The Java target version (e.g., '6' or '7'). It specifies for which Java runtime the class
+should be build.
+ """,
+ ),
+ "timezone_data": attr.label(
+ cfg = "exec",
+ allow_single_file = True,
+ doc = """
+Label of a resource jar containing timezone data. If set, the timezone data is added as an
+implicitly runtime dependency of all java_binary rules.
+ """,
+ ),
+ "tools": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """
+Labels of tools available for label-expansion in jvm_opts.
+ """,
+ ),
+ "turbine_data": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """
+Labels of data available for label-expansion in turbine_jvm_opts.
+ """,
+ ),
+ "turbine_jvm_opts": attr.string_list(
+ doc = """
+The list of arguments for the JVM when invoking turbine.
+ """,
+ ),
+ "xlint": attr.string_list(
+ default = [],
+ doc = """
+The list of warning to add or removes from default list. Precedes it with a dash to
+removes it. Please see the Javac documentation on the -Xlint options for more information.
+ """,
+ ),
+ "licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
+ "_bytecode_optimizer": attr.label(
+ cfg = "exec",
+ executable = True,
+ default = configuration_field(fragment = "java", name = "java_toolchain_bytecode_optimizer"),
+ ),
+ "_local_java_optimization_configuration": attr.label(
+ cfg = "exec",
+ default = configuration_field(fragment = "java", name = "local_java_optimization_configuration"),
+ allow_files = True,
+ ),
+ "_legacy_any_type_attrs": attr.string_list(default = _LEGACY_ANY_TYPE_ATTRS),
+ },
+ fragments = ["java"],
+)
diff --git a/java/common/rules/proguard_validation.bzl b/java/common/rules/proguard_validation.bzl
new file mode 100644
index 0000000..18142b3
--- /dev/null
+++ b/java/common/rules/proguard_validation.bzl
@@ -0,0 +1,71 @@
+# Copyright 2021 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.
+
+"""
+Proguard
+"""
+
+load("//java/common:java_semantics.bzl", "semantics")
+load("//java/common:proguard_spec_info.bzl", "ProguardSpecInfo")
+
+visibility("private")
+
+def _filter_provider(provider, *attrs):
+ return [dep[provider] for attr in attrs for dep in attr if provider in dep]
+
+def _validate_spec(ctx, spec_file):
+ validated_proguard_spec = ctx.actions.declare_file(
+ "validated_proguard/%s/%s_valid" % (ctx.label.name, spec_file.path),
+ )
+
+ toolchain = semantics.find_java_toolchain(ctx)
+
+ args = ctx.actions.args()
+ args.add("--path", spec_file)
+ args.add("--output", validated_proguard_spec)
+
+ ctx.actions.run(
+ mnemonic = "ValidateProguard",
+ progress_message = "Validating proguard configuration %{input}",
+ executable = toolchain.proguard_allowlister,
+ arguments = [args],
+ inputs = [spec_file],
+ outputs = [validated_proguard_spec],
+ toolchain = Label(semantics.JAVA_TOOLCHAIN_TYPE),
+ )
+
+ return validated_proguard_spec
+
+def validate_proguard_specs(ctx, proguard_specs = [], transitive_attrs = []):
+ """
+ Creates actions that validate Proguard specification and returns ProguardSpecProvider.
+
+ Use transtive_attrs parameter to collect Proguard validations from `deps`,
+ `runtime_deps`, `exports`, `plugins`, and `exported_plugins` attributes.
+
+ Args:
+ ctx: (RuleContext) Used to register the actions.
+ proguard_specs: (list[File]) List of Proguard specs files.
+ transitive_attrs: (list[list[Target]]) Attributes to collect transitive
+ proguard validations from.
+ Returns:
+ (ProguardSpecProvider) A ProguardSpecProvider.
+ """
+ proguard_validations = _filter_provider(ProguardSpecInfo, *transitive_attrs)
+ return ProguardSpecInfo(
+ depset(
+ [_validate_spec(ctx, spec_file) for spec_file in proguard_specs],
+ transitive = [validation.specs for validation in proguard_validations],
+ ),
+ )
diff --git a/java/common/rules/rule_util.bzl b/java/common/rules/rule_util.bzl
new file mode 100644
index 0000000..2b1423a
--- /dev/null
+++ b/java/common/rules/rule_util.bzl
@@ -0,0 +1,50 @@
+# Copyright 2021 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.
+"""Defines rule utilities."""
+
+visibility(["//java/..."])
+
+def merge_attrs(*attribute_dicts, override_attrs = {}, remove_attrs = []):
+ """Merges attributes together.
+
+ Attributes are first merged, then overridden and removed.
+
+ If there are duplicate definitions of an attribute, the last one is used.
+ (Current API doesn't let us compare)
+
+ Overridden and removed attributes need to be present.
+
+ Args:
+ *attribute_dicts: (*dict[str,Attribute]) A list of attribute dictionaries
+ to merge together.
+ override_attrs: (dict[str,Attribute]) A dictionary of attributes to override
+ remove_attrs: (list[str]) A list of attributes to remove.
+ Returns:
+ (dict[str,Attribute]) The merged attributes dictionary.
+ """
+ all_attributes = {}
+ for attribute_dict in attribute_dicts:
+ for key, attr in attribute_dict.items():
+ all_attributes.setdefault(key, attr)
+ for key, attr in override_attrs.items():
+ if all_attributes.get(key) == None:
+ fail("Trying to override attribute %s where there is none." % key)
+ all_attributes[key] = attr
+ for key in remove_attrs:
+ if key in override_attrs:
+ fail("Trying to remove overridden attribute %s." % key)
+ if key not in all_attributes:
+ fail("Trying to remove non-existent attribute %s." % key)
+ all_attributes.pop(key)
+ return all_attributes