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