blob: f1060226cc3c0445dd41b5770750766e5c456b9c [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
Christoffer Quist Adamsena2a58772018-10-03 09:47:46 +020017import nest_data
Søren Gjesse889e09d2019-11-07 16:33:51 +010018from sanitize_libraries import SanitizeLibraries, SanitizeLibrariesInPgconf
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +020019import thread_utils
20from thread_utils import print_thread
Mathias Ravdd6a6de2018-05-18 10:18:33 +020021import toolhelper
Christoffer Quist Adamsen870fa462020-12-15 10:50:54 +010022import update_prebuilds_in_android
Ian Zerny877c1862017-07-06 11:12:26 +020023import utils
24import youtube_data
Rico Wind86bfc832018-09-18 07:48:21 +020025import chrome_data
Mads Ager418d1ca2017-05-22 09:35:49 +020026
27TYPES = ['dex', 'deploy', 'proguarded']
Ian Zernyce1f1262024-04-08 14:14:04 +020028APPS = ['nest', 'youtube', 'chrome']
Tamas Kenez63a51d02019-01-07 15:53:02 +010029COMPILERS = ['d8', 'r8']
30COMPILER_BUILDS = ['full', 'lib']
31
Rico Wind5fdec152018-12-17 09:16:14 +010032# We use this magic exit code to signal that the program OOM'ed
33OOM_EXIT_CODE = 42
Jinseong Jeon158a3f12019-02-08 01:40:59 -080034# According to Popen.returncode doc:
35# A negative value -N indicates that the child was terminated by signal N.
36TIMEOUT_KILL_CODE = -9
Mads Ager418d1ca2017-05-22 09:35:49 +020037
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +020038# Log file names
39FIND_MIN_XMX_FILE = 'find_min_xmx_results'
40FIND_MIN_XMX_DIR = 'find_min_xmx'
41
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020042
Tamas Kenez5b1c5852017-07-21 13:38:33 +020043def ParseOptions(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020044 result = argparse.ArgumentParser()
45 result.add_argument('--compiler',
46 help='The compiler to use',
47 choices=COMPILERS)
48 result.add_argument('--compiler-build',
49 help='Compiler build to use',
50 choices=COMPILER_BUILDS,
51 default='lib')
52 result.add_argument('--no-fail-fast',
53 help='Whether run_on_app.py should report all failures '
54 'and not just the first one',
55 default=False,
56 action='store_true')
57 result.add_argument('--hash', help='The version of D8/R8 to use')
58 result.add_argument('--app', help='What app to run on', choices=APPS)
59 result.add_argument('--run-all',
60 help='Compile all possible combinations',
61 default=False,
62 action='store_true')
63 result.add_argument('--expect-oom',
64 help='Expect that compilation will fail with an OOM',
65 default=False,
66 action='store_true')
67 result.add_argument('--type',
68 help='Default for R8: deploy, for D8: proguarded',
69 choices=TYPES)
70 result.add_argument('--out',
71 help='Where to place the output',
72 default=utils.BUILD)
73 result.add_argument('--no-build',
74 help='Run without building first',
75 default=False,
76 action='store_true')
77 result.add_argument('--max-memory',
78 help='The maximum memory in MB to run with',
79 type=int)
80 result.add_argument('--find-min-xmx',
81 help='Find the minimum amount of memory we can run in',
82 default=False,
83 action='store_true')
84 result.add_argument('--find-min-xmx-min-memory',
85 help='Setting the minimum memory baseline to run in',
86 type=int)
87 result.add_argument('--find-min-xmx-max-memory',
88 help='Setting the maximum memory baseline to run in',
89 type=int)
90 result.add_argument('--find-min-xmx-range-size',
91 help='Setting the size of the acceptable memory range',
92 type=int,
93 default=32)
94 result.add_argument('--find-min-xmx-archive',
95 help='Archive find-min-xmx results on GCS',
96 default=False,
97 action='store_true')
98 result.add_argument('--no-extra-pgconf',
99 '--no_extra_pgconf',
100 help='Build without the following extra rules: ' +
101 '-printconfiguration, -printmapping, -printseeds, ' +
102 '-printusage',
103 default=False,
104 action='store_true')
105 result.add_argument('--timeout',
106 type=int,
107 default=0,
108 help='Set timeout instead of waiting for OOM.')
109 result.add_argument('--ignore-java-version',
110 help='Do not check java version',
111 default=False,
112 action='store_true')
113 result.add_argument(
114 '--no-libraries',
115 help='Do not pass in libraries, even if they exist in conf',
116 default=False,
117 action='store_true')
118 result.add_argument(
119 '--disable-assertions',
120 '--disable_assertions',
121 '-da',
122 help='Disable Java assertions when running the compiler '
123 '(default enabled)',
124 default=False,
125 action='store_true')
126 result.add_argument('--debug-agent',
127 help='Run with debug agent.',
128 default=False,
129 action='store_true')
130 result.add_argument('--version', help='The version of the app to run')
131 result.add_argument('-k', help='Override the default ProGuard keep rules')
132 result.add_argument('--compiler-flags',
133 help='Additional option(s) for the compiler. ' +
134 'If passing several options use a quoted string.')
135 result.add_argument('--r8-flags',
136 help='Additional option(s) for the compiler. ' +
137 'Same as --compiler-flags, keeping it for backward'
138 ' compatibility. ' +
139 'If passing several options use a quoted string.')
140 result.add_argument('--track-memory-to-file',
141 help='Track how much memory the jvm is using while ' +
142 ' compiling. Output to the specified file.')
143 result.add_argument('--profile',
144 help='Profile R8 run.',
145 default=False,
146 action='store_true')
147 result.add_argument(
148 '--dump-args-file',
149 help='Dump a file with the arguments for the specified ' +
150 'configuration. For use as a @<file> argument to perform ' + 'the run.')
151 result.add_argument('--print-runtimeraw',
152 metavar='BENCHMARKNAME',
153 help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200154 ' <elapsed> ms\' at the end where <elapsed> is' +
155 ' the elapsed time in milliseconds.')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200156 result.add_argument('--print-memoryuse',
157 metavar='BENCHMARKNAME',
158 help='Print the line \'<BENCHMARKNAME>(MemoryUse):' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200159 ' <mem>\' at the end where <mem> is the peak' +
160 ' peak resident set size (VmHWM) in bytes.')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200161 result.add_argument('--print-dexsegments',
162 metavar='BENCHMARKNAME',
163 help='Print the sizes of individual dex segments as ' +
Tamas Kenez02bff032017-07-18 12:13:58 +0200164 '\'<BENCHMARKNAME>-<segment>(CodeSize): <bytes>\'')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200165 result.add_argument(
166 '--track-time-in-memory',
167 help='Plot the times taken from memory starting point to '
168 'end-point with defined memory increment',
169 default=False,
170 action='store_true')
171 result.add_argument('--track-time-in-memory-max',
172 help='Setting the maximum memory baseline to run in',
173 type=int)
174 result.add_argument('--track-time-in-memory-min',
175 help='Setting the minimum memory baseline to run in',
176 type=int)
177 result.add_argument('--track-time-in-memory-increment',
178 help='Setting the increment',
179 type=int,
180 default=32)
181 result.add_argument('--print-times',
182 help='Include timing',
183 default=False,
184 action='store_true')
185 result.add_argument('--cpu-list',
186 help='Run under \'taskset\' with these CPUs. See '
187 'the \'taskset\' -c option for the format')
188 result.add_argument('--quiet',
189 help='Disable compiler logging',
190 default=False,
191 action='store_true')
192 result.add_argument('--workers',
193 help='Number of workers to use',
194 default=1,
195 type=int)
196 (options, args) = result.parse_known_args(argv)
197 assert not options.hash or options.no_build, (
198 'Argument --no-build is required when using --hash')
199 assert not options.hash or options.compiler_build == 'full', (
200 'Compiler build lib not yet supported with --hash')
201 return (options, args)
202
Mads Ager418d1ca2017-05-22 09:35:49 +0200203
Man Cao29b9ef12019-03-25 11:19:35 -0700204# Most apps have -printmapping, -printseeds, -printusage and
205# -printconfiguration in the Proguard configuration. However we don't
206# want to write these files in the locations specified.
207# Instead generate an auxiliary Proguard configuration placing these
208# output files together with the dex output.
Søren Gjesse3a5aed92017-06-14 15:36:02 +0200209def GenerateAdditionalProguardConfiguration(temp, outdir):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200210 name = "output.config"
211 with open(os.path.join(temp, name), 'w') as f:
212 f.write('-printmapping ' + os.path.join(outdir, 'proguard.map') + "\n")
213 f.write('-printseeds ' + os.path.join(outdir, 'proguard.seeds') + "\n")
214 f.write('-printusage ' + os.path.join(outdir, 'proguard.usage') + "\n")
215 f.write('-printconfiguration ' +
216 os.path.join(outdir, 'proguard.config') + "\n")
217 return os.path.abspath(f.name)
218
Søren Gjesse3a5aed92017-06-14 15:36:02 +0200219
Rico Wind3f9302b2018-09-21 08:53:09 +0200220# Please add bug number for disabled permutations and please explicitly
221# do Bug: #BUG in the commit message of disabling to ensure re-enabling
222DISABLED_PERMUTATIONS = [
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200223 # (app, version, type), e.g., ('gmail', '180826.15', 'deploy')
Rico Wind3f9302b2018-09-21 08:53:09 +0200224]
225
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200226
Rico Windb57bbc12018-09-20 19:23:32 +0200227def get_permutations():
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200228 data_providers = {
229 'nest': nest_data,
230 'youtube': youtube_data,
231 'chrome': chrome_data,
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200232 }
233 # Check to ensure that we add all variants here.
234 assert len(APPS) == len(data_providers)
235 for app, data in data_providers.items():
236 for version in data.VERSIONS:
237 for type in data.VERSIONS[version]:
238 if (app, version, type) not in DISABLED_PERMUTATIONS:
239 # Only run with R8 lib to reduce cycle times.
240 for use_r8lib in [True]:
241 yield app, version, type, use_r8lib
242
Rico Windb57bbc12018-09-20 19:23:32 +0200243
244def run_all(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200245 # Build first so that each job won't.
246 if should_build(options):
247 gradle.RunGradle([utils.GRADLE_TASK_R8LIB])
248 options.no_build = True
249 assert not should_build(options)
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200250
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200251 # Args will be destroyed
252 assert len(args) == 0
253 jobs = []
254 for name, version, type, use_r8lib in get_permutations():
255 compiler = 'r8' if type == 'deploy' else 'd8'
256 compiler_build = 'lib' if use_r8lib else 'full'
257 fixed_options = copy.copy(options)
258 fixed_options.app = name
259 fixed_options.version = version
260 fixed_options.compiler = compiler
261 fixed_options.compiler_build = compiler_build
262 fixed_options.type = type
263 jobs.append(
264 create_job(compiler, compiler_build, name, fixed_options, type,
265 version))
266 exit_code = thread_utils.run_in_parallel(
267 jobs,
268 number_of_workers=options.workers,
269 stop_on_first_failure=not options.no_fail_fast)
270 exit(exit_code)
271
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200272
273def create_job(compiler, compiler_build, name, options, type, version):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200274 return lambda worker_id: run_job(compiler, compiler_build, name, options,
275 type, version, worker_id)
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200276
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200277
278def run_job(compiler, compiler_build, name, options, type, version, worker_id):
Christoffer Quist Adamsen65ef2982023-08-24 08:45:39 +0200279 print_thread(
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200280 'Executing %s/%s with %s %s %s' %
281 (compiler, compiler_build, name, version, type), worker_id)
282 if worker_id is not None:
283 options.out = os.path.join(options.out, str(worker_id))
284 os.makedirs(options.out, exist_ok=True)
285 exit_code = run_with_options(options, [], worker_id=worker_id)
286 if exit_code:
287 print_thread(
288 'Failed %s %s %s with %s/%s' %
289 (name, version, type, compiler, compiler_build), worker_id)
290 return exit_code
291
Rico Windb57bbc12018-09-20 19:23:32 +0200292
Rico Wind5fdec152018-12-17 09:16:14 +0100293def find_min_xmx(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200294 # Args will be destroyed
295 assert len(args) == 0
296 # If we can run in 128 MB then we are good (which we can for small examples
297 # or D8 on medium sized examples)
298 if options.find_min_xmx_min_memory:
299 not_working = options.find_min_xmx_min_memory
300 elif options.compiler == 'd8':
301 not_working = 128
Rico Wind5fdec152018-12-17 09:16:14 +0100302 else:
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200303 not_working = 1024
304 if options.find_min_xmx_max_memory:
305 working = options.find_min_xmx_max_memory
306 else:
307 working = 1024 * 8
308 exit_code = 0
309 range = int(options.find_min_xmx_range_size)
310 while working - not_working > range:
311 next_candidate = int(working - ((working - not_working) / 2))
312 print('working: %s, non_working: %s, next_candidate: %s' %
313 (working, not_working, next_candidate))
314 extra_args = ['-Xmx%sM' % next_candidate]
315 t0 = time.time()
316 exit_code = run_with_options(options, [], extra_args)
317 t1 = time.time()
318 print('Running took: %s ms' % (1000.0 * (t1 - t0)))
319 if exit_code != 0:
320 if exit_code not in [OOM_EXIT_CODE, TIMEOUT_KILL_CODE]:
321 print('Non OOM/Timeout error executing, exiting')
322 return 2
323 if exit_code == 0:
324 working = next_candidate
325 elif exit_code == TIMEOUT_KILL_CODE:
326 print('Timeout. Continue to the next candidate.')
327 not_working = next_candidate
328 else:
329 assert exit_code == OOM_EXIT_CODE
330 not_working = next_candidate
Rico Wind5fdec152018-12-17 09:16:14 +0100331
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200332 assert working - not_working <= range
333 found_range = 'Found range: %s - %s' % (not_working, working)
334 print(found_range)
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200335
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200336 if options.find_min_xmx_archive:
337 sha = utils.get_HEAD_sha1()
338 (version, _) = get_version_and_data(options)
339 destination = os.path.join(utils.R8_TEST_RESULTS_BUCKET,
340 FIND_MIN_XMX_DIR, sha, options.compiler,
341 options.compiler_build, options.app, version,
342 get_type(options))
343 gs_destination = 'gs://%s' % destination
344 utils.archive_value(FIND_MIN_XMX_FILE, gs_destination,
345 found_range + '\n')
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200346
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200347 return 0
348
Rico Wind5fdec152018-12-17 09:16:14 +0100349
Morten Krogh-Jespersenf2412302019-10-22 10:18:04 +0200350def print_min_xmx_ranges_for_hash(hash, compiler, compiler_build):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200351 app_directory = os.path.join(utils.R8_TEST_RESULTS_BUCKET, FIND_MIN_XMX_DIR,
352 hash, compiler, compiler_build)
353 gs_base = 'gs://%s' % app_directory
354 for app in utils.ls_files_on_cloud_storage(gs_base).strip().split('\n'):
355 for version in utils.ls_files_on_cloud_storage(app).strip().split('\n'):
356 for type in utils.ls_files_on_cloud_storage(version).strip().split(
357 '\n'):
358 gs_location = '%s%s' % (type, FIND_MIN_XMX_FILE)
359 value = utils.cat_file_on_cloud_storage(gs_location,
360 ignore_errors=True)
361 print('%s\n' % value)
362
Morten Krogh-Jespersenf2412302019-10-22 10:18:04 +0200363
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200364def track_time_in_memory(options, args):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200365 # Args will be destroyed
366 assert len(args) == 0
367 if not options.track_time_in_memory_min:
368 raise Exception(
369 'You have to specify --track_time_in_memory_min when running with '
370 '--track-time-in-memory')
371 if not options.track_time_in_memory_max:
372 raise Exception(
373 'You have to specify --track_time_in_memory_max when running with '
374 '--track-time-in-memory')
375 if not options.track_time_in_memory_increment:
376 raise Exception(
377 'You have to specify --track_time_in_memory_increment when running '
378 'with --track-time-in-memory')
379 current = options.track_time_in_memory_min
380 print('Memory (KB)\tTime (ms)')
381 with utils.TempDir() as temp:
382 stdout = os.path.join(temp, 'stdout')
383 stdout_fd = open(stdout, 'w')
384 while current <= options.track_time_in_memory_max:
385 extra_args = ['-Xmx%sM' % current]
386 t0 = time.time()
387 exit_code = run_with_options(options, [],
388 extra_args,
389 stdout_fd,
390 quiet=True)
391 t1 = time.time()
392 total = (1000.0 * (t1 - t0)) if exit_code == 0 else -1
393 print('%s\t%s' % (current, total))
394 current += options.track_time_in_memory_increment
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200395
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200396 return 0
397
Morten Krogh-Jespersenae9557c2019-10-23 15:14:02 +0200398
Tamas Kenez5b1c5852017-07-21 13:38:33 +0200399def main(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200400 (options, args) = ParseOptions(argv)
401 if options.expect_oom and not options.max_memory:
402 raise Exception(
403 'You should only use --expect-oom if also specifying --max-memory')
404 if options.expect_oom and options.timeout:
405 raise Exception(
406 'You should not use --timeout when also specifying --expect-oom')
407 if options.find_min_xmx and options.track_time_in_memory:
408 raise Exception(
409 'You cannot both find the min xmx and track time at the same time')
410 if options.run_all:
411 return run_all(options, args)
412 if options.find_min_xmx:
413 return find_min_xmx(options, args)
414 if options.track_time_in_memory:
415 return track_time_in_memory(options, args)
416 exit_code = run_with_options(options, args, quiet=options.quiet)
417 if options.expect_oom:
418 exit_code = 0 if exit_code == OOM_EXIT_CODE else 1
419 return exit_code
420
Rico Windb57bbc12018-09-20 19:23:32 +0200421
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200422def get_version_and_data(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200423 if options.app == 'nest':
424 version = options.version or '20180926'
425 data = nest_data
426 elif options.app == 'youtube':
427 version = options.version or youtube_data.LATEST_VERSION
428 data = youtube_data
429 elif options.app == 'chrome':
430 version = options.version or '180917'
431 data = chrome_data
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200432 else:
433 raise Exception("You need to specify '--app={}'".format('|'.join(APPS)))
434 return version, data
435
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200436
437def get_type(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200438 if not options.type:
439 return 'deploy' if options.compiler == 'r8' else 'proguarded'
440 return options.type
441
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200442
Søren Gjesse94793ca2019-11-08 13:53:08 +0100443def has_injars_and_libraryjars(pgconfs):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200444 # Check if there are -injars and -libraryjars in the configuration.
445 has_injars = False
446 has_libraryjars = False
447 for pgconf in pgconfs:
448 pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
449 with open(pgconf) as pgconf_file:
450 for line in pgconf_file:
451 trimmed = line.strip()
452 if trimmed.startswith('-injars'):
453 has_injars = True
454 elif trimmed.startswith('-libraryjars'):
455 has_libraryjars = True
456 if has_injars and has_libraryjars:
457 return True
458 return False
459
Søren Gjesse94793ca2019-11-08 13:53:08 +0100460
Søren Gjesse889e09d2019-11-07 16:33:51 +0100461def check_no_injars_and_no_libraryjars(pgconfs):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200462 # Ensure that there are no -injars or -libraryjars in the configuration.
463 for pgconf in pgconfs:
464 pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
465 with open(pgconf) as pgconf_file:
466 for line in pgconf_file:
467 trimmed = line.strip()
468 if trimmed.startswith('-injars'):
469 raise Exception("Unexpected -injars found in " + pgconf)
470 elif trimmed.startswith('-libraryjars'):
471 raise Exception("Unexpected -libraryjars found in " +
472 pgconf)
473
Søren Gjesse889e09d2019-11-07 16:33:51 +0100474
Søren Gjesse80e00482021-04-06 13:17:35 +0200475def should_build(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200476 return not options.no_build
Søren Gjesse80e00482021-04-06 13:17:35 +0200477
Søren Gjesse80e00482021-04-06 13:17:35 +0200478
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200479def build_desugared_library_dex(options, quiet, temp, android_java8_libs,
480 desugared_lib_pg_conf, inputs, outdir):
481 if not inputs:
482 raise Exception(
483 "If 'android_java8_libs' is specified the inputs must be explicit" +
484 "(not defined using '-injars' in Proguard configuration files)")
485 if outdir.endswith('.zip') or outdir.endswith('.jar'):
486 raise Exception(
487 "If 'android_java8_libs' is specified the output must be a directory"
488 )
Søren Gjesse80e00482021-04-06 13:17:35 +0200489
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200490 jar = None
491 main = None
492 if options.hash:
493 jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
494 main = 'com.android.tools.r8.R8'
Søren Gjesse80e00482021-04-06 13:17:35 +0200495
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200496 # Determine the l8 tool.
497 assert (options.compiler_build in ['full', 'lib'])
498 lib_prefix = 'r8lib-' if options.compiler_build == 'lib' else ''
499 tool = lib_prefix + 'l8'
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200500
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200501 # Prepare out directory.
502 android_java8_libs_output = os.path.join(temp, 'android_java8_libs')
503 os.makedirs(android_java8_libs_output)
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200504
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200505 # Prepare arguments for L8.
506 args = [
507 '--desugared-lib',
508 android_java8_libs['config'],
509 '--lib',
510 android_java8_libs['library'],
511 '--output',
512 android_java8_libs_output,
513 '--pg-conf',
514 desugared_lib_pg_conf,
515 '--release',
516 ]
517 if 'pgconf' in android_java8_libs:
518 for pgconf in android_java8_libs['pgconf']:
519 args.extend(['--pg-conf', pgconf])
520 args.extend(android_java8_libs['program'])
Christoffer Quist Adamsen7ff14092021-04-15 11:48:12 +0200521
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200522 # Run L8.
523 exit_code = toolhelper.run(tool,
524 args,
525 build=should_build(options),
Rico Wind2a90bde2024-11-26 07:59:20 +0100526 disable_assertions=options.disable_assertions,
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200527 quiet=quiet,
528 jar=jar,
529 main=main)
Søren Gjesse80e00482021-04-06 13:17:35 +0200530
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200531 # Copy the desugared library DEX to the output.
532 dex_file_name = ('classes' +
533 str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex')
534 shutil.copyfile(os.path.join(android_java8_libs_output, 'classes.dex'),
535 os.path.join(outdir, dex_file_name))
536
537
538def run_with_options(options,
539 args,
540 extra_args=None,
541 stdout=None,
542 quiet=False,
543 worker_id=None):
544 if extra_args is None:
545 extra_args = []
546 app_provided_pg_conf = False
547 # todo(121018500): remove when memory is under control
548 if not any('-Xmx' in arg for arg in extra_args):
549 if options.max_memory:
550 extra_args.append('-Xmx%sM' % options.max_memory)
551 else:
552 extra_args.append('-Xmx8G')
553 if not options.ignore_java_version:
554 utils.check_java_version()
555
556 if options.print_times:
557 extra_args.append('-Dcom.android.tools.r8.printtimes=1')
558
559 if not options.disable_assertions:
560 extra_args.append('-Dcom.android.tools.r8.enableTestAssertions=1')
561
562 outdir = options.out
563 (version_id, data) = get_version_and_data(options)
564
565 if options.compiler not in COMPILERS:
566 raise Exception("You need to specify '--compiler={}'".format(
567 '|'.join(COMPILERS)))
568
569 if options.compiler_build not in COMPILER_BUILDS:
570 raise Exception("You need to specify '--compiler-build={}'".format(
571 '|'.join(COMPILER_BUILDS)))
572
573 if not version_id in data.VERSIONS.keys():
574 print('No version {} for application {}'.format(version_id,
575 options.app))
576 print('Valid versions are {}'.format(data.VERSIONS.keys()))
577 return 1
578
579 version = data.VERSIONS[version_id]
580
581 type = get_type(options)
582
583 if type not in version:
584 print('No type {} for version {}'.format(type, version))
585 print('Valid types are {}'.format(version.keys()))
586 return 1
587 values = version[type]
588
589 args.extend(['--output', outdir])
590 if 'min-api' in values:
591 args.extend(['--min-api', values['min-api']])
592
593 if 'main-dex-list' in values:
594 args.extend(['--main-dex-list', values['main-dex-list']])
595
596 inputs = values['inputs']
597 libraries = values['libraries'] if 'libraries' in values else []
598
599 if options.compiler == 'r8':
600 if 'pgconf' in values and not options.k:
601 sanitized_lib_path = os.path.join(os.path.abspath(outdir),
602 'sanitized_lib.jar')
603 if has_injars_and_libraryjars(values['pgconf']):
604 sanitized_pgconf_path = os.path.join(os.path.abspath(outdir),
605 'sanitized.config')
606 SanitizeLibrariesInPgconf(sanitized_lib_path,
607 sanitized_pgconf_path,
608 values['pgconf'])
609 libraries = [sanitized_lib_path]
610 args.extend(['--pg-conf', sanitized_pgconf_path])
611 inputs = []
612 else:
613 # -injars without -libraryjars or vice versa is not supported.
614 check_no_injars_and_no_libraryjars(values['pgconf'])
615 for pgconf in values['pgconf']:
616 args.extend(['--pg-conf', pgconf])
617 if 'sanitize_libraries' in values and values[
618 'sanitize_libraries']:
619 SanitizeLibraries(sanitized_lib_path, values['libraries'],
620 values['inputs'])
621 libraries = [sanitized_lib_path]
622 app_provided_pg_conf = True
623 if 'pgconf_extra' in values:
624 extra_conf = os.path.join(os.path.abspath(outdir),
625 'pgconf_extra')
626 with open(extra_conf, 'w') as extra_f:
627 extra_f.write(values['pgconf_extra'])
628 args.extend(['--pg-conf', extra_conf])
629 if options.k:
630 args.extend(['--pg-conf', options.k])
631 if 'maindexrules' in values:
632 for rules in values['maindexrules']:
633 args.extend(['--main-dex-rules', rules])
634 if 'allow-type-errors' in values:
635 extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
636 extra_args.append(
637 '-Dcom.android.tools.r8.disallowClassInlinerGracefulExit=1')
638 if 'system-properties' in values:
639 for system_property in values['system-properties']:
640 extra_args.append(system_property)
641
642 if options.debug_agent:
643 if not options.compiler_build == 'full':
644 print(
645 'WARNING: Running debugging agent on r8lib is questionable...')
646 extra_args.append(
647 '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'
648 )
649
650 if not options.no_libraries:
651 for lib in libraries:
652 args.extend(['--lib', lib])
653
654 if not outdir.endswith('.zip') and not outdir.endswith('.jar') \
655 and not os.path.exists(outdir):
656 os.makedirs(outdir)
657
658 if options.hash:
659 # Download r8-<hash>.jar from
660 # https://storage.googleapis.com/r8-releases/raw/<hash>/.
661 download_path = archive.GetUploadDestination(options.hash, 'r8.jar',
662 True)
663 assert utils.file_exists_on_cloud_storage(download_path), (
664 'Could not find r8.jar file from provided hash: %s' % options.hash)
665 destination = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
666 utils.download_file_from_cloud_storage(download_path,
667 destination,
668 quiet=quiet)
669
670 # Additional flags for the compiler from the configuration file.
671 if 'flags' in values:
672 args.extend(values['flags'].split(' '))
673 if options.compiler == 'r8':
674 if 'r8-flags' in values:
675 args.extend(values['r8-flags'].split(' '))
676
677 # Additional flags for the compiler from the command line.
678 if options.compiler_flags:
679 args.extend(options.compiler_flags.split(' '))
680 if options.r8_flags:
681 args.extend(options.r8_flags.split(' '))
682
683 # Feature jars.
684 features = values['features'] if 'features' in values else []
685 for i, feature in enumerate(features, start=1):
686 feature_out = os.path.join(outdir, 'feature-%d.zip' % i)
687 for feature_jar in feature['inputs']:
688 args.extend(['--feature', feature_jar, feature_out])
689
690 args.extend(inputs)
691
692 t0 = None
693 if options.dump_args_file:
694 with open(options.dump_args_file, 'w') as args_file:
695 args_file.writelines([arg + os.linesep for arg in args])
Morten Krogh-Jespersen322c2f12019-10-08 10:41:21 +0200696 else:
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200697 with utils.TempDir() as temp:
698 if options.print_memoryuse and not options.track_memory_to_file:
699 options.track_memory_to_file = os.path.join(
700 temp, utils.MEMORY_USE_TMP_FILE)
701 if options.compiler == 'r8' and app_provided_pg_conf:
702 # Ensure that output of -printmapping and -printseeds go to the output
703 # location and not where the app Proguard configuration places them.
704 if outdir.endswith('.zip') or outdir.endswith('.jar'):
705 pg_outdir = os.path.dirname(outdir)
706 else:
707 pg_outdir = outdir
708 if not options.no_extra_pgconf:
709 additional_pg_conf = GenerateAdditionalProguardConfiguration(
710 temp, os.path.abspath(pg_outdir))
711 args.extend(['--pg-conf', additional_pg_conf])
Rico Windafdbbfd2019-02-22 09:32:07 +0100712
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200713 android_java8_libs = values.get('android_java8_libs')
714 if android_java8_libs:
715 desugared_lib_pg_conf = os.path.join(
716 temp, 'desugared-lib-pg-conf.txt')
717 args.extend(['--desugared-lib', android_java8_libs['config']])
718 args.extend(
719 ['--desugared-lib-pg-conf-output', desugared_lib_pg_conf])
Søren Gjesse04a94332020-01-27 15:35:42 +0100720
Christoffer Adamsen4c77afa2024-04-11 07:05:28 +0000721 stdout_path = os.path.join(temp, 'stdout')
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200722 stderr_path = os.path.join(temp, 'stderr')
Christoffer Adamsen4c77afa2024-04-11 07:05:28 +0000723 with open(stdout_path, 'w') as stdout_workers:
724 with open(stderr_path, 'w') as stderr:
725 jar = None
726 main = None
727 if options.compiler_build == 'full':
728 tool = options.compiler
729 else:
730 assert (options.compiler_build == 'lib')
731 tool = 'r8lib-' + options.compiler
732 if options.hash:
733 jar = os.path.join(utils.LIBS,
734 'r8-' + options.hash + '.jar')
735 main = 'com.android.tools.r8.' + options.compiler.upper()
736 if should_build(options):
737 gradle.RunGradle([
738 utils.GRADLE_TASK_R8LIB
739 if tool.startswith('r8lib') else utils.GRADLE_TASK_R8
740 ])
741 t0 = time.time()
742 exit_code = toolhelper.run(
743 tool,
744 args,
745 build=False,
Rico Wind2a90bde2024-11-26 07:59:20 +0100746 disable_assertions=options.disable_assertions,
Christoffer Adamsen4c77afa2024-04-11 07:05:28 +0000747 profile=options.profile,
748 track_memory_file=options.track_memory_to_file,
749 extra_args=extra_args,
750 stdout=stdout if worker_id is None else stdout_workers,
751 stderr=stderr,
752 timeout=options.timeout,
753 quiet=quiet,
754 cmd_prefix=['taskset', '-c', options.cpu_list]
755 if options.cpu_list else [],
756 jar=jar,
757 main=main,
758 worker_id=worker_id)
759 if worker_id is not None:
760 with open(stdout_path) as stdout:
761 stdout_text = stdout.read()
762 print_thread(stdout_text, worker_id)
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200763 if exit_code != 0:
764 with open(stderr_path) as stderr:
765 stderr_text = stderr.read()
766 if not quiet:
Christoffer Adamsen4c77afa2024-04-11 07:05:28 +0000767 print_thread(stderr_text, worker_id)
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200768 if 'java.lang.OutOfMemoryError' in stderr_text:
769 if not quiet:
770 print('Failure was OOM')
771 return OOM_EXIT_CODE
772 return exit_code
Morten Krogh-Jespersena565f012021-07-13 13:27:41 +0200773
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200774 if options.print_memoryuse:
775 print('{}(MemoryUse): {}'.format(
776 options.print_memoryuse,
777 utils.grep_memoryuse(options.track_memory_to_file)))
Morten Krogh-Jespersen0981b722019-10-09 10:00:33 +0200778
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200779 if android_java8_libs:
780 build_desugared_library_dex(options, quiet, temp,
781 android_java8_libs,
782 desugared_lib_pg_conf, inputs,
783 outdir)
Mads Ager418d1ca2017-05-22 09:35:49 +0200784
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200785 if options.print_runtimeraw:
786 print('{}(RunTimeRaw): {} ms'.format(options.print_runtimeraw,
787 1000.0 * (time.time() - t0)))
Tamas Kenez63a51d02019-01-07 15:53:02 +0100788
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200789 if options.print_dexsegments:
790 dex_files = glob(os.path.join(outdir, '*.dex'))
791 utils.print_dexsegments(options.print_dexsegments, dex_files)
792 print('{}-Total(CodeSize): {}'.format(
Morten Krogh-Jespersen254805e2022-06-03 09:32:42 +0200793 options.print_dexsegments, compute_size_of_dex_files(dex_files)))
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200794 return 0
795
Tamas Kenez02bff032017-07-18 12:13:58 +0200796
Morten Krogh-Jespersenf4a8f762021-12-22 12:31:21 +0100797def compute_size_of_dex_files(dex_files):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200798 dex_size = 0
799 for dex_file in dex_files:
800 dex_size += os.path.getsize(dex_file)
801 return dex_size
802
Morten Krogh-Jespersenf4a8f762021-12-22 12:31:21 +0100803
Mads Ager418d1ca2017-05-22 09:35:49 +0200804if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200805 sys.exit(main(sys.argv[1:]))