1# Copyright 2021 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Definition of java_import rule. 17""" 18 19load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") 20load("//java/common:java_semantics.bzl", "semantics") 21load("//java/common/rules/impl:basic_java_library_impl.bzl", "construct_defaultinfo") 22load("//java/common/rules/impl:import_deps_check.bzl", "import_deps_check") 23load("//java/private:java_common.bzl", "java_common") 24load("//java/private:java_common_internal.bzl", _run_ijar_private_for_builtins = "run_ijar") 25load("//java/private:java_info.bzl", "JavaInfo") 26load(":proguard_validation.bzl", "validate_proguard_specs") 27 28# copybara: default visibility 29 30def _filter_provider(provider, *attrs): 31 return [dep[provider] for attr in attrs for dep in attr if provider in dep] 32 33def _collect_jars(ctx, jars): 34 jars_dict = {} 35 for info in jars: 36 if JavaInfo in info: 37 fail("'jars' attribute cannot contain labels of Java targets") 38 for jar in info.files.to_list(): 39 jar_path = jar.dirname + jar.basename 40 if jars_dict.get(jar_path) != None: 41 fail("in jars attribute of java_import rule //" + ctx.label.package + ":" + ctx.attr.name + ": " + jar.basename + " is a duplicate") 42 jars_dict[jar_path] = jar 43 return [jar_tuple[1] for jar_tuple in jars_dict.items()] if len(jars_dict.items()) > 0 else [] 44 45def _process_with_ijars_if_needed(jars, ctx): 46 file_dict = {} 47 use_ijars = ctx.fragments.java.use_ijars() 48 for jar in jars: 49 interface_jar = jar 50 if use_ijars: 51 ijar_basename = jar.short_path.removeprefix("../").removesuffix("." + jar.extension) + "-ijar.jar" 52 interface_jar_directory = "_ijar/" + ctx.label.name + "/" + ijar_basename 53 54 interface_jar = ctx.actions.declare_file(interface_jar_directory) 55 _run_ijar_private_for_builtins( 56 ctx.actions, 57 target_label = ctx.label, 58 jar = jar, 59 output = interface_jar, 60 java_toolchain = semantics.find_java_toolchain(ctx), 61 ) 62 file_dict[jar] = interface_jar 63 64 return file_dict 65 66def _check_export_error(ctx, exports): 67 not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_exports") and not getattr(ctx.attr, "_allowlist_java_import_exports")[PackageSpecificationInfo].contains(ctx.label) 68 disallow_java_import_exports = ctx.fragments.java.disallow_java_import_exports() 69 70 if len(exports) != 0 and (disallow_java_import_exports or not_in_allowlist): 71 fail("java_import.exports is no longer supported; use java_import.deps instead") 72 73def _check_empty_jars_error(ctx, jars): 74 # TODO(kotlaja): Remove temporary incompatible flag [disallow_java_import_empty_jars] once migration is done. 75 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) 76 disallow_java_import_empty_jars = ctx.fragments.java.disallow_java_import_empty_jars() 77 78 if len(jars) == 0 and disallow_java_import_empty_jars and not_in_allowlist: 79 fail("empty java_import.jars is no longer supported " + ctx.label.package) 80 81def _create_java_info_with_dummy_output_file(ctx, srcjar, all_deps, exports, runtime_deps_list, neverlink, cc_info_list, add_exports, add_opens): 82 dummy_jar = ctx.actions.declare_file(ctx.label.name + "_dummy.jar") 83 dummy_src_jar = srcjar 84 if dummy_src_jar == None: 85 dummy_src_jar = ctx.actions.declare_file(ctx.label.name + "_src_dummy.java") 86 ctx.actions.write(dummy_src_jar, "") 87 return java_common.compile( 88 ctx, 89 output = dummy_jar, 90 java_toolchain = semantics.find_java_toolchain(ctx), 91 source_files = [dummy_src_jar], 92 deps = all_deps, 93 runtime_deps = runtime_deps_list, 94 neverlink = neverlink, 95 exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually. 96 native_libraries = cc_info_list, 97 add_exports = add_exports, 98 add_opens = add_opens, 99 ) 100 101def bazel_java_import_rule( 102 ctx, 103 jars = [], 104 srcjar = None, 105 deps = [], 106 runtime_deps = [], 107 exports = [], 108 neverlink = False, 109 proguard_specs = [], 110 add_exports = [], 111 add_opens = []): 112 """Implements java_import. 113 114 This rule allows the use of precompiled .jar files as libraries in other Java rules. 115 116 Args: 117 ctx: (RuleContext) Used to register the actions. 118 jars: (list[Artifact]) List of output jars. 119 srcjar: (Artifact) The jar containing the sources. 120 deps: (list[Target]) The list of dependent libraries. 121 runtime_deps: (list[Target]) Runtime dependencies to attach to the rule. 122 exports: (list[Target]) The list of exported libraries. 123 neverlink: (bool) Whether this rule should only be used for compilation and not at runtime. 124 proguard_specs: (list[File]) Files to be used as Proguard specification. 125 add_exports: (list[str]) Allow this library to access the given <module>/<package>. 126 add_opens: (list[str]) Allow this library to reflectively access the given <module>/<package>. 127 128 Returns: 129 (list[provider]) A list containing DefaultInfo, JavaInfo, 130 OutputGroupsInfo, ProguardSpecProvider providers. 131 """ 132 133 _check_empty_jars_error(ctx, jars) 134 _check_export_error(ctx, exports) 135 136 collected_jars = _collect_jars(ctx, jars) 137 all_deps = _filter_provider(JavaInfo, deps, exports) 138 139 jdeps_artifact = None 140 merged_java_info = java_common.merge(all_deps) 141 not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_deps_checking") and not ctx.attr._allowlist_java_import_deps_checking[PackageSpecificationInfo].contains(ctx.label) 142 if len(collected_jars) > 0 and not_in_allowlist and "incomplete-deps" not in ctx.attr.tags: 143 jdeps_artifact = import_deps_check( 144 ctx, 145 collected_jars, 146 merged_java_info.compile_jars, 147 merged_java_info.transitive_compile_time_jars, 148 "java_import", 149 ) 150 151 compilation_to_runtime_jar_map = _process_with_ijars_if_needed(collected_jars, ctx) 152 runtime_deps_list = [runtime_dep[JavaInfo] for runtime_dep in runtime_deps if JavaInfo in runtime_dep] 153 cc_info_list = [dep[CcInfo] for dep in deps if CcInfo in dep] 154 java_info = None 155 if len(collected_jars) > 0: 156 java_infos = [] 157 for jar in collected_jars: 158 java_infos.append(JavaInfo( 159 output_jar = jar, 160 compile_jar = compilation_to_runtime_jar_map[jar], 161 deps = all_deps, 162 runtime_deps = runtime_deps_list, 163 neverlink = neverlink, 164 source_jar = srcjar, 165 exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually. 166 native_libraries = cc_info_list, 167 add_exports = add_exports, 168 add_opens = add_opens, 169 )) 170 java_info = java_common.merge(java_infos) 171 else: 172 # TODO(kotlaja): Remove next line once all java_import targets with empty jars attribute are cleaned from depot (b/246559727). 173 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) 174 175 target = {"JavaInfo": java_info} 176 177 target["ProguardSpecProvider"] = validate_proguard_specs( 178 ctx, 179 proguard_specs, 180 [deps, runtime_deps, exports], 181 ) 182 183 # TODO(kotlaja): Revise if collected_runtimes can be added into construct_defaultinfo directly. 184 collected_runtimes = [] 185 for runtime_dep in ctx.attr.runtime_deps: 186 collected_runtimes.extend(runtime_dep.files.to_list()) 187 188 target["DefaultInfo"] = construct_defaultinfo( 189 ctx, 190 collected_jars, 191 collected_jars + collected_runtimes, 192 neverlink, 193 exports, 194 ) 195 196 output_group_src_jars = depset() if srcjar == None else depset([srcjar]) 197 target["OutputGroupInfo"] = OutputGroupInfo( 198 **{ 199 "_source_jars": output_group_src_jars, 200 "_direct_source_jars": output_group_src_jars, 201 "_validation": depset() if jdeps_artifact == None else depset([jdeps_artifact]), 202 "_hidden_top_level_INTERNAL_": target["ProguardSpecProvider"].specs, 203 } 204 ) 205 return target 206