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