• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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