xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/dex_desugar_aspect.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# Copyright 2023 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"""Aspect that transitively build .dex archives and desugar jars."""
16
17load(":utils.bzl", _get_android_sdk = "get_android_sdk", _utils = "utils")
18load(":dex.bzl", _dex = "dex")
19load(":desugar.bzl", _desugar = "desugar")
20load(":providers.bzl", "StarlarkAndroidDexInfo")
21load(":attrs.bzl", _attrs = "attrs")
22load("//rules:acls.bzl", "acls")
23
24_tristate = _attrs.tristate
25
26def _aspect_attrs():
27    """Attrs of the rule requiring traversal by the aspect."""
28    return [
29        "aidl_lib",  # for the aidl runtime in the android_sdk rule
30        "deps",
31        "exports",
32        "runtime",
33        "runtime_deps",
34        "_android_sdk",
35        "_aspect_proto_toolchain_for_javalite",  # To get from proto_library through proto_lang_toolchain rule to proto runtime library.
36        "_build_stamp_deps",  # for build stamp runtime class deps
37        "_build_stamp_mergee_manifest_lib",  # for empty build stamp Service class implementation
38        "_toolchain",  # to get Kotlin toolchain component in android_library
39    ]
40
41# Also used by the android_binary_internal rule
42def get_aspect_deps(ctx):
43    """Get all the deps of the dex_desugar_aspect that requires traversal.
44
45    Args:
46        ctx: The context.
47
48    Returns:
49        deps_list: List of all deps of the dex_desugar_aspect that requires traversal.
50    """
51    deps_list = []
52    for deps in [getattr(ctx.attr, attr, []) for attr in _aspect_attrs()]:
53        if str(type(deps)) == "list":
54            deps_list += deps
55        elif str(type(deps)) == "Target":
56            deps_list.append(deps)
57    return deps_list
58
59def _aspect_impl(target, ctx):
60    """Adapts the rule and target data.
61
62    Args:
63      target: The target.
64      ctx: The context.
65
66    Returns:
67      A list of providers.
68    """
69    if not acls.in_android_binary_starlark_dex_desugar_proguard(str(ctx.label)):
70        return []
71
72    incremental_dexing = getattr(ctx.rule.attr, "incremental_dexing", _tristate.auto)
73    min_sdk_version = getattr(ctx.rule.attr, "min_sdk_version", 0)
74
75    if incremental_dexing == _tristate.no or \
76       (not ctx.fragments.android.use_incremental_dexing and
77        incremental_dexing == _tristate.auto):
78        return []
79
80    # TODO(b/33557068): Desugar protos if needed instead of assuming they don't need desugaring
81    ignore_desugar = not ctx.fragments.android.desugar_java8 or ctx.rule.kind == "proto_library"
82
83    extra_toolchain_jars = _get_platform_based_toolchain_jars(ctx)
84
85    if hasattr(ctx.rule.attr, "neverlink") and ctx.rule.attr.neverlink:
86        return []
87
88    dex_archives_dict = {}
89    runtime_jars = _get_produced_runtime_jars(target, ctx, extra_toolchain_jars)
90    bootclasspath = _get_boot_classpath(target, ctx)
91    compiletime_classpath = target[JavaInfo].transitive_compile_time_jars if JavaInfo in target else None
92    if runtime_jars:
93        basename_clash = _check_basename_clash(runtime_jars)
94        aspect_dexopts = _get_aspect_dexopts(ctx)
95        min_sdk_filename_part = "--min_sdk_version=" + min_sdk_version if min_sdk_version > 0 else ""
96        for jar in runtime_jars:
97            if not ignore_desugar:
98                unique_desugar_filename = (jar.path if basename_clash else jar.basename) + \
99                                          min_sdk_filename_part + "_desugared.jar"
100                desugared_jar = _dex.get_dx_artifact(ctx, unique_desugar_filename)
101                _desugar.desugar(
102                    ctx,
103                    input = jar,
104                    output = desugared_jar,
105                    bootclasspath = bootclasspath,
106                    classpath = compiletime_classpath,
107                    min_sdk_version = min_sdk_version,
108                    desugar_exec = ctx.executable._desugar_java8,
109                )
110            else:
111                desugared_jar = None
112
113            for incremental_dexopts_list in aspect_dexopts:
114                incremental_dexopts = "".join(incremental_dexopts_list)
115
116                unique_dx_filename = (jar.short_path if basename_clash else jar.basename) + \
117                                     incremental_dexopts + min_sdk_filename_part + ".dex.zip"
118                dex = _dex.get_dx_artifact(ctx, unique_dx_filename)
119                _dex.dex(
120                    ctx,
121                    input = desugared_jar if desugared_jar else jar,
122                    output = dex,
123                    incremental_dexopts = incremental_dexopts_list,
124                    min_sdk_version = min_sdk_version,
125                    dex_exec = ctx.executable._dexbuilder,
126                )
127
128                dex_archive = struct(
129                    jar = jar,
130                    desugared_jar = desugared_jar,
131                    dex = dex,
132                )
133
134                if incremental_dexopts not in dex_archives_dict:
135                    dex_archives_dict[incremental_dexopts] = []
136                dex_archives_dict[incremental_dexopts].append(dex_archive)
137
138    infos = _utils.collect_providers(StarlarkAndroidDexInfo, get_aspect_deps(ctx.rule))
139    merged_info = _dex.merge_infos(infos)
140
141    for dexopts in dex_archives_dict:
142        if dexopts in merged_info.dex_archives_dict:
143            merged_info.dex_archives_dict[dexopts] = depset(dex_archives_dict[dexopts], transitive = [merged_info.dex_archives_dict[dexopts]])
144        else:
145            merged_info.dex_archives_dict[dexopts] = depset(dex_archives_dict[dexopts])
146
147    return [
148        StarlarkAndroidDexInfo(
149            dex_archives_dict = merged_info.dex_archives_dict,
150        ),
151    ]
152
153def _get_produced_runtime_jars(target, ctx, extra_toolchain_jars):
154    if ctx.rule.kind == "proto_library":
155        if getattr(ctx.rule.attr, "srcs", []):
156            if JavaInfo in target:
157                return [java_output.class_jar for java_output in target[JavaInfo].java_outputs]
158        return []
159    else:
160        jars = []
161        if JavaInfo in target:
162            jars.extend(target[JavaInfo].runtime_output_jars)
163
164        # TODO(b/124540821): Disable R.jar desugaring (with a flag).
165        if AndroidIdeInfo in target and target[AndroidIdeInfo].resource_jar:
166            jars.append(target[AndroidIdeInfo].resource_jar.class_jar)
167
168        if AndroidApplicationResourceInfo in target and target[AndroidApplicationResourceInfo].build_stamp_jar:
169            jars.append(target[AndroidApplicationResourceInfo].build_stamp_jar)
170
171        jars.extend(extra_toolchain_jars)
172        return jars
173
174def _get_platform_based_toolchain_jars(ctx):
175    if not ctx.fragments.android.incompatible_use_toolchain_resolution:
176        return []
177
178    if not getattr(ctx.rule.attr, "_android_sdk", None):
179        return []
180
181    android_sdk = ctx.rule.attr._android_sdk
182
183    if AndroidSdkInfo in android_sdk and android_sdk[AndroidSdkInfo].aidl_lib:
184        return android_sdk[AndroidSdkInfo].aidl_lib[JavaInfo].runtime_output_jars
185
186    return []
187
188def _get_aspect_dexopts(ctx):
189    return _power_set(_dex.normalize_dexopts(ctx.fragments.android.get_dexopts_supported_in_incremental_dexing))
190
191def _get_boot_classpath(target, ctx):
192    if JavaInfo in target:
193        compilation_info = target[JavaInfo].compilation_info
194        if compilation_info and compilation_info.boot_classpath:
195            return compilation_info.boot_classpath
196
197    android_jar = _get_android_sdk(ctx).android_jar
198    if android_jar:
199        return [android_jar]
200
201    # This shouldn't ever be reached, but if it is, we should be clear about the error.
202    fail("No compilation info or android jar!")
203
204def _check_basename_clash(artifacts):
205    seen = {}
206    for artifact in artifacts:
207        basename = artifact.basename
208        if basename not in seen:
209            seen[basename] = True
210        else:
211            return True
212    return False
213
214def _power_set(items):
215    """Calculates the power set of the given items.
216    """
217
218    def _exp(base, n):
219        """ Calculates base ** n."""
220        res = 1
221        for _ in range(n):
222            res *= base
223        return res
224
225    power_set = []
226    size = len(items)
227
228    for i in range(_exp(2, size)):
229        element = [items[j] for j in range(size) if (i // _exp(2, j) % 2) != 0]
230        power_set.append(element)
231
232    return power_set
233
234dex_desugar_aspect = aspect(
235    implementation = _aspect_impl,
236    attr_aspects = _aspect_attrs(),
237    attrs = _attrs.add(
238        {
239            "_desugar_java8": attr.label(
240                default = Label("@bazel_tools//tools/android:desugar_java8"),
241                cfg = "exec",
242                executable = True,
243            ),
244            "_dexbuilder": attr.label(
245                default = Label("@bazel_tools//tools/android:dexbuilder"),
246                allow_files = True,
247                cfg = "exec",
248                executable = True,
249            ),
250        },
251        _attrs.ANDROID_SDK,
252    ),
253    fragments = ["android"],
254    toolchains = ["//toolchains/android_sdk:toolchain_type"],
255    required_aspect_providers = [[JavaInfo]],
256)
257