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"""Creates the app launcher scripts.""" 15 16load(":utils.bzl", "utils") 17load(":workspace.bzl", "make_dex_sync", "make_generic_sync", "merge_syncs") 18load(":deploy_info.bzl", "make_deploy_info_pb") 19load(":providers.bzl", "MIAppInfo", "MIAppLaunchInfo") 20load("//rules/flags:flags.bzl", "flags") 21 22HOST_TEST_WORKSPACE = "host_test_runner_workspace" 23 24_DEPLOY_SCRIPT = '''#!/bin/bash 25set -e # exit on failure 26umask 022 # set default file/dir creation mode to 755 27 28FLAGS={flags} 29TEST_FLAGS={test_flags} 30DEPLOY={deploy} 31 32 33ALL_TEST_ARGS=("$@") 34if [[ ! -z ${{TEST_FLAGS}} ]]; 35then 36 RULE_TEST_ARGS={test_args} 37 ALL_TEST_ARGS=("--nolaunch_app" "${{RULE_TEST_ARGS[@]}}" "$@") 38 "${{DEPLOY}}" \ 39 -flagfile="${{TEST_FLAGS}}" \ 40 "${{ALL_TEST_ARGS[@]}}" 41else 42 "${{DEPLOY}}" -flagfile="${{FLAGS}}" \ 43 "${{ALL_TEST_ARGS[@]}}" 44fi 45 46''' 47 48def _make_deploy_script( 49 ctx, 50 out_script, 51 deploy, 52 flags, 53 test_args = "", 54 test_flags = ""): 55 deploy_contents = _DEPLOY_SCRIPT.format( 56 deploy = deploy, 57 flags = flags, 58 test_flags = test_flags, 59 test_args = test_args, 60 ) 61 ctx.actions.write(out_script, deploy_contents, is_executable = True) 62 63def _make_app_runner( 64 ctx, 65 sync, 66 manifest_package_name_path, 67 out_launcher, 68 out_launcher_flags, 69 shell_apk = None, 70 splits = None, 71 deploy_info_pb = None, 72 test_apk = None, 73 test_data = None, 74 test_args = None, 75 instrumented_app_package_name = None, 76 use_adb_root = True, 77 enable_metrics_logging = False, 78 use_studio_deployer = True, 79 is_test = False): 80 path_type = "path" if ctx.attr._mi_is_cmd else "short_path" 81 82 deploy = utils.first(ctx.attr._deploy.files.to_list()) 83 84 args = { 85 "data_sync_path": getattr(sync, path_type), 86 "is_cmd": str(ctx.attr._mi_is_cmd).lower(), 87 "manifest_package_name_path": getattr(manifest_package_name_path, path_type), 88 "target": ctx.label, 89 } 90 if shell_apk: 91 args["shell_app_path"] = getattr(shell_apk, path_type) 92 93 if splits: 94 args["splits"] = [getattr(s, path_type) for s in splits] 95 args["enable_splits"] = True 96 97 if ctx.attr._mi_is_cmd: 98 args["host_test_runner_workspace"] = HOST_TEST_WORKSPACE 99 100 args["java_home"] = utils.host_jvm_path(ctx) 101 102 args["studio_deployer"] = getattr(ctx.file._studio_deployer, path_type) 103 args["use_adb_root"] = str(use_adb_root).lower() 104 args["enable_metrics_logging"] = str(enable_metrics_logging).lower() 105 args["use_studio_deployer"] = str(use_studio_deployer).lower() 106 107 args["use_direct_deploy"] = True 108 109 android_test_runner = None 110 if is_test and hasattr(ctx.attr, "_android_test_runner"): 111 android_test_runner = ctx.file._android_test_runner 112 args["android_test_runner"] = getattr(android_test_runner, path_type) 113 args["is_test"] = True 114 115 if test_data: 116 args["data_files"] = ",".join([f.short_path for f in test_data]) 117 118 if test_apk: 119 args["test_apk"] = test_apk.path 120 121 if instrumented_app_package_name: 122 args["instrumented_app_package_name"] = getattr(instrumented_app_package_name, path_type) 123 124 if deploy_info_pb: 125 args["deploy_info"] = getattr(deploy_info_pb, path_type) 126 127 utils.create_flag_file(ctx, out_launcher_flags, **args) 128 129 _make_deploy_script( 130 ctx, 131 out_launcher, 132 getattr(deploy, path_type), 133 flags = getattr(out_launcher_flags, path_type), 134 # Converts the python array of args into a bash array. Each arg is 135 # wrapped with quotes to handle "space" separted flag value entries 136 # and as result also escapes existing quotes. 137 test_args = ("(%s)" % " ".join([ 138 '"--test_arg=%s"' % arg.replace('"', '\\"') 139 for arg in test_args 140 ])) if test_args else "", 141 test_flags = getattr(out_launcher_flags, path_type) if test_args or is_test else "", 142 ) 143 144 runner = [deploy] 145 if android_test_runner: 146 runner.extend(ctx.attr._java_jdk.files.to_list()) 147 runner.append(android_test_runner) 148 return runner 149 150def make_direct_launcher( 151 ctx, 152 mi_app_info, 153 launcher, 154 test_data = None, 155 test_args = None, 156 test_support_apps = None, 157 use_adb_root = True, 158 is_test = False): 159 """ Runfiles for launching the apps are created. 160 161 Args: 162 ctx: The context 163 mi_app_info: The MIAppInfo provider 164 launcher: The launcher file 165 test_data: The test data 166 test_support_apps: The test support apps 167 test_args: The test arguments 168 use_adb_root: Boolean argument to restart adb with root permissions. 169 is_test: Boolean argument to identify if it's a test 170 Returns: 171 A list of files required for runtime common for both running binary and test. 172 """ 173 app_pbs = [] 174 runfiles = [] 175 176 launcher_flags = utils.isolated_declare_file(ctx, "launcher.flag", sibling = launcher) 177 178 runfiles.extend([launcher, launcher_flags]) 179 180 runfiles.append(ctx.file._studio_deployer) 181 if getattr(mi_app_info, "merged_manifest", None): 182 runfiles.append(mi_app_info.merged_manifest) 183 runfiles.append(mi_app_info.manifest_package_name) 184 185 shell_apk = None 186 if hasattr(mi_app_info, "shell_apk") and mi_app_info.shell_apk: 187 shell_apk = mi_app_info.shell_apk 188 app_pbs.append( 189 make_generic_sync( 190 ctx, 191 files = [shell_apk], 192 replacements = [ 193 shell_apk.short_path[:shell_apk.short_path.rindex("/")], 194 "apk", 195 ], 196 sibling = launcher, 197 ), 198 ) 199 runfiles.append(mi_app_info.shell_apk) 200 201 splits = None 202 if hasattr(mi_app_info, "splits"): 203 splits = mi_app_info.splits 204 runfiles.extend(mi_app_info.splits) 205 206 if hasattr(mi_app_info, "native_zip") and mi_app_info.native_zip: 207 app_pbs.append(make_generic_sync(ctx, zips = [mi_app_info.native_zip], sibling = launcher)) 208 runfiles.append(mi_app_info.native_zip) 209 210 if hasattr(mi_app_info, "r_dex"): 211 runfiles.append(mi_app_info.r_dex) 212 app_pbs.append(make_dex_sync(ctx, mi_app_info.r_dex, dir_name = "rdexes", sibling = launcher)) 213 214 if hasattr(mi_app_info, "merged_dex_shards"): 215 runfiles.extend(mi_app_info.merged_dex_shards) 216 bin_dex_sync = merge_syncs( 217 ctx, 218 [ 219 make_dex_sync(ctx, dex_shard, sibling = launcher) 220 for dex_shard in mi_app_info.merged_dex_shards 221 ], 222 "bin", 223 sibling = launcher, 224 ) 225 app_pbs.append(bin_dex_sync) 226 227 deploy_info_pb = None 228 if hasattr(mi_app_info, "merged_manifest"): 229 deploy_info_pb = make_deploy_info_pb( 230 ctx, 231 mi_app_info.merged_manifest, 232 mi_app_info.splits if mi_app_info.splits else [mi_app_info.shell_apk], 233 ) 234 runfiles.append(deploy_info_pb) 235 236 if test_data: 237 runfiles.extend(test_data) 238 if is_test: 239 test_apk = mi_app_info.apk 240 runfiles.append(test_apk) 241 else: 242 test_apk = None 243 244 sync = utils.make_sync(ctx, app_pbs, mi_app_info.manifest_package_name, "app", sibling = launcher) 245 runfiles.append(sync) 246 247 sync_pbs = [] 248 sync_pbs.append(sync) 249 instrumented_app_package_name = None 250 instrumented_app = None 251 if hasattr(mi_app_info, "instrumented_app") and mi_app_info.instrumented_app: 252 sync_pbs.append(mi_app_info.instrumented_app[MIAppLaunchInfo].sync) 253 runfiles.extend(mi_app_info.instrumented_app[MIAppLaunchInfo].runfiles) 254 instrumented_app_package_name = mi_app_info.instrumented_app[MIAppInfo].manifest_package_name 255 256 if test_support_apps: 257 for support_app in test_support_apps: 258 if MIAppLaunchInfo in support_app: 259 sync_pbs.append(support_app[MIAppLaunchInfo].sync) 260 runfiles.extend(support_app[MIAppLaunchInfo].runfiles) 261 262 if len(sync_pbs) > 1: 263 final_sync_pb = utils.sync_merger(ctx, sync_pbs, sibling = launcher) 264 runfiles.append(final_sync_pb) 265 else: 266 final_sync_pb = sync 267 268 runfiles.extend(_make_app_runner( 269 ctx, 270 final_sync_pb, 271 mi_app_info.manifest_package_name, 272 launcher, 273 launcher_flags, 274 shell_apk = shell_apk, 275 splits = splits, 276 deploy_info_pb = deploy_info_pb, 277 test_apk = test_apk, 278 test_data = test_data, 279 test_args = test_args, 280 instrumented_app_package_name = instrumented_app_package_name, 281 use_adb_root = use_adb_root, 282 enable_metrics_logging = flags.get(ctx).enable_metrics_logging, 283 use_studio_deployer = flags.get(ctx).use_studio_deployer, 284 is_test = is_test, 285 )) 286 287 return MIAppLaunchInfo( 288 launcher = launcher, 289 launcher_flags = launcher_flags, 290 runfiles = runfiles, 291 sync = sync, 292 ) 293