blob: 31a8eb64f77d0c76af6889fb87a35911e65f9a16 [file] [log] [blame]
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +02001#!/usr/bin/env python3
2# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6import argparse
7import os
8import subprocess
9import sys
10
11sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12
13import apk_masseur
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +010014import apk_utils
Christoffer Quist Adamsen4c6d15d2022-09-08 11:30:04 +020015import extractmarker
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +020016import toolhelper
17import utils
Christoffer Quist Adamsen4c6d15d2022-09-08 11:30:04 +020018import zip_utils
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +020019
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020020LOWEST_SUPPORTED_MIN_API = 21 # Android L (native multi dex)
21
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +010022
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +020023def parse_options(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020024 result = argparse.ArgumentParser(
25 description='Relayout a given APK using a startup profile.')
26 result.add_argument('--apk', help='Path to the .apk', required=True)
Christoffer Adamsen0a513882024-07-03 12:23:33 +020027 result.add_argument('--compress-dex',
28 help='Whether the dex should be stored compressed',
29 action='store_true',
30 default=False)
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020031 result.add_argument(
32 '--desugared-library',
33 choices=['auto', 'true', 'false'],
34 default='auto',
35 help='Whether the last dex file of the app is desugared '
36 'library')
37 result.add_argument('--no-build',
38 action='store_true',
39 default=False,
40 help='To disable building using gradle')
41 result.add_argument('--out',
42 help='Destination of resulting apk',
43 required=True)
44 result.add_argument('--profile', help='Path to the startup profile')
45 options, args = result.parse_known_args(argv)
46 return options, args
47
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +020048
Christoffer Quist Adamsen4c6d15d2022-09-08 11:30:04 +020049def get_dex_to_relayout(options, temp):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020050 marker = extractmarker.extractmarker(options.apk,
51 build=not options.no_build)
52 if '~~L8' not in marker:
53 return [options.apk], None
54 dex_dir = os.path.join(temp, 'dex')
55 dex_predicate = \
56 lambda name : name.startswith('classes') and name.endswith('.dex')
57 extracted_dex_files = \
58 zip_utils.extract_all_that_matches(options.apk, dex_dir, dex_predicate)
59 desugared_library_dex = 'classes%s.dex' % len(extracted_dex_files)
60 assert desugared_library_dex in extracted_dex_files
61 return [
62 os.path.join(dex_dir, name) \
63 for name in extracted_dex_files if name != desugared_library_dex], \
64 os.path.join(dex_dir, desugared_library_dex)
65
Christoffer Quist Adamsen4c6d15d2022-09-08 11:30:04 +020066
67def has_desugared_library_dex(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020068 if options.desugared_library == 'auto':
69 marker = extractmarker.extractmarker(options.apk,
70 build=not options.no_build)
71 return '~~L8' in marker
72 return options.desugared_library == 'true'
73
Christoffer Quist Adamsen4c6d15d2022-09-08 11:30:04 +020074
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +020075def main(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020076 (options, args) = parse_options(argv)
77 with utils.TempDir() as temp:
78 dex = os.path.join(temp, 'dex.zip')
79 d8_args = [
80 '--min-api',
81 str(
82 max(apk_utils.get_min_api(options.apk),
83 LOWEST_SUPPORTED_MIN_API)), '--output', dex,
84 '--no-desugaring', '--release'
85 ]
86 if options.profile:
87 d8_args.extend(['--startup-profile', options.profile])
88 dex_to_relayout, desugared_library_dex = get_dex_to_relayout(
89 options, temp)
90 d8_args.extend(dex_to_relayout)
91 toolhelper.run('d8',
92 d8_args,
93 build=not options.no_build,
94 main='com.android.tools.r8.D8')
95 if desugared_library_dex is not None:
96 dex_files = [name for name in \
97 zip_utils.get_names_that_matches(dex, lambda x : True)]
98 zip_utils.add_file_to_zip(desugared_library_dex,
99 'classes%s.dex' % str(len(dex_files) + 1),
100 dex)
Christoffer Adamsen0a513882024-07-03 12:23:33 +0200101 apk_masseur.masseur(options.apk,
102 dex=dex,
103 out=options.out,
104 compress_dex=options.compress_dex)
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200105
Christoffer Quist Adamsen84da5262022-08-24 13:54:21 +0200106
107if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200108 sys.exit(main(sys.argv[1:]))