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