xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/java.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# Copyright 2018 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"""Bazel Java APIs for the Android rules."""
16
17load(":path.bzl", _path = "path")
18load(":utils.bzl", "log")
19
20_ANDROID_CONSTRAINT_MISSING_ERROR = (
21    "A list of constraints provided without the 'android' constraint."
22)
23
24# TODO(b/283499746): Reduce singlejar memory if possible.
25_SINGLEJAR_MEMORY_FOR_DEPLOY_JAR_MB = 1600
26
27def _segment_idx(path_segments):
28    """Finds the index of the segment in the path that preceeds the source root.
29
30    Args:
31      path_segments: A list of strings, where each string is the segment of a
32        filesystem path.
33
34    Returns:
35      An index to the path segment that represents the Java segment or -1 if
36      none found.
37    """
38    if _path.is_absolute(path_segments[0]):
39        log.error("path must not be absolute: %s" % _path.join(path_segments))
40
41    root_idx = -1
42    for idx, segment in enumerate(path_segments):
43        if segment in ["java", "javatests", "src", "testsrc"]:
44            root_idx = idx
45            break
46    if root_idx < 0:
47        return root_idx
48
49    is_src = path_segments[root_idx] == "src"
50    check_maven_idx = root_idx if is_src else -1
51    if root_idx == 0 or is_src:
52        # Check for a nested root directory.
53        for idx in range(root_idx + 1, len(path_segments) - 2):
54            segment = path_segments[idx]
55            if segment == "src" or (is_src and segment in ["java", "javatests"]):
56                next_segment = path_segments[idx + 1]
57                if next_segment in ["com", "org", "net"]:
58                    root_idx = idx
59                elif segment == "src":
60                    check_maven_idx = idx
61                break
62
63    if check_maven_idx >= 0 and check_maven_idx + 2 < len(path_segments):
64        next_segment = path_segments[check_maven_idx + 1]
65        if next_segment in ["main", "test"]:
66            next_segment = path_segments[check_maven_idx + 2]
67            if next_segment in ["java", "resources"]:
68                root_idx = check_maven_idx + 2
69    return root_idx
70
71def _resolve_package(path):
72    """Determines the Java package name from the given path.
73
74    Examples:
75        "{workspace}/java/foo/bar/wiz" -> "foo.bar.wiz"
76        "{workspace}/javatests/foo/bar/wiz" -> "foo.bar.wiz"
77
78    Args:
79      path: A string, representing a file path.
80
81    Returns:
82      A string representing a Java package name or None if could not be
83      determined.
84    """
85    path_segments = _path.split(path.partition(":")[0])
86    java_idx = _segment_idx(path_segments)
87    if java_idx < 0:
88        return None
89    else:
90        return ".".join(path_segments[java_idx + 1:])
91
92def _resolve_package_from_label(
93        label,
94        custom_package = None):
95    """Resolves the Java package from a Label.
96
97    When no legal Java package can be resolved from the label, None will be
98    returned unless fallback is specified.
99
100    When a fallback is requested, a not safe for Java compilation package will
101    be returned. The fallback value will be derrived by taking the label.package
102    and replacing all path separators with ".".
103    """
104    if custom_package:
105        return custom_package
106
107    # For backwards compatibility, also include directories
108    # from the label's name
109    # Ex: "//foo/bar:java/com/google/baz" is a legal one and
110    # results in "com.google"
111    label_path = _path.join(
112        [label.package] +
113        _path.split(label.name)[:-1],
114    )
115    return _resolve_package(label_path)
116
117def _root(path):
118    """Determines the Java root from the given path.
119
120    Examples:
121        "{workspace}/java/foo/bar/wiz" -> "{workspace}/java"
122        "{workspace}/javatests/foo/bar/wiz" -> "{workspace}/javatests"
123        "java/foo/bar/wiz" -> "java"
124        "javatests/foo/bar/wiz" -> "javatests"
125
126    Args:
127      path: A string, representing a file path.
128
129    Returns:
130      A string representing the Java root path or None if could not be
131      determined.
132    """
133    path_segments = _path.split(path.partition(":")[0])
134    java_idx = _segment_idx(path_segments)
135    if java_idx < 0:
136        return None
137    else:
138        return _path.join(path_segments[0:java_idx + 1])
139
140def _check_for_invalid_java_package(java_package):
141    return "-" in java_package or len(java_package.split(".")) < 2
142
143def _invalid_java_package(custom_package, java_package):
144    """Checks if the given java package is invalid.
145
146    Only checks if either custom_package or java_package contains the
147    illegal character "-" or if they are composed of only one word.
148    Only checks java_package if custom_package is an empty string or None.
149
150    Args:
151      custom_package: string. Java package given as an attribute to a rule to override
152      the java_package.
153      java_package: string. Java package inferred from the directory where the BUILD
154      containing the rule is.
155
156    Returns:
157      A boolean. True if custom_package or java_package contains "-" or is only one word.
158      Only checks java_package if custom_package is an empty string or None.
159    """
160    return (
161        (custom_package and _check_for_invalid_java_package(custom_package)) or
162        (not custom_package and _check_for_invalid_java_package(java_package))
163    )
164
165# The Android specific Java compile.
166def _compile_android(
167        ctx,
168        output_jar,
169        output_srcjar = None,
170        srcs = [],
171        resources = [],
172        javac_opts = [],
173        r_java = None,
174        deps = [],
175        exports = [],
176        plugins = [],
177        exported_plugins = [],
178        annotation_processor_additional_outputs = [],
179        annotation_processor_additional_inputs = [],
180        enable_deps_without_srcs = False,
181        neverlink = False,
182        constraints = ["android"],
183        strict_deps = "Error",
184        java_toolchain = None):
185    """Compiles the Java and IDL sources for Android.
186
187    Args:
188      ctx: The context.
189      output_jar: File. The artifact to place the compilation unit.
190      output_srcjar: File. The artifact to place the sources of the compilation
191        unit. Optional.
192      srcs: sequence of Files. A list of files and jars to be compiled.
193      resources: sequence of Files. Will be added to the output jar - see
194        java_library.resources. Optional.
195      javac_opts: sequence of strings. A list of the desired javac options.
196        Optional.
197      r_java: JavaInfo. The R.jar dependency. Optional.
198      deps: sequence of JavaInfo providers. A list of dependencies. Optional.
199      exports: sequence of JavaInfo providers. A list of exports. Optional.
200      plugins: sequence of JavaPluginInfo providers. A list of plugins. Optional.
201      exported_plugins: sequence of JavaPluginInfo providers. A list of exported
202        plugins. Optional.
203      annotation_processor_additional_outputs: sequence of Files. A list of
204        files produced by an annotation processor.
205      annotation_processor_additional_inputs: sequence of Files. A list of
206        files consumed by an annotation processor.
207      enable_deps_without_srcs: Enables the behavior from b/14473160.
208      neverlink: Bool. Makes the compiled unit a compile-time only dependency.
209      constraints: sequence of Strings. A list of constraints, to constrain the
210        target. Optional. By default [].
211      strict_deps: string. A string that specifies how to handle strict deps.
212        Possible values: 'OFF', 'ERROR','WARN' and 'DEFAULT'. For more details
213        see https://docs.bazel.build/versions/master/user-manual.html#flag--strict_java_deps.
214        By default 'ERROR'.
215      java_toolchain: The java_toolchain Target.
216
217    Returns:
218      A JavaInfo provider representing the Java compilation.
219    """
220    if "android" not in constraints:
221        log.error(_ANDROID_CONSTRAINT_MISSING_ERROR)
222
223    if not srcs:
224        if deps and enable_deps_without_srcs:
225            # TODO(b/122039567): Produces a JavaInfo that exports the deps, but
226            # not the plugins. To reproduce the "deps without srcs" bug,
227            # b/14473160, behavior in Starlark.
228            exports = exports + [
229                android_common.enable_implicit_sourceless_deps_exports_compatibility(dep)
230                for dep in deps
231            ]
232        if not exports:
233            # Add a "no-op JavaInfo" to propagate the exported_plugins when
234            # deps or exports have not been specified by the target and
235            # additionally forces java_common.compile method to create the
236            # empty output jar and srcjar when srcs have not been specified.
237            noop_java_info = java_common.merge([])
238            exports = exports + [noop_java_info]
239
240    r_java_info = [r_java] if r_java else []
241
242    java_info = _compile(
243        ctx,
244        output_jar,
245        output_srcjar = output_srcjar,
246        srcs = srcs,
247        resources = resources,
248        javac_opts = javac_opts,
249        deps = r_java_info + deps,
250        # In native, the JavaInfo exposes two Jars as compile-time deps, the
251        # compiled sources and the Android R.java jars. To simulate this
252        # behavior, the JavaInfo of the R.jar is also exported.
253        exports = r_java_info + exports,
254        plugins = plugins,
255        exported_plugins = exported_plugins,
256        annotation_processor_additional_outputs = (
257            annotation_processor_additional_outputs
258        ),
259        annotation_processor_additional_inputs = (
260            annotation_processor_additional_inputs
261        ),
262        neverlink = neverlink,
263        constraints = constraints,
264        strict_deps = strict_deps,
265        java_toolchain = java_toolchain,
266    )
267    return java_info
268
269def _compile(
270        ctx,
271        output_jar,
272        output_srcjar = None,
273        srcs = [],
274        resources = [],
275        javac_opts = [],
276        deps = [],
277        exports = [],
278        plugins = [],
279        exported_plugins = [],
280        annotation_processor_additional_outputs = [],
281        annotation_processor_additional_inputs = [],
282        neverlink = False,
283        constraints = [],
284        strict_deps = "Error",
285        java_toolchain = None):
286    """Compiles the Java and IDL sources for Android.
287
288    Args:
289      ctx: The context.
290      output_jar: File. The artifact to place the compilation unit.
291      output_srcjar: File. The artifact to place the sources of the compilation
292        unit. Optional.
293      srcs: sequence of Files. A list of files and jars to be compiled.
294      resources: sequence of Files. Will be added to the output jar - see
295        java_library.resources. Optional.
296      javac_opts: sequence of strings. A list of the desired javac options.
297        Optional.
298      deps: sequence of JavaInfo providers. A list of dependencies. Optional.
299      exports: sequence of JavaInfo providers. A list of exports. Optional.
300      plugins: sequence of JavaPluginInfo providers. A list of plugins. Optional.
301      exported_plugins: sequence of JavaPluginInfo providers. A list of exported
302        plugins. Optional.
303      annotation_processor_additional_outputs: sequence of Files. A list of
304        files produced by an annotation processor.
305      annotation_processor_additional_inputs: sequence of Files. A list of
306        files consumed by an annotation processor.
307      resources: sequence of Files. Will be added to the output jar - see
308        java_library.resources. Optional.
309      neverlink: Bool. Makes the compiled unit a compile-time only dependency.
310      constraints: sequence of Strings. A list of constraints, to constrain the
311        target. Optional. By default [].
312      strict_deps: string. A string that specifies how to handle strict deps.
313        Possible values: 'OFF', 'ERROR','WARN' and 'DEFAULT'. For more details
314        see https://docs.bazel.build/versions/master/user-manual.html#flag--strict_java_deps.
315        By default 'ERROR'.
316      java_toolchain: The java_toolchain Target.
317
318    Returns:
319      A JavaInfo provider representing the Java compilation.
320    """
321
322    # Split javac opts.
323    opts = []
324    for opt in javac_opts:
325        opts.extend(opt.split(" "))
326
327    # Separate the sources *.java from *.srcjar.
328    source_files = []
329    source_jars = []
330    for src in srcs:
331        if src.path.endswith(".srcjar"):
332            source_jars.append(src)
333        else:
334            source_files.append(src)
335
336    return java_common.compile(
337        ctx,
338        output = output_jar,
339        output_source_jar = output_srcjar,
340        source_files = source_files,
341        source_jars = source_jars,
342        resources = resources,
343        javac_opts = opts,
344        deps = deps,
345        exports = exports,
346        plugins = plugins,
347        exported_plugins = exported_plugins,
348        annotation_processor_additional_outputs = (
349            annotation_processor_additional_outputs
350        ),
351        annotation_processor_additional_inputs = (
352            annotation_processor_additional_inputs
353        ),
354        neverlink = neverlink,
355        strict_deps = strict_deps,
356        java_toolchain = java_toolchain[java_common.JavaToolchainInfo],
357    )
358
359def _singlejar(
360        ctx,
361        inputs,
362        output,
363        mnemonic = "SingleJar",
364        progress_message = "Merge into a single jar.",
365        build_target = "",
366        check_desugar_deps = False,
367        compression = True,
368        deploy_manifest_lines = [],
369        include_build_data = False,
370        include_prefixes = [],
371        java_toolchain = None,
372        resource_set = None):
373    args = ctx.actions.args()
374    args.add("--output")
375    args.add(output)
376    if compression:
377        args.add("--compression")
378    args.add("--normalize")
379    if not include_build_data:
380        args.add("--exclude_build_data")
381    args.add("--warn_duplicate_resources")
382    if inputs:
383        args.add("--sources")
384        args.add_all(inputs)
385
386    if build_target:
387        args.add("--build_target", build_target)
388    if check_desugar_deps:
389        args.add("--check_desugar_deps")
390    if deploy_manifest_lines:
391        args.add_all("--deploy_manifest_lines", deploy_manifest_lines)
392    if include_prefixes:
393        args.add_all("--include_prefixes", include_prefixes)
394
395    args.use_param_file("@%s")
396    args.set_param_file_format("multiline")
397
398    ctx.actions.run(
399        executable = java_toolchain[java_common.JavaToolchainInfo].single_jar,
400        toolchain = "@bazel_tools//tools/jdk:toolchain_type",
401        arguments = [args],
402        inputs = inputs,
403        outputs = [output],
404        mnemonic = mnemonic,
405        progress_message = progress_message,
406        resource_set = resource_set,
407    )
408
409def _run(
410        ctx,
411        host_javabase,
412        jvm_flags = [],
413        **args):
414    """Run a java binary
415
416    Args:
417      ctx: The context.
418      host_javabase: Target. The host_javabase.
419      jvm_flags: Additional arguments to the JVM itself.
420      **args: Additional arguments to pass to ctx.actions.run(). Some will get modified.
421    """
422
423    if type(ctx) != "ctx":
424        fail("Expected type ctx for argument ctx, got %s" % type(ctx))
425
426    if type(host_javabase) != "Target":
427        fail("Expected type Target for argument host_javabase, got %s" % type(host_javabase))
428
429    # Set reasonable max heap default. Required to prevent runaway memory usage.
430    # Can still be overridden by callers of this method.
431    jvm_flags = ["-Xms4G", "-Xmx4G", "-XX:+ExitOnOutOfMemoryError"] + jvm_flags
432
433    # executable should be a File or a FilesToRunProvider
434    jar = args.get("executable")
435    if type(jar) == "FilesToRunProvider":
436        jar = jar.executable
437    elif type(jar) != "File":
438        fail("Expected type File or FilesToRunProvider for argument executable, got %s" % type(jar))
439
440    java_runtime = host_javabase[java_common.JavaRuntimeInfo]
441    args["executable"] = java_runtime.java_executable_exec_path
442    args["toolchain"] = "@bazel_tools//tools/jdk:toolchain_type"
443
444    # inputs can be a list or a depset of File
445    inputs = args.get("inputs", default = [])
446    if type(inputs) == type([]):
447        args["inputs"] = depset(direct = inputs + [jar], transitive = [java_runtime.files])
448    else:  # inputs is a depset
449        args["inputs"] = depset(direct = [jar], transitive = [inputs, java_runtime.files])
450
451    jar_args = ctx.actions.args()
452    jar_args.add("-jar", jar)
453
454    args["arguments"] = jvm_flags + [jar_args] + args.get("arguments", default = [])
455
456    ctx.actions.run(**args)
457
458def _create_deploy_jar(
459        ctx,
460        output = None,
461        runtime_jars = depset(),
462        java_toolchain = None,
463        build_target = "",
464        deploy_manifest_lines = []):
465    _singlejar(
466        ctx,
467        inputs = runtime_jars,
468        output = output,
469        mnemonic = "JavaDeployJar",
470        progress_message = "Building deploy jar %s" % output.short_path,
471        java_toolchain = java_toolchain,
472        build_target = build_target,
473        check_desugar_deps = True,
474        compression = False,
475        deploy_manifest_lines = deploy_manifest_lines,
476        resource_set = _resource_set_for_deploy_jar,
477    )
478    return output
479
480def _resource_set_for_deploy_jar(_os, _inputs_size):
481    # parameters are unused but required by the resource_set API
482    return {"memory": _SINGLEJAR_MEMORY_FOR_DEPLOY_JAR_MB, "cpu": 1}
483
484java = struct(
485    compile = _compile,
486    compile_android = _compile_android,
487    resolve_package = _resolve_package,
488    resolve_package_from_label = _resolve_package_from_label,
489    root = _root,
490    invalid_java_package = _invalid_java_package,
491    run = _run,
492    singlejar = _singlejar,
493    create_deploy_jar = _create_deploy_jar,
494)
495