Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 1 | #!/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 | |
| 6 | import argparse |
| 7 | import os |
| 8 | import subprocess |
| 9 | import sys |
| 10 | |
| 11 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 12 | |
| 13 | import apk_masseur |
Christoffer Quist Adamsen | eb29bfe | 2023-02-21 14:50:22 +0100 | [diff] [blame] | 14 | import apk_utils |
Christoffer Quist Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 15 | import extractmarker |
Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 16 | import toolhelper |
| 17 | import utils |
Christoffer Quist Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 18 | import zip_utils |
Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 19 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 20 | LOWEST_SUPPORTED_MIN_API = 21 # Android L (native multi dex) |
| 21 | |
Christoffer Quist Adamsen | eb29bfe | 2023-02-21 14:50:22 +0100 | [diff] [blame] | 22 | |
Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 23 | def parse_options(argv): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 24 | 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 Adamsen | 0a51388 | 2024-07-03 12:23:33 +0200 | [diff] [blame] | 27 | result.add_argument('--compress-dex', |
| 28 | help='Whether the dex should be stored compressed', |
| 29 | action='store_true', |
| 30 | default=False) |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 31 | 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 Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 48 | |
Christoffer Quist Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 49 | def get_dex_to_relayout(options, temp): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 50 | 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 Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 66 | |
| 67 | def has_desugared_library_dex(options): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 68 | 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 Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 74 | |
Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 75 | def main(argv): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 76 | (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 Adamsen | 0a51388 | 2024-07-03 12:23:33 +0200 | [diff] [blame] | 101 | apk_masseur.masseur(options.apk, |
| 102 | dex=dex, |
| 103 | out=options.out, |
| 104 | compress_dex=options.compress_dex) |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 105 | |
Christoffer Quist Adamsen | 84da526 | 2022-08-24 13:54:21 +0200 | [diff] [blame] | 106 | |
| 107 | if __name__ == '__main__': |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 108 | sys.exit(main(sys.argv[1:])) |