blob: 8684f89457a62cb06a4ac5c268f868bdbf55ed17 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
from __future__ import print_function
from glob import glob
import argparse
import copy
import os
import shutil
import sys
import time
import archive
import gradle
import gmail_data
import nest_data
from sanitize_libraries import SanitizeLibraries, SanitizeLibrariesInPgconf
import thread_utils
from thread_utils import print_thread
import toolhelper
import update_prebuilds_in_android
import utils
import youtube_data
import chrome_data
TYPES = ['dex', 'deploy', 'proguarded']
APPS = ['nest', 'youtube', 'gmail', 'chrome']
COMPILERS = ['d8', 'r8']
COMPILER_BUILDS = ['full', 'lib']
# We use this magic exit code to signal that the program OOM'ed
OOM_EXIT_CODE = 42
# According to Popen.returncode doc:
# A negative value -N indicates that the child was terminated by signal N.
TIMEOUT_KILL_CODE = -9
# Log file names
FIND_MIN_XMX_FILE = 'find_min_xmx_results'
FIND_MIN_XMX_DIR = 'find_min_xmx'
def ParseOptions(argv):
result = argparse.ArgumentParser()
result.add_argument('--compiler',
help='The compiler to use',
choices=COMPILERS)
result.add_argument('--compiler-build',
help='Compiler build to use',
choices=COMPILER_BUILDS,
default='lib')
result.add_argument('--no-fail-fast',
help='Whether run_on_app.py should report all failures '
'and not just the first one',
default=False,
action='store_true')
result.add_argument('--hash', help='The version of D8/R8 to use')
result.add_argument('--app', help='What app to run on', choices=APPS)
result.add_argument('--run-all',
help='Compile all possible combinations',
default=False,
action='store_true')
result.add_argument('--expect-oom',
help='Expect that compilation will fail with an OOM',
default=False,
action='store_true')
result.add_argument('--type',
help='Default for R8: deploy, for D8: proguarded',
choices=TYPES)
result.add_argument('--out',
help='Where to place the output',
default=utils.BUILD)
result.add_argument('--no-build',
help='Run without building first',
default=False,
action='store_true')
result.add_argument('--max-memory',
help='The maximum memory in MB to run with',
type=int)
result.add_argument('--find-min-xmx',
help='Find the minimum amount of memory we can run in',
default=False,
action='store_true')
result.add_argument('--find-min-xmx-min-memory',
help='Setting the minimum memory baseline to run in',
type=int)
result.add_argument('--find-min-xmx-max-memory',
help='Setting the maximum memory baseline to run in',
type=int)
result.add_argument('--find-min-xmx-range-size',
help='Setting the size of the acceptable memory range',
type=int,
default=32)
result.add_argument('--find-min-xmx-archive',
help='Archive find-min-xmx results on GCS',
default=False,
action='store_true')
result.add_argument('--no-extra-pgconf',
'--no_extra_pgconf',
help='Build without the following extra rules: ' +
'-printconfiguration, -printmapping, -printseeds, ' +
'-printusage',
default=False,
action='store_true')
result.add_argument('--timeout',
type=int,
default=0,
help='Set timeout instead of waiting for OOM.')
result.add_argument('--ignore-java-version',
help='Do not check java version',
default=False,
action='store_true')
result.add_argument(
'--no-libraries',
help='Do not pass in libraries, even if they exist in conf',
default=False,
action='store_true')
result.add_argument(
'--disable-assertions',
'--disable_assertions',
'-da',
help='Disable Java assertions when running the compiler '
'(default enabled)',
default=False,
action='store_true')
result.add_argument('--debug-agent',
help='Run with debug agent.',
default=False,
action='store_true')
result.add_argument('--version', help='The version of the app to run')
result.add_argument('-k', help='Override the default ProGuard keep rules')
result.add_argument('--compiler-flags',
help='Additional option(s) for the compiler. ' +
'If passing several options use a quoted string.')
result.add_argument('--r8-flags',
help='Additional option(s) for the compiler. ' +
'Same as --compiler-flags, keeping it for backward'
' compatibility. ' +
'If passing several options use a quoted string.')
result.add_argument('--track-memory-to-file',
help='Track how much memory the jvm is using while ' +
' compiling. Output to the specified file.')
result.add_argument('--profile',
help='Profile R8 run.',
default=False,
action='store_true')
result.add_argument(
'--dump-args-file',
help='Dump a file with the arguments for the specified ' +
'configuration. For use as a @<file> argument to perform ' + 'the run.')
result.add_argument('--print-runtimeraw',
metavar='BENCHMARKNAME',
help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
' <elapsed> ms\' at the end where <elapsed> is' +
' the elapsed time in milliseconds.')
result.add_argument('--print-memoryuse',
metavar='BENCHMARKNAME',
help='Print the line \'<BENCHMARKNAME>(MemoryUse):' +
' <mem>\' at the end where <mem> is the peak' +
' peak resident set size (VmHWM) in bytes.')
result.add_argument('--print-dexsegments',
metavar='BENCHMARKNAME',
help='Print the sizes of individual dex segments as ' +
'\'<BENCHMARKNAME>-<segment>(CodeSize): <bytes>\'')
result.add_argument(
'--track-time-in-memory',
help='Plot the times taken from memory starting point to '
'end-point with defined memory increment',
default=False,
action='store_true')
result.add_argument('--track-time-in-memory-max',
help='Setting the maximum memory baseline to run in',
type=int)
result.add_argument('--track-time-in-memory-min',
help='Setting the minimum memory baseline to run in',
type=int)
result.add_argument('--track-time-in-memory-increment',
help='Setting the increment',
type=int,
default=32)
result.add_argument('--print-times',
help='Include timing',
default=False,
action='store_true')
result.add_argument('--cpu-list',
help='Run under \'taskset\' with these CPUs. See '
'the \'taskset\' -c option for the format')
result.add_argument('--quiet',
help='Disable compiler logging',
default=False,
action='store_true')
result.add_argument('--workers',
help='Number of workers to use',
default=1,
type=int)
(options, args) = result.parse_known_args(argv)
assert not options.hash or options.no_build, (
'Argument --no-build is required when using --hash')
assert not options.hash or options.compiler_build == 'full', (
'Compiler build lib not yet supported with --hash')
return (options, args)
# Most apps have -printmapping, -printseeds, -printusage and
# -printconfiguration in the Proguard configuration. However we don't
# want to write these files in the locations specified.
# Instead generate an auxiliary Proguard configuration placing these
# output files together with the dex output.
def GenerateAdditionalProguardConfiguration(temp, outdir):
name = "output.config"
with open(os.path.join(temp, name), 'w') as f:
f.write('-printmapping ' + os.path.join(outdir, 'proguard.map') + "\n")
f.write('-printseeds ' + os.path.join(outdir, 'proguard.seeds') + "\n")
f.write('-printusage ' + os.path.join(outdir, 'proguard.usage') + "\n")
f.write('-printconfiguration ' +
os.path.join(outdir, 'proguard.config') + "\n")
return os.path.abspath(f.name)
# Please add bug number for disabled permutations and please explicitly
# do Bug: #BUG in the commit message of disabling to ensure re-enabling
DISABLED_PERMUTATIONS = [
# (app, version, type), e.g., ('gmail', '180826.15', 'deploy')
]
def get_permutations():
data_providers = {
'nest': nest_data,
'youtube': youtube_data,
'chrome': chrome_data,
'gmail': gmail_data,
}
# Check to ensure that we add all variants here.
assert len(APPS) == len(data_providers)
for app, data in data_providers.items():
for version in data.VERSIONS:
for type in data.VERSIONS[version]:
if (app, version, type) not in DISABLED_PERMUTATIONS:
# Only run with R8 lib to reduce cycle times.
for use_r8lib in [True]:
yield app, version, type, use_r8lib
def run_all(options, args):
# Build first so that each job won't.
if should_build(options):
gradle.RunGradle([utils.GRADLE_TASK_R8LIB])
options.no_build = True
assert not should_build(options)
# Args will be destroyed
assert len(args) == 0
jobs = []
for name, version, type, use_r8lib in get_permutations():
compiler = 'r8' if type == 'deploy' else 'd8'
compiler_build = 'lib' if use_r8lib else 'full'
fixed_options = copy.copy(options)
fixed_options.app = name
fixed_options.version = version
fixed_options.compiler = compiler
fixed_options.compiler_build = compiler_build
fixed_options.type = type
jobs.append(
create_job(compiler, compiler_build, name, fixed_options, type,
version))
exit_code = thread_utils.run_in_parallel(
jobs,
number_of_workers=options.workers,
stop_on_first_failure=not options.no_fail_fast)
exit(exit_code)
def create_job(compiler, compiler_build, name, options, type, version):
return lambda worker_id: run_job(compiler, compiler_build, name, options,
type, version, worker_id)
def run_job(compiler, compiler_build, name, options, type, version, worker_id):
print_thread(
'Executing %s/%s with %s %s %s' %
(compiler, compiler_build, name, version, type), worker_id)
if worker_id is not None:
options.out = os.path.join(options.out, str(worker_id))
os.makedirs(options.out, exist_ok=True)
exit_code = run_with_options(options, [], worker_id=worker_id)
if exit_code:
print_thread(
'Failed %s %s %s with %s/%s' %
(name, version, type, compiler, compiler_build), worker_id)
return exit_code
def find_min_xmx(options, args):
# Args will be destroyed
assert len(args) == 0
# If we can run in 128 MB then we are good (which we can for small examples
# or D8 on medium sized examples)
if options.find_min_xmx_min_memory:
not_working = options.find_min_xmx_min_memory
elif options.compiler == 'd8':
not_working = 128
else:
not_working = 1024
if options.find_min_xmx_max_memory:
working = options.find_min_xmx_max_memory
else:
working = 1024 * 8
exit_code = 0
range = int(options.find_min_xmx_range_size)
while working - not_working > range:
next_candidate = int(working - ((working - not_working) / 2))
print('working: %s, non_working: %s, next_candidate: %s' %
(working, not_working, next_candidate))
extra_args = ['-Xmx%sM' % next_candidate]
t0 = time.time()
exit_code = run_with_options(options, [], extra_args)
t1 = time.time()
print('Running took: %s ms' % (1000.0 * (t1 - t0)))
if exit_code != 0:
if exit_code not in [OOM_EXIT_CODE, TIMEOUT_KILL_CODE]:
print('Non OOM/Timeout error executing, exiting')
return 2
if exit_code == 0:
working = next_candidate
elif exit_code == TIMEOUT_KILL_CODE:
print('Timeout. Continue to the next candidate.')
not_working = next_candidate
else:
assert exit_code == OOM_EXIT_CODE
not_working = next_candidate
assert working - not_working <= range
found_range = 'Found range: %s - %s' % (not_working, working)
print(found_range)
if options.find_min_xmx_archive:
sha = utils.get_HEAD_sha1()
(version, _) = get_version_and_data(options)
destination = os.path.join(utils.R8_TEST_RESULTS_BUCKET,
FIND_MIN_XMX_DIR, sha, options.compiler,
options.compiler_build, options.app, version,
get_type(options))
gs_destination = 'gs://%s' % destination
utils.archive_value(FIND_MIN_XMX_FILE, gs_destination,
found_range + '\n')
return 0
def print_min_xmx_ranges_for_hash(hash, compiler, compiler_build):
app_directory = os.path.join(utils.R8_TEST_RESULTS_BUCKET, FIND_MIN_XMX_DIR,
hash, compiler, compiler_build)
gs_base = 'gs://%s' % app_directory
for app in utils.ls_files_on_cloud_storage(gs_base).strip().split('\n'):
for version in utils.ls_files_on_cloud_storage(app).strip().split('\n'):
for type in utils.ls_files_on_cloud_storage(version).strip().split(
'\n'):
gs_location = '%s%s' % (type, FIND_MIN_XMX_FILE)
value = utils.cat_file_on_cloud_storage(gs_location,
ignore_errors=True)
print('%s\n' % value)
def track_time_in_memory(options, args):
# Args will be destroyed
assert len(args) == 0
if not options.track_time_in_memory_min:
raise Exception(
'You have to specify --track_time_in_memory_min when running with '
'--track-time-in-memory')
if not options.track_time_in_memory_max:
raise Exception(
'You have to specify --track_time_in_memory_max when running with '
'--track-time-in-memory')
if not options.track_time_in_memory_increment:
raise Exception(
'You have to specify --track_time_in_memory_increment when running '
'with --track-time-in-memory')
current = options.track_time_in_memory_min
print('Memory (KB)\tTime (ms)')
with utils.TempDir() as temp:
stdout = os.path.join(temp, 'stdout')
stdout_fd = open(stdout, 'w')
while current <= options.track_time_in_memory_max:
extra_args = ['-Xmx%sM' % current]
t0 = time.time()
exit_code = run_with_options(options, [],
extra_args,
stdout_fd,
quiet=True)
t1 = time.time()
total = (1000.0 * (t1 - t0)) if exit_code == 0 else -1
print('%s\t%s' % (current, total))
current += options.track_time_in_memory_increment
return 0
def main(argv):
(options, args) = ParseOptions(argv)
if options.expect_oom and not options.max_memory:
raise Exception(
'You should only use --expect-oom if also specifying --max-memory')
if options.expect_oom and options.timeout:
raise Exception(
'You should not use --timeout when also specifying --expect-oom')
if options.find_min_xmx and options.track_time_in_memory:
raise Exception(
'You cannot both find the min xmx and track time at the same time')
if options.run_all:
return run_all(options, args)
if options.find_min_xmx:
return find_min_xmx(options, args)
if options.track_time_in_memory:
return track_time_in_memory(options, args)
exit_code = run_with_options(options, args, quiet=options.quiet)
if options.expect_oom:
exit_code = 0 if exit_code == OOM_EXIT_CODE else 1
return exit_code
def get_version_and_data(options):
if options.app == 'nest':
version = options.version or '20180926'
data = nest_data
elif options.app == 'youtube':
version = options.version or youtube_data.LATEST_VERSION
data = youtube_data
elif options.app == 'chrome':
version = options.version or '180917'
data = chrome_data
elif options.app == 'gmail':
version = options.version or '170604.16'
data = gmail_data
else:
raise Exception("You need to specify '--app={}'".format('|'.join(APPS)))
return version, data
def get_type(options):
if not options.type:
return 'deploy' if options.compiler == 'r8' else 'proguarded'
return options.type
def has_injars_and_libraryjars(pgconfs):
# Check if there are -injars and -libraryjars in the configuration.
has_injars = False
has_libraryjars = False
for pgconf in pgconfs:
pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
with open(pgconf) as pgconf_file:
for line in pgconf_file:
trimmed = line.strip()
if trimmed.startswith('-injars'):
has_injars = True
elif trimmed.startswith('-libraryjars'):
has_libraryjars = True
if has_injars and has_libraryjars:
return True
return False
def check_no_injars_and_no_libraryjars(pgconfs):
# Ensure that there are no -injars or -libraryjars in the configuration.
for pgconf in pgconfs:
pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
with open(pgconf) as pgconf_file:
for line in pgconf_file:
trimmed = line.strip()
if trimmed.startswith('-injars'):
raise Exception("Unexpected -injars found in " + pgconf)
elif trimmed.startswith('-libraryjars'):
raise Exception("Unexpected -libraryjars found in " +
pgconf)
def should_build(options):
return not options.no_build
def build_desugared_library_dex(options, quiet, temp, android_java8_libs,
desugared_lib_pg_conf, inputs, outdir):
if not inputs:
raise Exception(
"If 'android_java8_libs' is specified the inputs must be explicit" +
"(not defined using '-injars' in Proguard configuration files)")
if outdir.endswith('.zip') or outdir.endswith('.jar'):
raise Exception(
"If 'android_java8_libs' is specified the output must be a directory"
)
jar = None
main = None
if options.hash:
jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
main = 'com.android.tools.r8.R8'
# Determine the l8 tool.
assert (options.compiler_build in ['full', 'lib'])
lib_prefix = 'r8lib-' if options.compiler_build == 'lib' else ''
tool = lib_prefix + 'l8'
# Prepare out directory.
android_java8_libs_output = os.path.join(temp, 'android_java8_libs')
os.makedirs(android_java8_libs_output)
# Prepare arguments for L8.
args = [
'--desugared-lib',
android_java8_libs['config'],
'--lib',
android_java8_libs['library'],
'--output',
android_java8_libs_output,
'--pg-conf',
desugared_lib_pg_conf,
'--release',
]
if 'pgconf' in android_java8_libs:
for pgconf in android_java8_libs['pgconf']:
args.extend(['--pg-conf', pgconf])
args.extend(android_java8_libs['program'])
# Run L8.
exit_code = toolhelper.run(tool,
args,
build=should_build(options),
debug=not options.disable_assertions,
quiet=quiet,
jar=jar,
main=main)
# Copy the desugared library DEX to the output.
dex_file_name = ('classes' +
str(len(glob(os.path.join(outdir, '*.dex'))) + 1) + '.dex')
shutil.copyfile(os.path.join(android_java8_libs_output, 'classes.dex'),
os.path.join(outdir, dex_file_name))
def run_with_options(options,
args,
extra_args=None,
stdout=None,
quiet=False,
worker_id=None):
if extra_args is None:
extra_args = []
app_provided_pg_conf = False
# todo(121018500): remove when memory is under control
if not any('-Xmx' in arg for arg in extra_args):
if options.max_memory:
extra_args.append('-Xmx%sM' % options.max_memory)
else:
extra_args.append('-Xmx8G')
if not options.ignore_java_version:
utils.check_java_version()
if options.print_times:
extra_args.append('-Dcom.android.tools.r8.printtimes=1')
if not options.disable_assertions:
extra_args.append('-Dcom.android.tools.r8.enableTestAssertions=1')
outdir = options.out
(version_id, data) = get_version_and_data(options)
if options.compiler not in COMPILERS:
raise Exception("You need to specify '--compiler={}'".format(
'|'.join(COMPILERS)))
if options.compiler_build not in COMPILER_BUILDS:
raise Exception("You need to specify '--compiler-build={}'".format(
'|'.join(COMPILER_BUILDS)))
if not version_id in data.VERSIONS.keys():
print('No version {} for application {}'.format(version_id,
options.app))
print('Valid versions are {}'.format(data.VERSIONS.keys()))
return 1
version = data.VERSIONS[version_id]
type = get_type(options)
if type not in version:
print('No type {} for version {}'.format(type, version))
print('Valid types are {}'.format(version.keys()))
return 1
values = version[type]
args.extend(['--output', outdir])
if 'min-api' in values:
args.extend(['--min-api', values['min-api']])
if 'main-dex-list' in values:
args.extend(['--main-dex-list', values['main-dex-list']])
inputs = values['inputs']
libraries = values['libraries'] if 'libraries' in values else []
if options.compiler == 'r8':
if 'pgconf' in values and not options.k:
sanitized_lib_path = os.path.join(os.path.abspath(outdir),
'sanitized_lib.jar')
if has_injars_and_libraryjars(values['pgconf']):
sanitized_pgconf_path = os.path.join(os.path.abspath(outdir),
'sanitized.config')
SanitizeLibrariesInPgconf(sanitized_lib_path,
sanitized_pgconf_path,
values['pgconf'])
libraries = [sanitized_lib_path]
args.extend(['--pg-conf', sanitized_pgconf_path])
inputs = []
else:
# -injars without -libraryjars or vice versa is not supported.
check_no_injars_and_no_libraryjars(values['pgconf'])
for pgconf in values['pgconf']:
args.extend(['--pg-conf', pgconf])
if 'sanitize_libraries' in values and values[
'sanitize_libraries']:
SanitizeLibraries(sanitized_lib_path, values['libraries'],
values['inputs'])
libraries = [sanitized_lib_path]
app_provided_pg_conf = True
if 'pgconf_extra' in values:
extra_conf = os.path.join(os.path.abspath(outdir),
'pgconf_extra')
with open(extra_conf, 'w') as extra_f:
extra_f.write(values['pgconf_extra'])
args.extend(['--pg-conf', extra_conf])
if options.k:
args.extend(['--pg-conf', options.k])
if 'maindexrules' in values:
for rules in values['maindexrules']:
args.extend(['--main-dex-rules', rules])
if 'allow-type-errors' in values:
extra_args.append('-Dcom.android.tools.r8.allowTypeErrors=1')
extra_args.append(
'-Dcom.android.tools.r8.disallowClassInlinerGracefulExit=1')
if 'system-properties' in values:
for system_property in values['system-properties']:
extra_args.append(system_property)
if options.debug_agent:
if not options.compiler_build == 'full':
print(
'WARNING: Running debugging agent on r8lib is questionable...')
extra_args.append(
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'
)
if not options.no_libraries:
for lib in libraries:
args.extend(['--lib', lib])
if not outdir.endswith('.zip') and not outdir.endswith('.jar') \
and not os.path.exists(outdir):
os.makedirs(outdir)
if options.hash:
# Download r8-<hash>.jar from
# https://storage.googleapis.com/r8-releases/raw/<hash>/.
download_path = archive.GetUploadDestination(options.hash, 'r8.jar',
True)
assert utils.file_exists_on_cloud_storage(download_path), (
'Could not find r8.jar file from provided hash: %s' % options.hash)
destination = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar')
utils.download_file_from_cloud_storage(download_path,
destination,
quiet=quiet)
# Additional flags for the compiler from the configuration file.
if 'flags' in values:
args.extend(values['flags'].split(' '))
if options.compiler == 'r8':
if 'r8-flags' in values:
args.extend(values['r8-flags'].split(' '))
# Additional flags for the compiler from the command line.
if options.compiler_flags:
args.extend(options.compiler_flags.split(' '))
if options.r8_flags:
args.extend(options.r8_flags.split(' '))
# Feature jars.
features = values['features'] if 'features' in values else []
for i, feature in enumerate(features, start=1):
feature_out = os.path.join(outdir, 'feature-%d.zip' % i)
for feature_jar in feature['inputs']:
args.extend(['--feature', feature_jar, feature_out])
args.extend(inputs)
t0 = None
if options.dump_args_file:
with open(options.dump_args_file, 'w') as args_file:
args_file.writelines([arg + os.linesep for arg in args])
else:
with utils.TempDir() as temp:
if options.print_memoryuse and not options.track_memory_to_file:
options.track_memory_to_file = os.path.join(
temp, utils.MEMORY_USE_TMP_FILE)
if options.compiler == 'r8' and app_provided_pg_conf:
# Ensure that output of -printmapping and -printseeds go to the output
# location and not where the app Proguard configuration places them.
if outdir.endswith('.zip') or outdir.endswith('.jar'):
pg_outdir = os.path.dirname(outdir)
else:
pg_outdir = outdir
if not options.no_extra_pgconf:
additional_pg_conf = GenerateAdditionalProguardConfiguration(
temp, os.path.abspath(pg_outdir))
args.extend(['--pg-conf', additional_pg_conf])
android_java8_libs = values.get('android_java8_libs')
if android_java8_libs:
desugared_lib_pg_conf = os.path.join(
temp, 'desugared-lib-pg-conf.txt')
args.extend(['--desugared-lib', android_java8_libs['config']])
args.extend(
['--desugared-lib-pg-conf-output', desugared_lib_pg_conf])
stderr_path = os.path.join(temp, 'stderr')
with open(stderr_path, 'w') as stderr:
jar = None
main = None
if options.compiler_build == 'full':
tool = options.compiler
else:
assert (options.compiler_build == 'lib')
tool = 'r8lib-' + options.compiler
if options.hash:
jar = os.path.join(utils.LIBS,
'r8-' + options.hash + '.jar')
main = 'com.android.tools.r8.' + options.compiler.upper()
if should_build(options):
gradle.RunGradle([
utils.GRADLE_TASK_R8LIB
if tool.startswith('r8lib') else UTILS.GRADLE_TASK_R8
])
t0 = time.time()
exit_code = toolhelper.run(
tool,
args,
build=False,
debug=not options.disable_assertions,
profile=options.profile,
track_memory_file=options.track_memory_to_file,
extra_args=extra_args,
stdout=stdout,
stderr=stderr,
timeout=options.timeout,
quiet=quiet,
cmd_prefix=['taskset', '-c', options.cpu_list]
if options.cpu_list else [],
jar=jar,
main=main,
worker_id=worker_id)
if exit_code != 0:
with open(stderr_path) as stderr:
stderr_text = stderr.read()
if not quiet:
print(stderr_text)
if 'java.lang.OutOfMemoryError' in stderr_text:
if not quiet:
print('Failure was OOM')
return OOM_EXIT_CODE
return exit_code
if options.print_memoryuse:
print('{}(MemoryUse): {}'.format(
options.print_memoryuse,
utils.grep_memoryuse(options.track_memory_to_file)))
if android_java8_libs:
build_desugared_library_dex(options, quiet, temp,
android_java8_libs,
desugared_lib_pg_conf, inputs,
outdir)
if options.print_runtimeraw:
print('{}(RunTimeRaw): {} ms'.format(options.print_runtimeraw,
1000.0 * (time.time() - t0)))
if options.print_dexsegments:
dex_files = glob(os.path.join(outdir, '*.dex'))
utils.print_dexsegments(options.print_dexsegments, dex_files)
print('{}-Total(CodeSize): {}'.format(
options.print_dexsegments, compute_size_of_dex_files(dex_files)))
return 0
def compute_size_of_dex_files(dex_files):
dex_size = 0
for dex_file in dex_files:
dex_size += os.path.getsize(dex_file)
return dex_size
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))