diff --git a/java/bazel/rules/bazel_java_binary.bzl b/java/bazel/rules/bazel_java_binary.bzl index 87f36e51..f3680c12 100644 --- a/java/bazel/rules/bazel_java_binary.bzl +++ b/java/bazel/rules/bazel_java_binary.bzl @@ -21,20 +21,27 @@ 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:java_binary.bzl", "BASIC_JAVA_BINARY_ATTRIBUTES") load("//java/common/rules:rule_util.bzl", "merge_attrs") +load("//java/common/rules/impl:java_binary_deploy_jar.bzl", "create_deploy_archives") +load("//java/common/rules/impl:java_binary_impl.bzl", "basic_java_binary") +load("//java/common/rules/impl:java_helper.bzl", "helper") visibility("private") def _bazel_java_binary_impl(ctx): - return _bazel_base_binary_impl(ctx, is_test_rule_class = False) + helper.executable_providers(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): + """Common implementation for binaries and tests -def _bazel_base_binary_impl(ctx, is_test_rule_class): + Args: + ctx: (RuleContext) + is_test_rule_class: (bool) + + Returns: + [Provider] + """ deps = _collect_all_targets_as_deps(ctx, classpath_type = "compile_only") runtime_deps = _collect_all_targets_as_deps(ctx) @@ -288,7 +295,7 @@ def _short_path(file): 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): +def make_binary_rule(implementation, *, doc, attrs, executable = False, test = False, initializer = None): return rule( implementation = implementation, initializer = initializer, @@ -315,7 +322,7 @@ def _make_binary_rule(implementation, *, doc, attrs, executable = False, test = subrules = [android_lint_subrule], ) -_BASE_BINARY_ATTRS = merge_attrs( +BASE_BINARY_ATTRS = merge_attrs( BASIC_JAVA_BINARY_ATTRIBUTES, { "resource_strip_prefix": attr.string( @@ -345,7 +352,7 @@ logic as the Java package of source files. For example, a source file at ) def make_java_binary(executable): - return _make_binary_rule( + return make_binary_rule( _bazel_java_binary_impl, doc = """

@@ -439,7 +446,7 @@ java_binary( """, attrs = merge_attrs( - _BASE_BINARY_ATTRS, + BASE_BINARY_ATTRS, ({} if executable else { "args": attr.string_list(), "output_licenses": attr.string_list(), @@ -449,128 +456,3 @@ java_binary( ) 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 = """ -

-A java_test() 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. -

- -

Implicit output targets

- - -

-See the section on java_binary() arguments. This rule also -supports all attributes common -to all test rules (*_test). -

- -

Examples

- -
-
-
-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",
-    ],
-)
-
-
- """, - 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") + """ -
-You can use this to override the default -behavior, which is to use test runner for -java_test rules, -and not use it for java_binary rules. It is unlikely -you will want to do this. One use is for AllTest -rules that are invoked by another rule (to set up a database -before running the tests, for example). The AllTest -rule must be declared as a java_binary, but should -still use the test runner as its main entry point. - -The name of a test runner class can be overridden with main_class attribute. - """, - ), - "stamp": attr.int( - default = 0, - values = [-1, 0, 1], - doc = """ -Whether to encode build information into the binary. Possible values: - -

Stamped binaries are not rebuilt unless their dependencies change.

- """, - ), - }, - remove_attrs = ["deploy_env"], - ), - test = True, - initializer = _java_test_initializer, -) diff --git a/java/bazel/rules/bazel_java_binary_wrapper.bzl b/java/bazel/rules/bazel_java_binary_wrapper.bzl index 31696bcc..15e8f6b2 100644 --- a/java/bazel/rules/bazel_java_binary_wrapper.bzl +++ b/java/bazel/rules/bazel_java_binary_wrapper.bzl @@ -29,7 +29,7 @@ load(":bazel_java_binary_nonexec.bzl", java_bin_nonexec = "java_binary") _java_common_internal = java_common.internal_DO_NOT_USE() -visibility("private") +visibility(["//java"]) def java_binary(**kwargs): if _java_common_internal.incompatible_disable_non_executable_java_binary(): diff --git a/java/bazel/rules/bazel_java_import.bzl b/java/bazel/rules/bazel_java_import.bzl new file mode 100644 index 00000000..02951d52 --- /dev/null +++ b/java/bazel/rules/bazel_java_import.bzl @@ -0,0 +1,68 @@ +# 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("//java/common:java_info.bzl", "JavaInfo") +load("//java/common:java_semantics.bzl", "semantics") +load("//java/common/rules:java_import.bzl", "JAVA_IMPORT_ATTRS") +load("//java/common/rules/impl:bazel_java_import_impl.bzl", "bazel_java_import_rule") + +visibility(["//java"]) + +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() + +java_import = rule( + _proxy, + doc = """ +

+ This rule allows the use of precompiled .jar files as + libraries for java_library and + java_binary rules. +

+ +

Examples

+ +
+
+    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",
+        ],
+    )
+
+
+ """, + attrs = JAVA_IMPORT_ATTRS, + provides = [JavaInfo], + fragments = ["java", "cpp"], + toolchains = [semantics.JAVA_TOOLCHAIN], +) diff --git a/java/bazel/rules/bazel_java_library.bzl b/java/bazel/rules/bazel_java_library.bzl new file mode 100644 index 00000000..e70d54fd --- /dev/null +++ b/java/bazel/rules/bazel_java_library.bzl @@ -0,0 +1,67 @@ +# 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("//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_library.bzl", "JAVA_LIBRARY_ATTRS") +load("//java/common/rules/impl:bazel_java_library_impl.bzl", "bazel_java_library_rule") + +visibility(["//java/..."]) + +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 = rule( + _proxy, + doc = """ +

This rule compiles and links sources into a .jar file.

+ +

Implicit outputs

+ + """, + 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/bazel/rules/bazel_java_plugin.bzl b/java/bazel/rules/bazel_java_plugin.bzl new file mode 100644 index 00000000..462df8c3 --- /dev/null +++ b/java/bazel/rules/bazel_java_plugin.bzl @@ -0,0 +1,156 @@ +# 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("//java/common/rules:android_lint.bzl", "android_lint_subrule") +load("//java/common/rules:java_library.bzl", "JAVA_LIBRARY_IMPLICIT_ATTRS") +load("//java/common/rules:java_plugin.bzl", "JAVA_PLUGIN_ATTRS") +load("//java/common/rules:rule_util.bzl", "merge_attrs") +load("//java/common/rules/impl:basic_java_library_impl.bzl", "basic_java_library", "construct_defaultinfo") + +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 /. + add_opens: (list[str]) Allow this library to reflectively access the given /. + 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_IMPLICIT_ATTRS = JAVA_LIBRARY_IMPLICIT_ATTRS + +java_plugin = rule( + _proxy, + doc = """ +

+ java_plugin defines plugins for the Java compiler run by Bazel. The + only supported kind of plugins are annotation processors. A java_library or + java_binary rule can run plugins by depending on them via the plugins + attribute. A java_library can also automatically export plugins to libraries that + directly depend on it using + exported_plugins. +

+ +

Implicit output targets

+
    +
  • libname.jar: A Java archive.
  • +
+ +

+ Arguments are identical to java_library, except + for the addition of the processor_class argument. +

+ """, + 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/bazel/rules/bazel_java_test.bzl b/java/bazel/rules/bazel_java_test.bzl new file mode 100644 index 00000000..a019b867 --- /dev/null +++ b/java/bazel/rules/bazel_java_test.bzl @@ -0,0 +1,150 @@ +# 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_test rule""" + +load("//java/common:java_semantics.bzl", "semantics") +load("//java/common/rules:java_binary.bzl", "BASE_TEST_ATTRIBUTES") +load("//java/common/rules:rule_util.bzl", "merge_attrs") +load("//java/common/rules/impl:java_helper.bzl", "helper") +load(":bazel_java_binary.bzl", "BASE_BINARY_ATTRS", "bazel_base_binary_impl", "make_binary_rule") + +visibility(["//java"]) + +def _bazel_java_test_impl(ctx): + return bazel_base_binary_impl(ctx, is_test_rule_class = True) + helper.test_providers(ctx) + +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 = """ +

+A java_test() 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. +

+ +

Implicit output targets

+
    +
  • name.jar: A Java archive.
  • +
  • name_deploy.jar: A Java archive suitable + for deployment. (Only built if explicitly requested.) See the description of the + name_deploy.jar output from + java_binary for more details.
  • +
+ +

+See the section on java_binary() arguments. This rule also +supports all attributes common +to all test rules (*_test). +

+ +

Examples

+ +
+
+
+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",
+    ],
+)
+
+
+ """, + 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") + """ +
+You can use this to override the default +behavior, which is to use test runner for +java_test rules, +and not use it for java_binary rules. It is unlikely +you will want to do this. One use is for AllTest +rules that are invoked by another rule (to set up a database +before running the tests, for example). The AllTest +rule must be declared as a java_binary, but should +still use the test runner as its main entry point. + +The name of a test runner class can be overridden with main_class attribute. + """, + ), + "stamp": attr.int( + default = 0, + values = [-1, 0, 1], + doc = """ +Whether to encode build information into the binary. Possible values: +
    +
  • + stamp = 1: Always stamp the build information into the binary, even in + --nostamp builds. This + setting should be avoided, since it potentially kills remote caching for the + binary and any downstream actions that depend on it. +
  • +
  • + stamp = 0: Always replace build information by constant values. This + gives good build result caching. +
  • +
  • + stamp = -1: Embedding of build information is controlled by the + --[no]stamp flag. +
  • +
+

Stamped binaries are not rebuilt unless their dependencies change.

+ """, + ), + }, + remove_attrs = ["deploy_env"], + ), + test = True, + initializer = _java_test_initializer, +) diff --git a/java/bazel/rules/empty.bzl b/java/bazel/rules/empty.bzl deleted file mode 100644 index 6981db7e..00000000 --- a/java/bazel/rules/empty.bzl +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2024 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. -"""Empty file as a placeholder for migration""" diff --git a/java/common/BUILD b/java/common/BUILD index e9d0165b..e8b08c0e 100644 --- a/java/common/BUILD +++ b/java/common/BUILD @@ -6,7 +6,9 @@ licenses(["notice"]) filegroup( name = "srcs", - srcs = glob(["**"]), + srcs = glob(["**"]) + [ + "//java/common/rules:srcs", + ], visibility = ["//java:__pkg__"], ) diff --git a/java/common/rules/BUILD b/java/common/rules/BUILD index ffd0fb0c..5031de89 100644 --- a/java/common/rules/BUILD +++ b/java/common/rules/BUILD @@ -1 +1,9 @@ package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]) + [ + "//java/common/rules/impl:srcs", + ], + visibility = ["//java/common:__pkg__"], +) diff --git a/java/common/rules/android_lint.bzl b/java/common/rules/android_lint.bzl index 9d23681b..4c96c16e 100644 --- a/java/common/rules/android_lint.bzl +++ b/java/common/rules/android_lint.bzl @@ -14,13 +14,17 @@ """Creates the android lint action for java rules""" -load("//java/common:java_semantics.bzl", "semantics") -load("//java/common/rules:java_helper.bzl", "helper") +load( + "//java/common:java_semantics.bzl", + "semantics", + _semantics_tokenize_javacopts = "tokenize_javacopts", +) visibility(["//java/..."]) def _tokenize_opts(opts_depset): - return helper.tokenize_javacopts(ctx = None, opts = opts_depset) + opts = reversed(opts_depset.to_list()) + return _semantics_tokenize_javacopts(opts) def _android_lint_action(ctx, source_files, source_jars, compilation_info): """ diff --git a/java/common/rules/basic_java_library.bzl b/java/common/rules/basic_java_library.bzl index 221baca4..c8a13fb5 100644 --- a/java/common/rules/basic_java_library.bzl +++ b/java/common/rules/basic_java_library.bzl @@ -16,256 +16,15 @@ 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 /. - add_opens: (list[str]) Allow this library to reflectively access the given /. - 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( diff --git a/java/common/rules/impl/BUILD b/java/common/rules/impl/BUILD new file mode 100644 index 00000000..e755396a --- /dev/null +++ b/java/common/rules/impl/BUILD @@ -0,0 +1,7 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//java/common/rules:__pkg__"], +) diff --git a/java/common/rules/impl/basic_java_library_impl.bzl b/java/common/rules/impl/basic_java_library_impl.bzl new file mode 100644 index 00000000..8ba53430 --- /dev/null +++ b/java/common/rules/impl/basic_java_library_impl.bzl @@ -0,0 +1,265 @@ +# 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/rules:android_lint.bzl", "android_lint_subrule") +load(":compile_action.bzl", "compile_action") +load(":proguard_validation.bzl", "validate_proguard_specs") + +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 /. + add_opens: (list[str]) Allow this library to reflectively access the given /. + 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 diff --git a/java/common/rules/impl/bazel_java_import_impl.bzl b/java/common/rules/impl/bazel_java_import_impl.bzl new file mode 100644 index 00000000..1c84fb79 --- /dev/null +++ b/java/common/rules/impl/bazel_java_import_impl.bzl @@ -0,0 +1,207 @@ +# 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("//java/common/rules/impl:basic_java_library_impl.bzl", "construct_defaultinfo") +load("//java/common/rules/impl: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 /. + add_opens: (list[str]) Allow this library to reflectively access the given /. + + 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 diff --git a/java/common/rules/impl/bazel_java_library_impl.bzl b/java/common/rules/impl/bazel_java_library_impl.bzl new file mode 100644 index 00000000..f0a22748 --- /dev/null +++ b/java/common/rules/impl/bazel_java_library_impl.bzl @@ -0,0 +1,98 @@ +# 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("//java/common/rules/impl:basic_java_library_impl.bzl", "basic_java_library", "construct_defaultinfo") + +visibility(["//java/..."]) + +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 /. + add_opens: (list[str]) Allow this library to reflectively access the given /. + 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 diff --git a/java/common/rules/impl/compile_action.bzl b/java/common/rules/impl/compile_action.bzl new file mode 100644 index 00000000..d1c7f0ed --- /dev/null +++ b/java/common/rules/impl/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 /. + add_opens: (list[str]) Allow this library to reflectively access the given /. + 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/impl/import_deps_check.bzl b/java/common/rules/impl/import_deps_check.bzl new file mode 100644 index 00000000..306a40ca --- /dev/null +++ b/java/common/rules/impl/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(["//java/common/rules/..."]) + +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/impl/java_binary_deploy_jar.bzl b/java/common/rules/impl/java_binary_deploy_jar.bzl new file mode 100644 index 00000000..183be458 --- /dev/null +++ b/java/common/rules/impl/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/impl/java_binary_impl.bzl b/java/common/rules/impl/java_binary_impl.bzl new file mode 100644 index 00000000..03f2cbe2 --- /dev/null +++ b/java/common/rules/impl/java_binary_impl.bzl @@ -0,0 +1,467 @@ +# 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_semantics.bzl", "semantics") +load("//java/common/rules/impl:basic_java_library_impl.bzl", "basic_java_library", "collect_deps") +load("//third_party/protobuf/bazel/common:proto_info.bzl", "ProtoInfo") +load(":java_binary_deploy_jar.bzl", "create_deploy_archive") +load(":java_helper.bzl", "helper") + +visibility(["//java/..."]) + +_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 diff --git a/java/common/rules/impl/java_helper.bzl b/java/common/rules/impl/java_helper.bzl new file mode 100644 index 00000000..82897db6 --- /dev/null +++ b/java/common/rules/impl/java_helper.bzl @@ -0,0 +1,474 @@ +# 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("@bazel_skylib//lib:paths.bzl", "paths") +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") + +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. + +

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 _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, + detokenize_javacopts = _detokenize_javacopts, + derive_output_file = _derive_output_file, + is_stamping_enabled = _is_stamping_enabled, +) diff --git a/java/common/rules/impl/proguard_validation.bzl b/java/common/rules/impl/proguard_validation.bzl new file mode 100644 index 00000000..18142b35 --- /dev/null +++ b/java/common/rules/impl/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/java_binary.bzl b/java/common/rules/java_binary.bzl index 79d6ff5d..5bdcd480 100644 --- a/java/common/rules/java_binary.bzl +++ b/java/common/rules/java_binary.bzl @@ -15,462 +15,19 @@ """ Implementation of java_binary for bazel """ load("@bazel_skylib//lib:paths.bzl", "paths") -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/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(":basic_java_library.bzl", "BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS") 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, diff --git a/java/common/rules/java_import.bzl b/java/common/rules/java_import.bzl index 7ba8065c..eb8f09ea 100644 --- a/java/common/rules/java_import.bzl +++ b/java/common/rules/java_import.bzl @@ -16,210 +16,11 @@ 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 /. - add_opens: (list[str]) Allow this library to reflectively access the given /. - - 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", @@ -324,33 +125,3 @@ 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 = """ -

- This rule allows the use of precompiled .jar files as - libraries for java_library and - java_binary rules. -

- -

Examples

- -
-
-    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",
-        ],
-    )
-
-
- """, - 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 index 1ca788d5..8cf012f9 100644 --- a/java/common/rules/java_library.bzl +++ b/java/common/rules/java_library.bzl @@ -20,110 +20,13 @@ 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(":basic_java_library.bzl", "BASIC_JAVA_LIBRARY_IMPLICIT_ATTRS") 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 /. - add_opens: (list[str]) Allow this library to reflectively access the given /. - 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( @@ -367,26 +270,3 @@ This corresponds to the javac and JVM --add-opens= flags. "_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE), }, ) - -java_library = rule( - _proxy, - doc = """ -

This rule compiles and links sources into a .jar file.

- -

Implicit outputs

-
    -
  • libname.jar: A Java archive containing the class files.
  • -
  • libname-src.jar: An archive containing the sources ("source - jar").
  • -
- """, - 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 index 32e3abd1..1f766d07 100644 --- a/java/common/rules/java_package_configuration.bzl +++ b/java/common/rules/java_package_configuration.bzl @@ -15,7 +15,7 @@ """Implementation for the java_package_configuration rule""" load("//java/common:java_common.bzl", "java_common") -load(":java_helper.bzl", "helper") +load("//java/common/rules/impl:java_helper.bzl", "helper") visibility(["//java/..."]) diff --git a/java/common/rules/java_plugin.bzl b/java/common/rules/java_plugin.bzl index 4dbf4e90..571fdff2 100644 --- a/java/common/rules/java_plugin.bzl +++ b/java/common/rules/java_plugin.bzl @@ -16,106 +16,11 @@ 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(":java_library.bzl", "JAVA_LIBRARY_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 /. - add_opens: (list[str]) Allow this library to reflectively access the given /. - 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, { @@ -145,41 +50,3 @@ java.util.ServiceLoader.) }, remove_attrs = ["runtime_deps", "exports", "exported_plugins"], ) - -JAVA_PLUGIN_IMPLICIT_ATTRS = JAVA_LIBRARY_IMPLICIT_ATTRS - -java_plugin = rule( - _proxy, - doc = """ -

- java_plugin defines plugins for the Java compiler run by Bazel. The - only supported kind of plugins are annotation processors. A java_library or - java_binary rule can run plugins by depending on them via the plugins - attribute. A java_library can also automatically export plugins to libraries that - directly depend on it using - exported_plugins. -

- -

Implicit output targets

-
    -
  • libname.jar: A Java archive.
  • -
- -

- Arguments are identical to java_library, except - for the addition of the processor_class argument. -

- """, - 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 index 0876639c..7991d5ed 100644 --- a/java/common/rules/java_runtime.bzl +++ b/java/common/rules/java_runtime.bzl @@ -19,7 +19,7 @@ Definition of java_runtime rule and JavaRuntimeInfo provider. load("@bazel_skylib//lib:paths.bzl", "paths") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load("//java/common:java_semantics.bzl", "PLATFORMS_ROOT") -load(":java_helper.bzl", "helper") +load("//java/common/rules/impl:java_helper.bzl", "helper") visibility(["//java/..."]) diff --git a/java/common/rules/java_toolchain.bzl b/java/common/rules/java_toolchain.bzl index 8e0f5837..5a9cb87f 100644 --- a/java/common/rules/java_toolchain.bzl +++ b/java/common/rules/java_toolchain.bzl @@ -17,7 +17,7 @@ Definition of java_toolchain rule and JavaToolchainInfo provider. """ load("//java/common:java_semantics.bzl", "semantics") -load(":java_helper.bzl", "helper") +load("//java/common/rules/impl:java_helper.bzl", "helper") load(":java_package_configuration.bzl", "JavaPackageConfigurationInfo") load(":java_runtime.bzl", "JavaRuntimeInfo")