blob: 62a227578441c86134627c81ed967f74930e4483 [file] [log] [blame]
Søren Gjessecdae8792018-12-12 09:02:43 +01001#!/usr/bin/env python
2# Copyright (c) 2018, 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
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +01006import apk_masseur
Søren Gjessecdae8792018-12-12 09:02:43 +01007import apk_utils
Morten Krogh-Jespersenc2fbde22019-02-07 13:59:55 +01008import golem
Søren Gjesseeed839d2019-01-11 15:19:16 +01009import gradle
Ian Zerny3f54e222019-02-12 10:51:17 +010010import jdk
Søren Gjessecdae8792018-12-12 09:02:43 +010011import os
12import optparse
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +010013import shutil
Søren Gjessecdae8792018-12-12 09:02:43 +010014import subprocess
15import sys
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010016import time
Søren Gjessecdae8792018-12-12 09:02:43 +010017import utils
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +010018import zipfile
Søren Gjessecdae8792018-12-12 09:02:43 +010019
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010020import as_utils
21
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +010022SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +010023WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010024
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +010025if ('R8_BENCHMARK_DIR' in os.environ
26 and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010027 WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
Søren Gjessecdae8792018-12-12 09:02:43 +010028
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +010029# For running on Golem all APPS are bundled as an x20-dependency and then copied
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +010030# to WORKING_DIR. To update the app-bundle use 'run_on_as_app_x20_packager.py'.
Søren Gjessecdae8792018-12-12 09:02:43 +010031APPS = {
32 # 'app-name': {
33 # 'git_repo': ...
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010034 # 'revision': ...,
Søren Gjessecdae8792018-12-12 09:02:43 +010035 # 'app_module': ... (default app)
36 # 'archives_base_name': ... (default same as app_module)
37 # 'flavor': ... (default no flavor)
Søren Gjesse8c111482018-12-21 14:47:57 +010038 # 'releaseTarget': ... (default <app_module>:assemble<flavor>Release
Søren Gjessecdae8792018-12-12 09:02:43 +010039 # },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010040 'AnExplorer': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010041 'app_id': 'dev.dworks.apps.anexplorer.pro',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010042 'git_repo': 'https://github.com/christofferqa/AnExplorer',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010043 'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010044 'flavor': 'googleMobilePro',
45 'signed-apk-name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +010046 'min_sdk': 17
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010047 },
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010048 'AntennaPod': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010049 'app_id': 'de.danoeh.antennapod',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010050 'git_repo': 'https://github.com/christofferqa/AntennaPod.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010051 'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010052 'flavor': 'play',
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +010053 'min_sdk': 14,
54 'compile_sdk': 26
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010055 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010056 'apps-android-wikipedia': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010057 'app_id': 'org.wikipedia',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010058 'git_repo': 'https://github.com/christofferqa/apps-android-wikipedia',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010059 'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010060 'flavor': 'prod',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010061 'signed-apk-name': 'app-prod-universal-release.apk',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010062 },
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +010063 'chanu': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010064 'app_id': 'com.chanapps.four.activity',
65 'git_repo': 'https://github.com/mkj-gram/chanu.git',
66 'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +010067 },
Christoffer Quist Adamsen61b8c802018-12-20 08:18:40 +010068 'friendlyeats-android': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010069 'app_id': 'com.google.firebase.example.fireeats',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010070 'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git',
71 'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
Christoffer Quist Adamsen61b8c802018-12-20 08:18:40 +010072 },
Christoffer Quist Adamsena2d16cd2019-02-07 10:41:20 +010073 'Instabug-Android': {
74 'app_id': 'com.example.instabug',
75 'git_repo': 'https://github.com/christofferqa/Instabug-Android.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010076 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f'
Christoffer Quist Adamsena2d16cd2019-02-07 10:41:20 +010077 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010078 'KISS': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010079 'app_id': 'fr.neamar.kiss',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010080 'git_repo': 'https://github.com/christofferqa/KISS',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010081 'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010082 },
83 'materialistic': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010084 'app_id': 'io.github.hidroh.materialistic',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010085 'git_repo': 'https://github.com/christofferqa/materialistic',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010086 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010087 },
88 'Minimal-Todo': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010089 'app_id': 'com.avjindersinghsekhon.minimaltodo',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010090 'git_repo': 'https://github.com/christofferqa/Minimal-Todo',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010091 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010092 },
93 'NewPipe': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010094 'app_id': 'org.schabi.newpipe',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010095 'git_repo': 'https://github.com/christofferqa/NewPipe',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010096 'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010097 },
Morten Krogh-Jespersen4293dcd2019-01-28 11:38:50 +010098 'rover-android': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010099 'app_id': 'io.rover.app.debug',
100 'app_module': 'debug-app',
101 'git_repo': 'https://github.com/mkj-gram/rover-android.git',
102 'revision': 'd2e876e597b3af7eab406e38a0e08327a38bd942',
Morten Krogh-Jespersen4293dcd2019-01-28 11:38:50 +0100103 },
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +0100104 'Signal-Android': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100105 'app_id': 'org.thoughtcrime.securesms',
106 'app_module': '',
107 'flavor': 'play',
108 'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100109 'main_dex_rules': 'multidex-config.pro',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100110 'revision': '85e1a10993e5e9ffe923f0798b26cbc44068ba31',
111 'releaseTarget': 'assemblePlayRelease',
112 'signed-apk-name': 'Signal-play-release-4.32.7.apk',
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +0100113 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100114 'Simple-Calendar': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100115 'app_id': 'com.simplemobiletools.calendar.pro',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +0100116 'git_repo': 'https://github.com/christofferqa/Simple-Calendar',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100117 'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100118 'signed-apk-name': 'calendar-release.apk'
119 },
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100120 'sqldelight': {
121 'app_id': 'com.example.sqldelight.hockey',
122 'git_repo': 'https://github.com/christofferqa/sqldelight.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100123 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100124 'app_module': 'sample/android',
125 'archives_base_name': 'android',
126 'min_sdk': 14,
127 'compile_sdk': 28,
128 },
Søren Gjessecdae8792018-12-12 09:02:43 +0100129 'tachiyomi': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100130 'app_id': 'eu.kanade.tachiyomi',
Søren Gjessecdae8792018-12-12 09:02:43 +0100131 'git_repo': 'https://github.com/sgjesse/tachiyomi.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100132 'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
Søren Gjessecdae8792018-12-12 09:02:43 +0100133 'flavor': 'standard',
Søren Gjesse8c111482018-12-21 14:47:57 +0100134 'releaseTarget': 'app:assembleRelease',
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100135 'min_sdk': 16
Søren Gjessecdae8792018-12-12 09:02:43 +0100136 },
Søren Gjesse9fb48802019-01-18 11:00:00 +0100137 'tivi': {
138 'app_id': 'app.tivi',
Søren Gjesse9fb48802019-01-18 11:00:00 +0100139 'git_repo': 'https://github.com/sgjesse/tivi.git',
Søren Gjessedbe6ebc2019-02-14 09:38:47 +0100140 'revision': '25c52e3593e7c98da4e537b49b29f6f67f88754d',
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100141 'min_sdk': 23,
142 'compile_sdk': 28,
Søren Gjesse9fb48802019-01-18 11:00:00 +0100143 },
Morten Krogh-Jespersen580dc642019-01-25 10:18:22 +0100144 'Tusky': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100145 'app_id': 'com.keylesspalace.tusky',
146 'git_repo': 'https://github.com/mkj-gram/Tusky.git',
147 'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
148 'flavor': 'blue'
Morten Krogh-Jespersen580dc642019-01-25 10:18:22 +0100149 },
Morten Krogh-Jespersenaf6377d2019-01-29 11:18:31 +0100150 'Vungle-Android-SDK': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100151 'app_id': 'com.publisher.vungle.sample',
152 'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
153 'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
Morten Krogh-Jespersenaf6377d2019-01-29 11:18:31 +0100154 },
Søren Gjessecdae8792018-12-12 09:02:43 +0100155 # This does not build yet.
156 'muzei': {
157 'git_repo': 'https://github.com/sgjesse/muzei.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100158 'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
Søren Gjessecdae8792018-12-12 09:02:43 +0100159 'app_module': 'main',
160 'archives_base_name': 'muzei',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100161 'skip': True,
Søren Gjessecdae8792018-12-12 09:02:43 +0100162 },
163}
164
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100165# TODO(christofferqa): Do not rely on 'emulator-5554' name
166emulator_id = 'emulator-5554'
167
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100168def ComputeSizeOfDexFilesInApk(apk):
169 dex_size = 0
170 z = zipfile.ZipFile(apk, 'r')
171 for filename in z.namelist():
172 if filename.endswith('.dex'):
173 dex_size += z.getinfo(filename).file_size
174 return dex_size
175
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100176def IsBuiltWithR8(apk, temp_dir, options):
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100177 r8_jar = os.path.join(temp_dir, 'r8.jar')
178
179 # Use the copy of r8.jar if it is there.
180 if os.path.isfile(r8_jar):
Ian Zerny3f54e222019-02-12 10:51:17 +0100181 cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100182 else:
183 script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
184 cmd = ['python', script, apk]
185
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100186 utils.PrintCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100187 return '~~R8' in subprocess.check_output(cmd).strip()
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100188
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100189def IsMinifiedR8(shrinker):
190 return 'nolib' not in shrinker
191
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100192def IsTrackedByGit(file):
193 return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
194
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100195def GitClone(git_url, revision, checkout_dir, quiet):
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100196 result = subprocess.check_output(
197 ['git', 'clone', git_url, checkout_dir]).strip()
198 head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
199 if revision == head_rev:
200 return result
201 warn('Target revision is not head in {}.'.format(checkout_dir))
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100202 with utils.ChangedWorkingDirectory(checkout_dir, quiet=quiet):
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100203 subprocess.check_output(['git', 'reset', '--hard', revision])
204 return result
Søren Gjessecdae8792018-12-12 09:02:43 +0100205
206def GitCheckout(file):
207 return subprocess.check_output(['git', 'checkout', file]).strip()
208
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100209def InstallApkOnEmulator(apk_dest, options):
210 cmd = ['adb', '-s', emulator_id, 'install', '-r', '-d', apk_dest]
211 if options.quiet:
212 with open(os.devnull, 'w') as devnull:
213 subprocess.check_call(cmd, stdout=devnull)
214 else:
215 subprocess.check_call(cmd)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100216
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100217def PercentageDiffAsString(before, after):
218 if after < before:
219 return '-' + str(round((1.0 - after / before) * 100)) + '%'
220 else:
221 return '+' + str(round((after - before) / before * 100)) + '%'
222
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100223def UninstallApkOnEmulator(app, config, options):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100224 app_id = config.get('app_id')
225 process = subprocess.Popen(
Christoffer Quist Adamsen67608982019-02-08 10:09:35 +0100226 ['adb', '-s', emulator_id, 'uninstall', app_id],
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100227 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
228 stdout, stderr = process.communicate()
229
230 if stdout.strip() == 'Success':
231 # Successfully uninstalled
232 return
233
234 if 'Unknown package: {}'.format(app_id) in stderr:
235 # Application not installed
236 return
237
238 raise Exception(
239 'Unexpected result from `adb uninstall {}\nStdout: {}\nStderr: {}'.format(
240 app_id, stdout, stderr))
241
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100242def WaitForEmulator():
243 stdout = subprocess.check_output(['adb', 'devices'])
244 if '{}\tdevice'.format(emulator_id) in stdout:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100245 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100246
247 print('Emulator \'{}\' not connected; waiting for connection'.format(
248 emulator_id))
249
250 time_waited = 0
251 while True:
252 time.sleep(10)
253 time_waited += 10
254 stdout = subprocess.check_output(['adb', 'devices'])
255 if '{}\tdevice'.format(emulator_id) not in stdout:
256 print('... still waiting for connection')
257 if time_waited >= 5 * 60:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100258 return False
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100259 else:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100260 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100261
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100262def GetResultsForApp(app, config, options, temp_dir):
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100263 # Checkout and build in the build directory.
264 checkout_dir = os.path.join(WORKING_DIR, app)
265
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100266 result = {}
267
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100268 if not os.path.exists(checkout_dir) and not options.golem:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100269 with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100270 GitClone(
271 config['git_repo'], config['revision'], checkout_dir, options.quiet)
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100272
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100273 checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
274 if config['revision'] != checkout_rev:
275 msg = 'Checkout is not target revision for {} in {}.'.format(
276 app, checkout_dir)
277 if options.ignore_versions:
278 warn(msg)
279 else:
280 raise Exception(msg)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100281
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100282 result['status'] = 'success'
283
284 result_per_shrinker = BuildAppWithSelectedShrinkers(
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100285 app, config, options, checkout_dir, temp_dir)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100286 for shrinker, shrinker_result in result_per_shrinker.iteritems():
287 result[shrinker] = shrinker_result
288
289 return result
290
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100291def BuildAppWithSelectedShrinkers(app, config, options, checkout_dir, temp_dir):
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100292 result_per_shrinker = {}
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100293
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100294 with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100295 for shrinker in SHRINKERS:
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100296 if options.shrinker and shrinker not in options.shrinker:
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100297 continue
298
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100299 apk_dest = None
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100300
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100301 result = {}
302 try:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100303 out_dir = os.path.join(checkout_dir, 'out', shrinker)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100304 (apk_dest, profile_dest_dir, proguard_config_file) = \
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100305 BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100306 temp_dir, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100307 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100308 result['apk_dest'] = apk_dest
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100309 result['build_status'] = 'success'
310 result['dex_size'] = dex_size
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100311 result['profile_dest_dir'] = profile_dest_dir
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100312
313 profile = as_utils.ParseProfileReport(profile_dest_dir)
314 result['profile'] = {
315 task_name:duration for task_name, duration in profile.iteritems()
316 if as_utils.IsGradleCompilerTask(task_name, shrinker)}
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100317 except Exception as e:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100318 warn('Failed to build {} with {}'.format(app, shrinker))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100319 if e:
320 print('Error: ' + str(e))
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100321 result['build_status'] = 'failed'
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100322
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100323 if result.get('build_status') == 'success':
324 if options.monkey:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100325 result['monkey_status'] = 'success' if RunMonkey(
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100326 app, config, options, apk_dest) else 'failed'
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100327
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100328 if 'r8' in shrinker and options.r8_compilation_steps > 1:
329 recompilation_results = []
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100330
331 # Build app with gradle using -D...keepRuleSynthesisForRecompilation=
332 # true.
333 out_dir = os.path.join(checkout_dir, 'out', shrinker + '-1')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100334 (apk_dest, profile_dest_dir, ext_proguard_config_file) = \
335 BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100336 temp_dir, options, keepRuleSynthesisForRecompilation=True)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100337 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
338 recompilation_result = {
339 'apk_dest': apk_dest,
340 'build_status': 'success',
341 'dex_size': ComputeSizeOfDexFilesInApk(apk_dest),
342 'monkey_status': 'skipped'
343 }
344 recompilation_results.append(recompilation_result)
345
346 # Sanity check that keep rules have changed.
347 with open(ext_proguard_config_file) as new:
348 with open(proguard_config_file) as old:
Christoffer Quist Adamsend2bfcde2019-01-22 10:05:15 +0100349 assert(sum(1 for line in new) > sum(1 for line in old))
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100350
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100351 # Extract min-sdk and target-sdk
352 (min_sdk, compile_sdk) = as_utils.GetMinAndCompileSdk(app, config,
353 checkout_dir, apk_dest)
354
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100355 # Now rebuild generated apk.
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100356 previous_apk = apk_dest
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100357
358 # We may need main dex rules when re-compiling with R8 as standalone.
359 main_dex_rules = None
360 if config.get('main_dex_rules'):
361 main_dex_rules = os.path.join(
362 checkout_dir, config.get('main_dex_rules'))
363
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100364 for i in range(1, options.r8_compilation_steps):
365 try:
366 recompiled_apk_dest = os.path.join(
367 checkout_dir, 'out', shrinker, 'app-release-{}.apk'.format(i))
368 RebuildAppWithShrinker(
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100369 app, previous_apk, recompiled_apk_dest,
370 ext_proguard_config_file, shrinker, min_sdk, compile_sdk,
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100371 options, temp_dir, main_dex_rules)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100372 recompilation_result = {
373 'apk_dest': recompiled_apk_dest,
374 'build_status': 'success',
375 'dex_size': ComputeSizeOfDexFilesInApk(recompiled_apk_dest)
376 }
377 if options.monkey:
378 recompilation_result['monkey_status'] = 'success' if RunMonkey(
379 app, config, options, recompiled_apk_dest) else 'failed'
380 recompilation_results.append(recompilation_result)
381 previous_apk = recompiled_apk_dest
382 except Exception as e:
383 warn('Failed to recompile {} with {}'.format(app, shrinker))
384 recompilation_results.append({ 'build_status': 'failed' })
385 break
386 result['recompilation_results'] = recompilation_results
387
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100388 result_per_shrinker[shrinker] = result
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100389
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100390 if not options.app:
391 print('')
392 LogResultsForApp(app, result_per_shrinker, options)
393 print('')
394
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100395 return result_per_shrinker
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100396
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100397def BuildAppWithShrinker(
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100398 app, config, shrinker, checkout_dir, out_dir, temp_dir, options,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100399 keepRuleSynthesisForRecompilation=False):
400 print('Building {} with {}{}'.format(
401 app,
402 shrinker,
403 ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100404
Morten Krogh-Jespersende566ea2019-02-18 11:59:48 +0100405 # Add settings.gradle file if it is not present to prevent gradle from finding
406 # the settings.gradle file in the r8 root when apps are placed under
407 # $R8/build.
408 as_utils.add_settings_gradle(checkout_dir, app)
409
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100410 # Add 'r8.jar' from top-level build.gradle.
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100411 as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100412
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100413 app_module = config.get('app_module', 'app')
Morten Krogh-Jespersen47d62ba2019-01-22 08:35:49 +0100414 archives_base_name = config.get('archives_base_name', app_module)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100415 flavor = config.get('flavor')
416
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100417 if not os.path.exists(out_dir):
418 os.makedirs(out_dir)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100419
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100420 # Set -printconfiguration in Proguard rules.
421 proguard_config_dest = os.path.abspath(
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100422 os.path.join(out_dir, 'proguard-rules.pro'))
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100423 as_utils.SetPrintConfigurationDirective(
424 app, config, checkout_dir, proguard_config_dest)
425
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100426 env = {}
Morten Krogh-Jespersenc8efedd2019-01-28 11:36:17 +0100427 env['ANDROID_HOME'] = utils.ANDROID_HOME
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100428 env['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100429
Søren Gjesse8c111482018-12-21 14:47:57 +0100430 releaseTarget = config.get('releaseTarget')
431 if not releaseTarget:
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100432 releaseTarget = app_module.replace('/', ':') + ':' + 'assemble' + (
Søren Gjesse8c111482018-12-21 14:47:57 +0100433 flavor.capitalize() if flavor else '') + 'Release'
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100434
Søren Gjesse9fb48802019-01-18 11:00:00 +0100435 # Value for property android.enableR8.
436 enableR8 = 'r8' in shrinker
437 # Value for property android.enableR8.fullMode.
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100438 enableR8FullMode = shrinker == 'r8-full' or shrinker == 'r8-nolib-full'
Søren Gjesse9fb48802019-01-18 11:00:00 +0100439 # Build gradlew command line.
440 cmd = ['./gradlew', '--no-daemon', 'clean', releaseTarget,
441 '--profile', '--stacktrace',
442 '-Pandroid.enableR8=' + str(enableR8).lower(),
443 '-Pandroid.enableR8.fullMode=' + str(enableR8FullMode).lower()]
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100444 if keepRuleSynthesisForRecompilation:
445 cmd.append('-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100446 if options.gradle_flags:
447 cmd.extend(options.gradle_flags.split(' '))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100448
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100449 stdout = utils.RunCmd(cmd, env, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100450
451 apk_base_name = (archives_base_name
452 + (('-' + flavor) if flavor else '') + '-release')
453 signed_apk_name = config.get('signed-apk-name', apk_base_name + '.apk')
454 unsigned_apk_name = apk_base_name + '-unsigned.apk'
455
456 build_dir = config.get('build_dir', 'build')
457 build_output_apks = os.path.join(app_module, build_dir, 'outputs', 'apk')
458 if flavor:
459 build_output_apks = os.path.join(build_output_apks, flavor, 'release')
460 else:
461 build_output_apks = os.path.join(build_output_apks, 'release')
462
463 signed_apk = os.path.join(build_output_apks, signed_apk_name)
464 unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)
465
466 if options.sign_apks and not os.path.isfile(signed_apk):
467 assert os.path.isfile(unsigned_apk)
468 if options.sign_apks:
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100469 apk_utils.sign_with_apksigner(
Morten Krogh-Jespersenc8efedd2019-01-28 11:36:17 +0100470 utils.ANDROID_BUILD_TOOLS,
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100471 unsigned_apk,
472 signed_apk,
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100473 options.keystore,
474 options.keystore_password,
475 quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100476
477 if os.path.isfile(signed_apk):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100478 apk_dest = os.path.join(out_dir, signed_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100479 as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100480 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100481 apk_dest = os.path.join(out_dir, unsigned_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100482 as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100483
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100484 assert IsBuiltWithR8(apk_dest, temp_dir, options) == ('r8' in shrinker), (
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100485 'Unexpected marker in generated APK for {}'.format(shrinker))
486
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100487 profile_dest_dir = os.path.join(out_dir, 'profile')
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100488 as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100489
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100490 return (apk_dest, profile_dest_dir, proguard_config_dest)
491
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100492def RebuildAppWithShrinker(
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100493 app, apk, apk_dest, proguard_config_file, shrinker, min_sdk, compile_sdk,
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100494 options, temp_dir, main_dex_rules):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100495 assert 'r8' in shrinker
496 assert apk_dest.endswith('.apk')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100497
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100498 print('Rebuilding {} with {}'.format(app, shrinker))
499
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100500 # Compile given APK with shrinker to temporary zip file.
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100501 android_jar = utils.get_android_jar(compile_sdk)
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100502 r8_jar = os.path.join(
503 temp_dir, 'r8lib.jar' if IsMinifiedR8(shrinker) else 'r8.jar')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100504 zip_dest = apk_dest[:-4] + '.zip'
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100505
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100506 # TODO(christofferqa): Entry point should be CompatProguard if the shrinker
507 # is 'r8'.
508 entry_point = 'com.android.tools.r8.R8'
509
Ian Zerny3f54e222019-02-12 10:51:17 +0100510 cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar, entry_point,
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100511 '--release', '--min-api', str(min_sdk), '--pg-conf', proguard_config_file,
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100512 '--lib', android_jar, '--output', zip_dest, apk]
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100513
514 for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
515 cmd.append('--lib')
516 cmd.append(android_optional_jar)
517
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100518 if main_dex_rules:
519 cmd.append('--main-dex-rules')
520 cmd.append(main_dex_rules)
521
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100522 utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100523
524 # Make a copy of the given APK, move the newly generated dex files into the
525 # copied APK, and then sign the APK.
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100526 apk_masseur.masseur(
527 apk, dex=zip_dest, out=apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100528
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100529def RunMonkey(app, config, options, apk_dest):
530 if not WaitForEmulator():
531 return False
532
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100533 UninstallApkOnEmulator(app, config, options)
534 InstallApkOnEmulator(apk_dest, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100535
536 app_id = config.get('app_id')
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100537 number_of_events_to_generate = options.monkey_events
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100538
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100539 # Intentionally using a constant seed such that the monkey generates the same
540 # event sequence for each shrinker.
541 random_seed = 42
542
543 cmd = ['adb', 'shell', 'monkey', '-p', app_id, '-s', str(random_seed),
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100544 str(number_of_events_to_generate)]
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100545
546 try:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100547 stdout = utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100548 succeeded = (
549 'Events injected: {}'.format(number_of_events_to_generate) in stdout)
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100550 except subprocess.CalledProcessError as e:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100551 succeeded = False
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100552
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100553 UninstallApkOnEmulator(app, config, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100554
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100555 return succeeded
556
557def LogResultsForApps(result_per_shrinker_per_app, options):
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100558 print('')
559 for app, result_per_shrinker in sorted(
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100560 result_per_shrinker_per_app.iteritems(), key=lambda s: s[0].lower()):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100561 LogResultsForApp(app, result_per_shrinker, options)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100562
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100563def LogResultsForApp(app, result_per_shrinker, options):
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100564 if options.print_dexsegments:
565 LogSegmentsForApp(app, result_per_shrinker, options)
566 else:
567 LogComparisonResultsForApp(app, result_per_shrinker, options)
568
569def LogSegmentsForApp(app, result_per_shrinker, options):
570 for shrinker in SHRINKERS:
571 if shrinker not in result_per_shrinker:
572 continue
573 result = result_per_shrinker[shrinker];
574 benchmark_name = '{}-{}'.format(options.print_dexsegments, app)
575 utils.print_dexsegments(benchmark_name, [result.get('apk_dest')])
576 duration = sum(result.get('profile').values())
577 print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration * 1000))
578 print('%s-Total(CodeSize): %s' % (benchmark_name, result.get('dex_size')))
579
580
581def LogComparisonResultsForApp(app, result_per_shrinker, options):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100582 print(app + ':')
583
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100584 if result_per_shrinker.get('status', 'success') != 'success':
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100585 error_message = result_per_shrinker.get('error_message')
586 print(' skipped ({})'.format(error_message))
587 return
588
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100589 proguard_result = result_per_shrinker.get('pg', {})
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100590 proguard_dex_size = float(proguard_result.get('dex_size', -1))
591 proguard_duration = sum(proguard_result.get('profile', {}).values())
592
593 for shrinker in SHRINKERS:
594 if shrinker not in result_per_shrinker:
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100595 continue
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100596 result = result_per_shrinker.get(shrinker)
597 build_status = result.get('build_status')
598 if build_status != 'success':
599 warn(' {}: {}'.format(shrinker, build_status))
600 else:
601 print(' {}:'.format(shrinker))
602 dex_size = result.get('dex_size')
603 msg = ' dex size: {}'.format(dex_size)
604 if dex_size != proguard_dex_size and proguard_dex_size >= 0:
605 msg = '{} ({}, {})'.format(
606 msg, dex_size - proguard_dex_size,
607 PercentageDiffAsString(proguard_dex_size, dex_size))
608 success(msg) if dex_size < proguard_dex_size else warn(msg)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100609 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100610 print(msg)
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100611
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100612 profile = result.get('profile')
613 duration = sum(profile.values())
614 msg = ' performance: {}s'.format(duration)
615 if duration != proguard_duration and proguard_duration > 0:
616 msg = '{} ({}s, {})'.format(
617 msg, duration - proguard_duration,
618 PercentageDiffAsString(proguard_duration, duration))
619 success(msg) if duration < proguard_duration else warn(msg)
620 else:
621 print(msg)
622 if len(profile) >= 2:
623 for task_name, task_duration in profile.iteritems():
624 print(' {}: {}s'.format(task_name, task_duration))
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100625
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100626 if options.monkey:
627 monkey_status = result.get('monkey_status')
628 if monkey_status != 'success':
629 warn(' monkey: {}'.format(monkey_status))
630 else:
631 success(' monkey: {}'.format(monkey_status))
632 recompilation_results = result.get('recompilation_results', [])
633 i = 0
634 for recompilation_result in recompilation_results:
635 build_status = recompilation_result.get('build_status')
636 if build_status != 'success':
637 print(' recompilation #{}: {}'.format(i, build_status))
638 else:
639 dex_size = recompilation_result.get('dex_size')
640 print(' recompilation #{}'.format(i))
641 print(' dex size: {}'.format(dex_size))
642 if options.monkey:
643 monkey_status = recompilation_result.get('monkey_status')
644 msg = ' monkey: {}'.format(monkey_status)
645 if monkey_status == 'success':
646 success(msg)
647 elif monkey_status == 'skipped':
648 print(msg)
649 else:
650 warn(msg)
651 i += 1
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100652
Søren Gjessecdae8792018-12-12 09:02:43 +0100653def ParseOptions(argv):
654 result = optparse.OptionParser()
655 result.add_option('--app',
656 help='What app to run on',
657 choices=APPS.keys())
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100658 result.add_option('--download-only', '--download_only',
659 help='Whether to download apps without any compilation',
660 default=False,
661 action='store_true')
662 result.add_option('--golem',
663 help='Running on golem, do not download',
664 default=False,
665 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100666 result.add_option('--gradle-flags', '--gradle_flags',
667 help='Flags to pass in to gradle')
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100668 result.add_option('--ignore-versions', '--ignore_versions',
669 help='Allow checked-out app to differ in revision from '
670 'pinned',
671 default=False,
672 action='store_true')
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100673 result.add_option('--keystore',
674 help='Path to app.keystore',
675 default='app.keystore')
676 result.add_option('--keystore-password', '--keystore_password',
677 help='Password for app.keystore',
678 default='android')
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100679 result.add_option('--monkey',
680 help='Whether to install and run app(s) with monkey',
681 default=False,
682 action='store_true')
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100683 result.add_option('--monkey_events',
684 help='Number of events that the monkey should trigger',
685 default=250,
686 type=int)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100687 result.add_option('--no-build', '--no_build',
688 help='Run without building ToT first (only when using ToT)',
689 default=False,
690 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100691 result.add_option('--quiet',
692 help='Disable verbose logging',
693 default=False,
694 action='store_true')
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100695 result.add_option('--print-dexsegments',
696 metavar='BENCHMARKNAME',
697 help='Print the sizes of individual dex segments as ' +
698 '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
699 '<bytes>\'')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100700 result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
701 help='Number of times R8 should be run on each app',
702 default=2,
703 type=int)
Søren Gjesseeed839d2019-01-11 15:19:16 +0100704 result.add_option('--sign-apks', '--sign_apks',
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100705 help='Whether the APKs should be signed',
706 default=False,
707 action='store_true')
708 result.add_option('--shrinker',
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100709 help='The shrinkers to use (by default, all are run)',
710 action='append')
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100711 (options, args) = result.parse_args(argv)
712 if options.shrinker:
713 for shrinker in options.shrinker:
714 assert shrinker in SHRINKERS
715 return (options, args)
Søren Gjessecdae8792018-12-12 09:02:43 +0100716
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100717def download_apps(quiet):
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100718 # Download apps and place in build
719 with utils.ChangedWorkingDirectory(WORKING_DIR):
720 for app, config in APPS.iteritems():
721 app_dir = os.path.join(WORKING_DIR, app)
722 if not os.path.exists(app_dir):
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100723 GitClone(config['git_repo'], config['revision'], app_dir, quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100724
725
Søren Gjessecdae8792018-12-12 09:02:43 +0100726def main(argv):
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100727 global SHRINKERS
728
Søren Gjessecdae8792018-12-12 09:02:43 +0100729 (options, args) = ParseOptions(argv)
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100730
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100731 if options.golem:
Morten Krogh-Jespersenc2fbde22019-02-07 13:59:55 +0100732 golem.link_third_party()
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100733 if os.path.exists(WORKING_DIR):
734 shutil.rmtree(WORKING_DIR)
735 shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
736
737 if not os.path.exists(WORKING_DIR):
738 os.makedirs(WORKING_DIR)
739
740 if options.download_only:
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100741 download_apps(options.quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100742 return
743
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100744 with utils.TempDir() as temp_dir:
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100745 if not options.no_build or options.golem:
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100746 gradle.RunGradle(['r8', 'r8lib'])
Søren Gjessecdae8792018-12-12 09:02:43 +0100747
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100748 # Make a copy of r8.jar and r8lib.jar such that they stay the same for
749 # the entire execution of this script.
Christoffer Quist Adamsenf2ac20a2019-02-15 10:33:04 +0100750 all_shrinkers_enabled = (options.shrinker == None)
751 if all_shrinkers_enabled or 'r8-nolib' in options.shrinker:
Morten Krogh-Jespersen19ffe182019-02-15 10:12:31 +0100752 assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
753 shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
Christoffer Quist Adamsenf2ac20a2019-02-15 10:33:04 +0100754 if all_shrinkers_enabled or 'r8' in options.shrinker:
Morten Krogh-Jespersen19ffe182019-02-15 10:12:31 +0100755 assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
756 shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100757
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100758 result_per_shrinker_per_app = {}
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100759
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100760 if options.app:
761 result_per_shrinker_per_app[options.app] = GetResultsForApp(
762 options.app, APPS.get(options.app), options, temp_dir)
763 else:
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100764 for app, config in sorted(APPS.iteritems(), key=lambda s: s[0].lower()):
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100765 if not config.get('skip', False):
766 result_per_shrinker_per_app[app] = GetResultsForApp(
767 app, config, options, temp_dir)
768
769 LogResultsForApps(result_per_shrinker_per_app, options)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100770
771def success(message):
772 CGREEN = '\033[32m'
773 CEND = '\033[0m'
774 print(CGREEN + message + CEND)
775
776def warn(message):
777 CRED = '\033[91m'
778 CEND = '\033[0m'
779 print(CRED + message + CEND)
Søren Gjessecdae8792018-12-12 09:02:43 +0100780
781if __name__ == '__main__':
782 sys.exit(main(sys.argv[1:]))