blob: 8684f89457a62cb06a4ac5c268f868bdbf55ed17 [file] [log] [blame]
Morten Krogh-Jespersen45abed92021-01-18 22:07:41 +01001#!/usr/bin/env python3
Mads Ager418d1ca2017-05-22 09:35:49 +02002# Copyright (c) 2017, 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
Tamas Kenezfc34cd82017-07-13 12:43:57 +02006from __future__ import print_function
Tamas Kenez02bff032017-07-18 12:13:58 +02007from glob import glob
Christoffer Quist Adamsen4684a0b2023-09-22 10:21:39 +02008import argparse
Rico Windb57bbc12018-09-20 19:23:32 +02009import copy
Mads Ager418d1ca2017-05-22 09:35:49 +020010import os
Søren Gjesse80e00482021-04-06 13:17:35 +020011import shutil
Mads Ager418d1ca2017-05-22 09:35:49 +020012import sys
Tamas Kenezf2ee2a32017-06-21 10:30:20 +020013import time
Mads Ager418d1ca2017-05-22 09:35:49 +020014
Christoffer Quist Adamsen870fa462020-12-15 10:50:54 +010015import archive
Morten Krogh-Jespersen89e19012021-10-05 09:55:01 +020016import gradle
Søren Gjesse5ecb04a2017-06-13 09:44:32 +020017import gmail_data
Christoffer Quist Adamsena2a58772018-10-03 09:47:46 +020018import nest_data
Søren Gjesse889e09d2019-11-07 16:33:51 +010019from sanitize_libraries import SanitizeLibraries, SanitizeLibrariesInPgconf
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +020020import thread_utils
21from thread_utils import print_thread
Mathias Ravdd6a6de2018-05-18 10:18:33 +020022import toolhelper
Christoffer Quist Adamsen870fa462020-12-15 10:50:54 +010023import update_prebuilds_in_android
Ian Zerny877c1862017-07-06 11:12:26 +020024import utils
25import youtube_data
Rico Wind86bfc832018-09-18 07:48:21 +020026import chrome_data
Mads Ager418d1ca2017-05-22 09:35:49 +020027
28TYPES = ['dex', 'deploy', 'proguarded']
Christoffer Quist Adamsenca2cc7d2023-08-24 09:14:43 +020029APPS = ['nest', 'youtube', 'gmail', 'chrome']
Tamas Kenez63a51d02019-01-07 15:53:02 +010030COMPILERS = ['d8', 'r8']
31COMPILER_BUILDS = ['full', 'lib']
32
Rico Wind5fdec152018-12-17 09:16:14 +010033# We use this magic exit code to signal that the program OOM'ed
34OOM_EXIT_CODE = 42
Jinseong Jeon158a3f12019-02-08 01:40:59 -080035# According to Popen.returncode doc:
36# A negative value -N indicates that the child was terminated by signal N.
37TIMEOUT_KILL_CODE = -9
Mads Ager418d1ca2017-05-22 09:35:49 +020038
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +020039# Log file names
40FIND_MIN_XMX_FILE = 'find_min_xmx_results'
41FIND_MIN_XMX_DIR = 'find_min_xmx'
42
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020043
Tamas Kenez5b1c5852017-07-21 13:38:33 +020044def ParseOptions(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020045 result = argparse.ArgumentParser()
46 result.add_argument('--compiler',
47 help='The compiler to use',
48 choices=COMPILERS)
49 result.add_argument('--compiler-build',
50 help='Compiler build to use',
51 choices=COMPILER_BUILDS,
52 default='lib')
53 result.add_argument('--no-fail-fast',
54 help='Whether run_on_app.py should report all failures '
55 'and not just the first one',
56 default=False,
57 action='store_true')
58 result.add_argument('--hash', help='The version of D8/R8 to use')
59 result.add_argument('--app', help='What app to run on', choices=APPS)
60 result.add_argument('--run-all',
61 help='Compile all possible combinations',
62 default=False,
63 action='store_true')
64 result.add_argument('--expect-oom',
65 help='Expect that compilation will fail with an OOM',
66 default=False,
67 action='store_true')
68 result.add_argument('--type',
69 help='Default for R8: deploy, for D8: proguarded',
70 choices=TYPES)
71 result.add_argument('--out',
72 help='Where to place the output',
73 default=utils.BUILD)
74 result.add_argument('--no-build',
75 help='Run without building first',
76 default=False,
77 action='store_true')
78 result.add_argument('--max-memory',
79 help='The maximum memory in MB to run with',
80 type=int)
81 result.add_argument('--find-min-xmx',
82 help='Find the minimum amount of memory we can run in',
83 default=False,
84 action='store_true')
85 result.add_argument('--find-min-xmx-min-memory',
86 help='Setting the minimum memory baseline to run in',
87 type=int)
88 result.add_argument('--find-min-xmx-max-memory',
89 help='Setting the maximum memory baseline to run in',
90 type=int)
91 result.add_argument('--find-min-xmx-range-size',
92 help='Setting the size of the acceptable memory range',
93 type=int,
94 default=32)
95 result.add_argument('--find-min-xmx-archive',
96 help='Archive find-min-xmx results on GCS',
97 default=False,
98 action='store_true')
99 result.add_argument('--no-extra-pgconf',
100 '--no_extra_pgconf',
101 help='Build without the following extra rules: ' +
102 '-printconfiguration, -printmapping, -printseeds, ' +
103 '-printusage',
104 default=False,
105 action='store_true')
106 result.add_argument('--timeout',
107 type=int,
108 default=0,
109 help='Set timeout instead of waiting for OOM.')
110 result.add_argument('--ignore-java-version',
111 help='Do not check java version',
112 default=False,
113 action='store_true')
114 result.add_argument(
115 '--no-libraries',
116 help='Do not pass in libraries, even if they exist in conf',
117 default=False,
118 action='store_true')
119 result.add_argument(
120 '--disable-assertions',
121 '--disable_assertions',
122 '-da',
123 help='Disable Java assertions when running the compiler '
124 '(default enabled)',
125 default=False,
126 action='store_true')
127 result.add_argument('--debug-agent',
128 help='Run with debug agent.',
129 default=False,
130 action='store_true')
131 result.add_argument('--version', help='The version of the app to run')
132 result.add_argument('-k', help='Override the default ProGuard keep rules')
133 result.add_argument('--compiler-flags',
134 help='Additional option(s) for the compiler. ' +
135 'If passing several options use a quoted string.')
136 result.add_argument('--r8-flags',
137 help='Additional option(s) for the compiler. ' +
138 'Same as --compiler-flags, keeping it for backward'
139 ' compatibility. ' +
140 'If passing several options use a quoted string.')
141 result.add_argument('--track-memory-to-file',
142 help='Track how much memory the jvm is using while ' +
143 ' compiling. Output to the specified file.')
144 result.add_argument('--profile',
145 help='Profile R8 run.',
146 default=False,
147 action='store_true')
148 result.add_argument(
149 '--dump-args-file',
150 help='Dump a file with the arguments for the specified ' +
151 'configuration. For use as a @<file> argument to perform ' + 'the run.')
152 result.add_argument('--print-runtimeraw',
153 metavar='BENCHMARKNAME',
154 help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200155 ' <elapsed> ms\' at the end where <elapsed> is' +
156 ' the elapsed time in milliseconds.')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200157 result.add_argument('--print-memoryuse',
158 metavar='BENCHMARKNAME',
159 help='Print the line \'<BENCHMARKNAME>(MemoryUse):' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200160 ' <mem>\' at the end where <mem> is the peak' +
161 ' peak resident set size (VmHWM) in bytes.')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200162 result.add_argument('--print-dexsegments',
163 metavar='BENCHMARKNAME',
164 help='Print the sizes of individual dex segments as ' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200165 '\'<BENCHMARKNAME>-<segment>(CodeSize): <bytes>\'')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200166 result.add_argument(
167 '--track-time-in-memory',
168 help='Plot the times taken from memory starting point to '
169 'end-point with defined memory increment',
170 default=False,
171 action='store_true')
172 result.add_argument('--track-time-in-memory-max',
173 help='Setting the maximum memory baseline to run in',
174 type=int)
175 result.add_argument('--track-time-in-memory-min',
176 help='Setting the minimum memory baseline to run in',
177 type=int)
178 result.add_argument('--track-time-in-memory-increment',
179 help='Setting the increment',
180 type=int,
181 default=32)
182 result.add_argument('--print-times',
183 help='Include timing',
184 default=False,
185 action='store_true')
186 result.add_argument('--cpu-list',
187 help='Run under \'taskset\' with these CPUs. See '
188 'the \'taskset\' -c option for the format')
189 result.add_argument('--quiet',
190 help='Disable compiler logging',
191 default=False,
192 action='store_true')
193 result.add_argument('--workers',
194 help='Number of workers to use',
195 default=1,
196 type=int)
197 (options, args) = result.parse_known_args(argv)
198 assert not options.hash or options.no_build, (
199 'Argument --no-build is required when using --hash')
200 assert not options.hash or options.compiler_build == 'full', (
201 'Compiler build lib not yet supported with --hash')
202 return (options, args)
203
Mads Ager418d1ca2017-05-22 09:35:49 +0200204
Man Cao29b9ef12019-03-25 11:19:35 -0700205# Most apps have -printmapping, -printseeds, -printusage and
206# -printconfiguration in the Proguard configuration. However we don't
207# want to write these files in the locations specified.
208# Instead generate an auxiliary Proguard configuration placing these
209# output files together with the dex output.
Søren Gjesse3a5aed92017-06-14 15:36:02 +0200210def GenerateAdditionalProguardConfiguration(temp, outdir):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200211 name = "output.config"
212 with open(os.path.join(temp, name), 'w') as f:
213 f.write('-printmapping ' + os.path.join(outdir, 'proguard.map') + "\n")
214 f.write('-printseeds ' + os.path.join(outdir, 'proguard.seeds') + "\n")
215 f.write('-printusage ' + os.path.join(outdir, 'proguard.usage') + "\n")
216 f.write('-printconfiguration ' +
217 os.path.join(outdir, 'proguard.config') + "\n")
218 return os.path.abspath(f.name)
219
Søren Gjesse3a5aed92017-06-14 15:36:02 +0200220
Rico Wind3f9302b2018-09-21 08:53:09 +0200221# Please add bug number for disabled permutations and please explicitly
222# do Bug: #BUG in the commit message of disabling to ensure re-enabling
223DISABLED_PERMUTATIONS = [
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200224 # (app, version, type), e.g., ('gmail', '180826.15', 'deploy')
Rico Wind3f9302b2018-09-21 08:53:09 +0200225]
226
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200227
Rico Windb57bbc12018-09-20 19:23:32 +0200228def get_permutations():
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200229 data_providers = {
230 'nest': nest_data,
231 'youtube': youtube_data,
232 'chrome': chrome_data,
233 'gmail': gmail_data,
234 }
235 # Check to ensure that we add all variants here.
236 assert len(APPS) == len(data_providers)
237 for app, data in data_providers.items():
238 for version in data.VERSIONS:
239 for type in data.VERSIONS[version]:
240 if (app, version, type) not in DISABLED_PERMUTATIONS:
241 # Only run with R8 lib to reduce cycle times.
242 for use_r8lib in [True]:
243 yield app, version, type, use_r8lib
244
Rico Windb57bbc12018-09-20 19:23:32 +0200245
246def run_all(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200247 # Build first so that each job won't.
248 if should_build(options):
249 gradle.RunGradle([utils.GRADLE_TASK_R8LIB])
250 options.no_build = True
251 assert not should_build(options)
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200252
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200253 # Args will be destroyed
254 assert len(args) == 0
255 jobs = []
256 for name, version, type, use_r8lib in get_permutations():
257 compiler = 'r8' if type == 'deploy' else 'd8'
258 compiler_build = 'lib' if use_r8lib else 'full'
259 fixed_options = copy.copy(options)
260 fixed_options.app = name
261 fixed_options.version = version
262 fixed_options.compiler = compiler
263 fixed_options.compiler_build = compiler_build
264 fixed_options.type = type
265 jobs.append(
266 create_job(compiler, compiler_build, name, fixed_options, type,
267 version))
268 exit_code = thread_utils.run_in_parallel(
269 jobs,
270 number_of_workers=options.workers,
271 stop_on_first_failure=not options.no_fail_fast)
272 exit(exit_code)
273
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200274
275def create_job(compiler, compiler_build, name, options, type, version):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200276 return lambda worker_id: run_job(compiler, compiler_build, name, options,
277 type, version, worker_id)
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200278
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200279
280def run_job(compiler, compiler_build, name, options, type, version, worker_id):
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200281 print_thread(
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200282 'Executing %s/%s with %s %s %s' %
283 (compiler, compiler_build, name, version, type), worker_id)
284 if worker_id is not None:
285 options.out = os.path.join(options.out, str(worker_id))
286 os.makedirs(options.out, exist_ok=True)
287 exit_code = run_with_options(options, [], worker_id=worker_id)
288 if exit_code:
289 print_thread(
290 'Failed %s %s %s with %s/%s' %
291 (name, version, type, compiler, compiler_build), worker_id)
292 return exit_code
293
Rico Windb57bbc12018-09-20 19:23:32 +0200294
Rico Wind5fdec152018-12-17 09:16:14 +0100295def find_min_xmx(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200296 # Args will be destroyed
297 assert len(args) == 0
298 # If we can run in 128 MB then we are good (which we can for small examples
299 # or D8 on medium sized examples)
300 if options.find_min_xmx_min_memory:
301 not_working = options.find_min_xmx_min_memory
302 elif options.compiler == 'd8':
303 not_working = 128
Rico Wind5fdec152018-12-17 09:16:14 +0100304 else:
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200305 not_working = 1024
306 if options.find_min_xmx_max_memory:
307 working = options.find_min_xmx_max_memory
308 else:
309 working = 1024 * 8
310 exit_code = 0
311 range = int(options.find_min_xmx_range_size)
312 while working - not_working > range:
313 next_candidate = int(working - ((working - not_working) / 2))
314 print('working: %s, non_working: %s, next_candidate: %s' %
315 (working, not_working, next_candidate))
316 extra_args = ['-Xmx%sM' % next_candidate]
317 t0 = time.time()
318 exit_code = run_with_options(options, [], extra_args)
319 t1 = time.time()
320 print('Running took: %s ms' % (1000.0 * (t1 - t0)))
321 if exit_code != 0:
322 if exit_code not in [OOM_EXIT_CODE, TIMEOUT_KILL_CODE]:
323 print('Non OOM/Timeout error executing, exiting')
324 return 2
325 if exit_code == 0:
326 working = next_candidate
327 elif exit_code == TIMEOUT_KILL_CODE:
328 print('Timeout. Continue to the next candidate.')
329 not_working = next_candidate
330 else:
331 assert exit_code == OOM_EXIT_CODE
332 not_working = next_candidate
Rico Wind5fdec152018-12-17 09:16:14 +0100333
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200334 assert working - not_working <= range
335 found_range = 'Found range: %s - %s' % (not_working, working)
336 print(found_range)
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200337
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200338 if options.find_min_xmx_archive:
339 sha = utils.get_HEAD_sha1()
340 (version, _) = get_version_and_data(options)
341 destination = os.path.join(utils.R8_TEST_RESULTS_BUCKET,
342 FIND_MIN_XMX_DIR, sha, options.compiler,
343 options.compiler_build, options.app, version,
344 get_type(options))
345 gs_destination = 'gs://%s' % destination
346 utils.archive_value(FIND_MIN_XMX_FILE, gs_destination,
347 found_range + '\n')
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200348
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200349 return 0
350
Rico Wind5fdec152018-12-17 09:16:14 +0100351
Morten Krogh-Jespersenf2412302019-10-22 10:18:04 +0200352def print_min_xmx_ranges_for_hash(hash, compiler, compiler_build):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200353 app_directory = os.path.join(utils.R8_TEST_RESULTS_BUCKET, FIND_MIN_XMX_DIR,
354 hash, compiler, compiler_build)
355 gs_base = 'gs://%s' % app_directory
356 for app in utils.ls_files_on_cloud_storage(gs_base).strip().split('\n'):
357 for version in utils.ls_files_on_cloud_storage(app).strip().split('\n'):
358 for type in utils.ls_files_on_cloud_storage(version).strip().split(
359 '\n'):
360 gs_location = '%s%s' % (type, FIND_MIN_XMX_FILE)
361 value = utils.cat_file_on_cloud_storage(gs_location,
362 ignore_errors=True)
363 print('%s\n' % value)
364
Morten Krogh-Jespersenf2412302019-10-22 10:18:04 +0200365
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200366def track_time_in_memory(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200367 # Args will be destroyed
368 assert len(args) == 0
369 if not options.track_time_in_memory_min:
370 raise Exception(
371 'You have to specify --track_time_in_memory_min when running with '
372 '--track-time-in-memory')
373 if not options.track_time_in_memory_max:
374 raise Exception(
375 'You have to specify --track_time_in_memory_max when running with '
376 '--track-time-in-memory')
377 if not options.track_time_in_memory_increment:
378 raise Exception(
379 'You have to specify --track_time_in_memory_increment when running '
380 'with --track-time-in-memory')
381 current = options.track_time_in_memory_min
382 print('Memory (KB)\tTime (ms)')
383 with utils.TempDir() as temp:
384 stdout = os.path.join(temp, 'stdout')
385 stdout_fd = open(stdout, 'w')
386 while current <= options.track_time_in_memory_max:
387 extra_args = ['-Xmx%sM' % current]
388 t0 = time.time()
389 exit_code = run_with_options(options, [],
390 extra_args,
391 stdout_fd,
392 quiet=True)
393 t1 = time.time()
394 total = (1000.0 * (t1 - t0)) if exit_code == 0 else -1
395 print('%s\t%s' % (current, total))
396 current += options.track_time_in_memory_increment
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200397
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200398 return 0
399
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200400
Tamas Kenez5b1c5852017-07-21 13:38:33 +0200401def main(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200402 (options, args) = ParseOptions(argv)
403 if options.expect_oom and not options.max_memory:
404 raise Exception(
405 'You should only use --expect-oom if also specifying --max-memory')
406 if options.expect_oom and options.timeout:
407 raise Exception(
408 'You should not use --timeout when also specifying --expect-oom')
409 if options.find_min_xmx and options.track_time_in_memory:
410 raise Exception(
411 'You cannot both find the min xmx and track time at the same time')
412 if options.run_all:
413 return run_all(options, args)
414 if options.find_min_xmx:
415 return find_min_xmx(options, args)
416 if options.track_time_in_memory:
417 return track_time_in_memory(options, args)
418 exit_code = run_with_options(options, args, quiet=options.quiet)
419 if options.expect_oom:
420 exit_code = 0 if exit_code == OOM_EXIT_CODE else 1
421 return exit_code
422
Rico Windb57bbc12018-09-20 19:23:32 +0200423
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200424def get_version_and_data(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200425 if options.app == 'nest':
426 version = options.version or '20180926'
427 data = nest_data
428 elif options.app == 'youtube':
429 version = options.version or youtube_data.LATEST_VERSION
430 data = youtube_data
431 elif options.app == 'chrome':
432 version = options.version or '180917'
433 data = chrome_data
434 elif options.app == 'gmail':
435 version = options.version or '170604.16'
436 data = gmail_data
437 else:
438 raise Exception("You need to specify '--app={}'".format('|'.join(APPS)))
439 return version, data
440
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200441
442def get_type(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200443 if not options.type:
444 return 'deploy' if options.compiler == 'r8' else 'proguarded'
445 return options.type
446
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200447
Søren Gjesse94793ca2019-11-08 13:53:08 +0100448def has_injars_and_libraryjars(pgconfs):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200449 # Check if there are -injars and -libraryjars in the configuration.
450 has_injars = False
451 has_libraryjars = False
452 for pgconf in pgconfs:
453 pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
454 with open(pgconf) as pgconf_file:
455 for line in pgconf_file:
456 trimmed = line.strip()
457 if trimmed.startswith('-injars'):
458 has_injars = True
459 elif trimmed.startswith('-libraryjars'):
460 has_libraryjars = True
461 if has_injars and has_libraryjars:
462 return True
463 return False
464
Søren Gjesse94793ca2019-11-08 13:53:08 +0100465
Søren Gjesse889e09d2019-11-07 16:33:51 +0100466def check_no_injars_and_no_libraryjars(pgconfs):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200467 # Ensure that there are no -injars or -libraryjars in the configuration.
468 for pgconf in pgconfs:
469 pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
470 with open(pgconf) as pgconf_file:
471 for line in pgconf_file:
472 trimmed = line.strip()
473 if trimmed.startswith('-injars'):
474 raise Exception("Unexpected -injars found in " + pgconf)
475 elif trimmed.startswith('-libraryjars'):
476 raise Exception("Unexpected -libraryjars found in " +
477 pgconf)
478
Søren Gjesse889e09d2019-11-07 16:33:51 +0100479
Søren Gjesse80e00482021-04-06 13:17:35 +0200480def should_build(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200481 return not options.no_build
Søren Gjesse80e00482021-04-06 13:17:35 +0200482
Søren Gjesse80e00482021-04-06 13:17:35 +0200483
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200484def build_desugared_library_dex(options, quiet, temp, android_java8_libs,
485 desugared_lib_pg_conf, inputs, outdir):
486 if not inputs:
487 raise Exception(
488 "If 'android_java8_libs' is specified the inputs must be explicit" +
489 "(not defined using '-injars' in Proguard configuration files)")
490 if outdir.endswith('.zip') or outdir.endswith('.jar'):
491 raise Exception(
492 "If 'android_java8_libs' is specified the output must be a directory"
493 )
Søren Gjesse80e00482021-04-06 13:17:35 +0200494
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200495 jar = None
496 main = None
497 if options.hash:
498 jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
499 main = 'com.android.tools.r8.R8'
Søren Gjesse80e00482021-04-06 13:17:35 +0200500
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200501 # Determine the l8 tool.
502 assert (options.compiler_build in ['full', 'lib'])
503 lib_prefix = 'r8lib-' if options.compiler_build == 'lib' else ''
504 tool = lib_prefix + 'l8'
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200505
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200506 # Prepare out directory.
507 android_java8_libs_output = os.path.join(temp, 'android_java8_libs')
508 os.makedirs(android_java8_libs_output)
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200509
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200510 # Prepare arguments for L8.
511 args = [
512 '--desugared-lib',
513 android_java8_libs['config'],
514 '--lib',
515 android_java8_libs['library'],
516 '--output',
517 android_java8_libs_output,
518 '--pg-conf',
519 desugared_lib_pg_conf,
520 '--release',
521 ]
522 if 'pgconf' in android_java8_libs:
523 for pgconf in android_java8_libs['pgconf']:
524 args.extend(['--pg-conf', pgconf])
525 args.extend(android_java8_libs['program'])
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200526
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200527 # Run L8.
528 exit_code = toolhelper.run(tool,
529 args,
530 build=should_build(options),
531 debug=not options.disable_assertions,
532 quiet=quiet,
533 jar=jar,
534 main=main)
Søren Gjesse80e00482021-04-06 13:17:35 +0200535
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200536 # Copy the desugared library DEX to the output.
537 dex_file_name = ('classes' +
538 str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex')
539 shutil.copyfile(os.path.join(android_java8_libs_output, 'classes.dex'),
540 os.path.join(outdir, dex_file_name))
541
542
543def run_with_options(options,
544 args,
545 extra_args=None,
546 stdout=None,
547 quiet=False,
548 worker_id=None):
549 if extra_args is None:
550 extra_args = []
551 app_provided_pg_conf = False
552 # todo(121018500): remove when memory is under control
553 if not any('-Xmx' in arg for arg in extra_args):
554 if options.max_memory:
555 extra_args.append('-Xmx%sM' % options.max_memory)
556 else:
557 extra_args.append('-Xmx8G')
558 if not options.ignore_java_version:
559 utils.check_java_version()
560
561 if options.print_times:
562 extra_args.append('-Dcom.android.tools.r8.printtimes=1')
563
564 if not options.disable_assertions:
565 extra_args.append('-Dcom.android.tools.r8.enableTestAssertions=1')
566
567 outdir = options.out
568 (version_id, data) = get_version_and_data(options)
569
570 if options.compiler not in COMPILERS:
571 raise Exception("You need to specify '--compiler={}'".format(
572 '|'.join(COMPILERS)))
573
574 if options.compiler_build not in COMPILER_BUILDS:
575 raise Exception("You need to specify '--compiler-build={}'".format(
576 '|'.join(COMPILER_BUILDS)))
577
578 if not version_id in data.VERSIONS.keys():
579 print('No version {} for application {}'.format(version_id,
580 options.app))
581 print('Valid versions are {}'.format(data.VERSIONS.keys()))
582 return 1
583
584 version = data.VERSIONS[version_id]
585
586 type = get_type(options)
587
588 if type not in version:
589 print('No type {} for version {}'.format(type, version))
590 print('Valid types are {}'.format(version.keys()))
591 return 1
592 values = version[type]
593
594 args.extend(['--output', outdir])
595 if 'min-api' in values:
596 args.extend(['--min-api', values['min-api']])
597
598 if 'main-dex-list' in values:
599 args.extend(['--main-dex-list', values['main-dex-list']])
600
601 inputs = values['inputs']
602 libraries = values['libraries'] if 'libraries' in values else []
603
604 if options.compiler == 'r8':
605 if 'pgconf' in values and not options.k:
606 sanitized_lib_path = os.path.join(os.path.abspath(outdir),
607 'sanitized_lib.jar')
608 if has_injars_and_libraryjars(values['pgconf']):
609 sanitized_pgconf_path = os.path.join(os.path.abspath(outdir),
610 'sanitized.config')
611 SanitizeLibrariesInPgconf(sanitized_lib_path,
612 sanitized_pgconf_path,
613 values['pgconf'])
614 libraries = [sanitized_lib_path]
615 args.extend(['--pg-conf', sanitized_pgconf_path])
616 inputs = []
617 else:
618 # -injars without -libraryjars or vice versa is not supported.
619 check_no_injars_and_no_libraryjars(values['pgconf'])
620 for pgconf in values['pgconf']:
621 args.extend(['--pg-conf', pgconf])
622 if 'sanitize_libraries' in values and values[
623 'sanitize_libraries']:
624 SanitizeLibraries(sanitized_lib_path, values['libraries'],
625 values['inputs'])
626 libraries = [sanitized_lib_path]
627 app_provided_pg_conf = True
628 if 'pgconf_extra' in values:
629 extra_conf = os.path.join(os.path.abspath(outdir),
630 'pgconf_extra')
631 with open(extra_conf, 'w') as extra_f:
632 extra_f.write(values['pgconf_extra'])
633 args.extend(['--pg-conf', extra_conf])
634 if options.k:
635 args.extend(['--pg-conf', options.k])
636 if 'maindexrules' in values:
637 for rules in values['maindexrules']:
638 args.extend(['--main-dex-rules', rules])
639 if 'allow-type-errors' in values:
640 extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
641 extra_args.append(
642 '-Dcom.android.tools.r8.disallowClassInlinerGracefulExit=1')
643 if 'system-properties' in values:
644 for system_property in values['system-properties']:
645 extra_args.append(system_property)
646
647 if options.debug_agent:
648 if not options.compiler_build == 'full':
649 print(
650 'WARNING: Running debugging agent on r8lib is questionable...')
651 extra_args.append(
652 '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'
653 )
654
655 if not options.no_libraries:
656 for lib in libraries:
657 args.extend(['--lib', lib])
658
659 if not outdir.endswith('.zip') and not outdir.endswith('.jar') \
660 and not os.path.exists(outdir):
661 os.makedirs(outdir)
662
663 if options.hash:
664 # Download r8-<hash>.jar from
665 # https://storage.googleapis.com/r8-releases/raw/<hash>/.
666 download_path = archive.GetUploadDestination(options.hash, 'r8.jar',
667 True)
668 assert utils.file_exists_on_cloud_storage(download_path), (
669 'Could not find r8.jar file from provided hash: %s' % options.hash)
670 destination = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
671 utils.download_file_from_cloud_storage(download_path,
672 destination,
673 quiet=quiet)
674
675 # Additional flags for the compiler from the configuration file.
676 if 'flags' in values:
677 args.extend(values['flags'].split(' '))
678 if options.compiler == 'r8':
679 if 'r8-flags' in values:
680 args.extend(values['r8-flags'].split(' '))
681
682 # Additional flags for the compiler from the command line.
683 if options.compiler_flags:
684 args.extend(options.compiler_flags.split(' '))
685 if options.r8_flags:
686 args.extend(options.r8_flags.split(' '))
687
688 # Feature jars.
689 features = values['features'] if 'features' in values else []
690 for i, feature in enumerate(features, start=1):
691 feature_out = os.path.join(outdir, 'feature-%d.zip' % i)
692 for feature_jar in feature['inputs']:
693 args.extend(['--feature', feature_jar, feature_out])
694
695 args.extend(inputs)
696
697 t0 = None
698 if options.dump_args_file:
699 with open(options.dump_args_file, 'w') as args_file:
700 args_file.writelines([arg + os.linesep for arg in args])
Morten Krogh-Jespersen322c2f12019-10-08 10:41:21 +0200701 else:
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200702 with utils.TempDir() as temp:
703 if options.print_memoryuse and not options.track_memory_to_file:
704 options.track_memory_to_file = os.path.join(
705 temp, utils.MEMORY_USE_TMP_FILE)
706 if options.compiler == 'r8' and app_provided_pg_conf:
707 # Ensure that output of -printmapping and -printseeds go to the output
708 # location and not where the app Proguard configuration places them.
709 if outdir.endswith('.zip') or outdir.endswith('.jar'):
710 pg_outdir = os.path.dirname(outdir)
711 else:
712 pg_outdir = outdir
713 if not options.no_extra_pgconf:
714 additional_pg_conf = GenerateAdditionalProguardConfiguration(
715 temp, os.path.abspath(pg_outdir))
716 args.extend(['--pg-conf', additional_pg_conf])
Rico Windafdbbfd2019-02-22 09:32:07 +0100717
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200718 android_java8_libs = values.get('android_java8_libs')
719 if android_java8_libs:
720 desugared_lib_pg_conf = os.path.join(
721 temp, 'desugared-lib-pg-conf.txt')
722 args.extend(['--desugared-lib', android_java8_libs['config']])
723 args.extend(
724 ['--desugared-lib-pg-conf-output', desugared_lib_pg_conf])
Søren Gjesse04a94332020-01-27 15:35:42 +0100725
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200726 stderr_path = os.path.join(temp, 'stderr')
727 with open(stderr_path, 'w') as stderr:
728 jar = None
729 main = None
730 if options.compiler_build == 'full':
731 tool = options.compiler
732 else:
733 assert (options.compiler_build == 'lib')
734 tool = 'r8lib-' + options.compiler
735 if options.hash:
736 jar = os.path.join(utils.LIBS,
737 'r8-' + options.hash + '.jar')
738 main = 'com.android.tools.r8.' + options.compiler.upper()
739 if should_build(options):
740 gradle.RunGradle([
741 utils.GRADLE_TASK_R8LIB
742 if tool.startswith('r8lib') else UTILS.GRADLE_TASK_R8
743 ])
744 t0 = time.time()
745 exit_code = toolhelper.run(
746 tool,
747 args,
748 build=False,
749 debug=not options.disable_assertions,
750 profile=options.profile,
751 track_memory_file=options.track_memory_to_file,
752 extra_args=extra_args,
753 stdout=stdout,
754 stderr=stderr,
755 timeout=options.timeout,
756 quiet=quiet,
757 cmd_prefix=['taskset', '-c', options.cpu_list]
758 if options.cpu_list else [],
759 jar=jar,
760 main=main,
761 worker_id=worker_id)
762 if exit_code != 0:
763 with open(stderr_path) as stderr:
764 stderr_text = stderr.read()
765 if not quiet:
766 print(stderr_text)
767 if 'java.lang.OutOfMemoryError' in stderr_text:
768 if not quiet:
769 print('Failure was OOM')
770 return OOM_EXIT_CODE
771 return exit_code
Morten Krogh-Jespersena565f012021-07-13 13:27:41 +0200772
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200773 if options.print_memoryuse:
774 print('{}(MemoryUse): {}'.format(
775 options.print_memoryuse,
776 utils.grep_memoryuse(options.track_memory_to_file)))
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200777
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200778 if android_java8_libs:
779 build_desugared_library_dex(options, quiet, temp,
780 android_java8_libs,
781 desugared_lib_pg_conf, inputs,
782 outdir)
Mads Ager418d1ca2017-05-22 09:35:49 +0200783
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200784 if options.print_runtimeraw:
785 print('{}(RunTimeRaw): {} ms'.format(options.print_runtimeraw,
786 1000.0 * (time.time() - t0)))
Tamas Kenez63a51d02019-01-07 15:53:02 +0100787
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200788 if options.print_dexsegments:
789 dex_files = glob(os.path.join(outdir, '*.dex'))
790 utils.print_dexsegments(options.print_dexsegments, dex_files)
791 print('{}-Total(CodeSize): {}'.format(
Morten Krogh-Jespersen254805e2022-06-03 09:32:42 +0200792 options.print_dexsegments, compute_size_of_dex_files(dex_files)))
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200793 return 0
794
Tamas Kenez02bff032017-07-18 12:13:58 +0200795
Morten Krogh-Jespersenf4a8f762021-12-22 12:31:21 +0100796def compute_size_of_dex_files(dex_files):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200797 dex_size = 0
798 for dex_file in dex_files:
799 dex_size += os.path.getsize(dex_file)
800 return dex_size
801
Morten Krogh-Jespersenf4a8f762021-12-22 12:31:21 +0100802
Mads Ager418d1ca2017-05-22 09:35:49 +0200803if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200804 sys.exit(main(sys.argv[1:]))