xref: /aosp_15_r20/external/bazelbuild-rules_android/mobile_install/launcher.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
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