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