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"""R8 processor steps for android_binary_internal.""" 16 17load("//rules:acls.bzl", "acls") 18load("//rules:proguard.bzl", "proguard") 19load( 20 "//rules:utils.bzl", 21 "ANDROID_TOOLCHAIN_TYPE", 22 "get_android_sdk", 23 "get_android_toolchain", 24) 25load( 26 "//rules:processing_pipeline.bzl", 27 "ProviderInfo", 28) 29load("//rules:common.bzl", "common") 30load("//rules:java.bzl", "java") 31load("//rules:resources.bzl", _resources = "resources") 32 33def process_r8(ctx, jvm_ctx, packaged_resources_ctx, build_info_ctx, **_unused_ctxs): 34 """Runs R8 for desugaring, optimization, and dexing. 35 36 Args: 37 ctx: Rule contxt. 38 jvm_ctx: Context from the java processor. 39 packaged_resources_ctx: Context from resource processing. 40 build_info_ctx: Context from build info processor. 41 **_unused_ctxs: Unused context. 42 43 Returns: 44 The r8_ctx ProviderInfo. 45 """ 46 local_proguard_specs = ctx.files.proguard_specs 47 if not acls.use_r8(str(ctx.label)) or not local_proguard_specs: 48 return ProviderInfo( 49 name = "r8_ctx", 50 value = struct( 51 providers = [], 52 ), 53 ) 54 55 # The R8 processor step creates its own deploy jar instead of 56 # The deploy jar from the deploy_jar processor is not used because as of now, whether it 57 # actually produces a deploy jar is determinted by a separate set of ACLs, and also does 58 # desugaring differently than with R8. 59 deploy_jar = ctx.actions.declare_file(ctx.label.name + "_deploy.jar") 60 java.create_deploy_jar( 61 ctx, 62 output = deploy_jar, 63 runtime_jars = depset( 64 direct = jvm_ctx.java_info.runtime_output_jars + [packaged_resources_ctx.class_jar], 65 transitive = [jvm_ctx.java_info.transitive_runtime_jars], 66 ), 67 java_toolchain = common.get_java_toolchain(ctx), 68 build_target = ctx.label.name, 69 deploy_manifest_lines = build_info_ctx.deploy_manifest_lines, 70 ) 71 72 dexes_zip = ctx.actions.declare_file(ctx.label.name + "_dexes.zip") 73 74 android_jar = get_android_sdk(ctx).android_jar 75 proguard_specs = proguard.get_proguard_specs(ctx, packaged_resources_ctx.resource_proguard_config) 76 min_sdk_version = getattr(ctx.attr, "min_sdk_version", None) 77 78 args = ctx.actions.args() 79 args.add("--release") 80 if min_sdk_version: 81 args.add("--min-api", min_sdk_version) 82 args.add("--output", dexes_zip) 83 args.add_all(proguard_specs, before_each = "--pg-conf") 84 args.add("--lib", android_jar) 85 args.add(deploy_jar) # jar to optimize + desugar + dex 86 87 java.run( 88 ctx = ctx, 89 host_javabase = common.get_host_javabase(ctx), 90 executable = get_android_toolchain(ctx).r8.files_to_run, 91 arguments = [args], 92 inputs = [android_jar, deploy_jar] + proguard_specs, 93 outputs = [dexes_zip], 94 mnemonic = "AndroidR8", 95 jvm_flags = ["-Xmx8G"], 96 progress_message = "R8 Optimizing, Desugaring, and Dexing %{label}", 97 ) 98 99 android_dex_info = AndroidDexInfo( 100 deploy_jar = deploy_jar, 101 final_classes_dex_zip = dexes_zip, 102 # R8 preserves the Java resources (i.e. non-Java-class files) in its output zip, so no need 103 # to provide a Java resources zip. 104 java_resource_jar = None, 105 ) 106 107 return ProviderInfo( 108 name = "r8_ctx", 109 value = struct( 110 final_classes_dex_zip = dexes_zip, 111 providers = [android_dex_info], 112 ), 113 ) 114 115def process_resource_shrinking_r8(ctx, r8_ctx, packaged_resources_ctx, **_unused_ctxs): 116 """Runs resource shrinking. 117 118 Args: 119 ctx: Rule contxt. 120 r8_ctx: Context from the R8 processor. 121 packaged_resources_ctx: Context from resource processing. 122 **_unused_ctxs: Unused context. 123 124 Returns: 125 The r8_ctx ProviderInfo. 126 """ 127 local_proguard_specs = ctx.files.proguard_specs 128 if (not acls.use_r8(str(ctx.label)) or 129 not local_proguard_specs or 130 not _resources.is_resource_shrinking_enabled( 131 ctx.attr.shrink_resources, 132 ctx.fragments.android.use_android_resource_shrinking, 133 )): 134 return ProviderInfo( 135 name = "resource_shrinking_r8_ctx", 136 value = struct( 137 android_application_resource_info_with_shrunk_resource_apk = None, 138 providers = [], 139 ), 140 ) 141 142 android_toolchain = get_android_toolchain(ctx) 143 144 # 1. Convert the resource APK to proto format (resource shrinker operates on a proto apk) 145 proto_resource_apk = ctx.actions.declare_file(ctx.label.name + "_proto_resource_apk.ap_") 146 ctx.actions.run( 147 arguments = [ctx.actions.args() 148 .add("convert") 149 .add(packaged_resources_ctx.resources_apk) # input apk 150 .add("-o", proto_resource_apk) # output apk 151 .add("--output-format", "proto")], 152 executable = android_toolchain.aapt2.files_to_run, 153 inputs = [packaged_resources_ctx.resources_apk], 154 mnemonic = "Aapt2ConvertToProtoForResourceShrinkerR8", 155 outputs = [proto_resource_apk], 156 toolchain = ANDROID_TOOLCHAIN_TYPE, 157 ) 158 159 # 2. Run the resource shrinker 160 proto_resource_apk_shrunk = ctx.actions.declare_file( 161 ctx.label.name + "_proto_resource_apk_shrunk.ap_", 162 ) 163 java.run( 164 ctx = ctx, 165 host_javabase = common.get_host_javabase(ctx), 166 executable = android_toolchain.resource_shrinker.files_to_run, 167 arguments = [ctx.actions.args() 168 .add("--input", proto_resource_apk) 169 .add("--dex_input", r8_ctx.final_classes_dex_zip) 170 .add("--output", proto_resource_apk_shrunk)], 171 inputs = [proto_resource_apk, r8_ctx.final_classes_dex_zip], 172 outputs = [proto_resource_apk_shrunk], 173 mnemonic = "ResourceShrinkerForR8", 174 progress_message = "Shrinking resources %{label}", 175 ) 176 177 # 3. Convert back to a binary APK 178 resource_apk_shrunk = ctx.actions.declare_file(ctx.label.name + "_resource_apk_shrunk.ap_") 179 ctx.actions.run( 180 arguments = [ctx.actions.args() 181 .add("convert") 182 .add(proto_resource_apk_shrunk) # input apk 183 .add("-o", resource_apk_shrunk) # output apk 184 .add("--output-format", "binary")], 185 executable = android_toolchain.aapt2.files_to_run, 186 inputs = [proto_resource_apk_shrunk], 187 mnemonic = "Aapt2ConvertBackToBinaryForResourceShrinkerR8", 188 outputs = [resource_apk_shrunk], 189 toolchain = ANDROID_TOOLCHAIN_TYPE, 190 ) 191 192 aari = packaged_resources_ctx.android_application_resource 193 194 # Replace the resource apk in the AndroidApplicationResourceInfo provider from resource 195 # processing. 196 new_aari = AndroidApplicationResourceInfo( 197 resource_apk = resource_apk_shrunk, 198 resource_java_src_jar = aari.resource_java_src_jar, 199 resource_java_class_jar = aari.resource_java_class_jar, 200 manifest = aari.manifest, 201 resource_proguard_config = aari.resource_proguard_config, 202 main_dex_proguard_config = aari.main_dex_proguard_config, 203 r_txt = aari.r_txt, 204 resources_zip = aari.resources_zip, 205 databinding_info = aari.databinding_info, 206 should_compile_java_srcs = aari.should_compile_java_srcs, 207 ) 208 209 return ProviderInfo( 210 name = "resource_shrinking_r8_ctx", 211 value = struct( 212 android_application_resource_info_with_shrunk_resource_apk = new_aari, 213 providers = [], 214 ), 215 ) 216