blob: b9e17abc296427dc31b2ef1c1bf6e6f0d6543486 [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
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +010011import json
Søren Gjessecdae8792018-12-12 09:02:43 +010012import os
13import optparse
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +010014import shutil
Søren Gjessecdae8792018-12-12 09:02:43 +010015import subprocess
16import sys
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010017import time
Søren Gjessecdae8792018-12-12 09:02:43 +010018import utils
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +010019import zipfile
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +010020from xml.dom import minidom
Søren Gjessecdae8792018-12-12 09:02:43 +010021
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010022import as_utils
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +010023import create_maven_release
24import update_prebuilds_in_android
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010025
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +010026SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +010027WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010028
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +010029if ('R8_BENCHMARK_DIR' in os.environ
30 and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010031 WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
Søren Gjessecdae8792018-12-12 09:02:43 +010032
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +010033class Repo(object):
34 def __init__(self, fields):
35 self.__dict__ = fields
36
37 # If there is only one app in this repository, then give the app the same
38 # name as the repository, if it does not already have one.
39 if len(self.apps) == 1:
40 app = self.apps[0]
41 if not app.name:
42 app.name = self.name
43
44class App(object):
45 def __init__(self, fields):
46 module = fields.get('module', 'app')
47 defaults = {
48 'archives_base_name': module,
49 'build_dir': 'build',
50 'compile_sdk': None,
51 'dir': '.',
52 'flavor': None,
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +010053 'has_instrumentation_tests': False,
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +010054 'main_dex_rules': None,
55 'module': module,
56 'min_sdk': None,
57 'name': None,
58 'releaseTarget': None,
59 'signed_apk_name': None,
60 'skip': False
61 }
62 self.__dict__ = dict(defaults.items() + fields.items())
63
64# For running on Golem all third-party repositories are bundled as an x20-
65# dependency and then copied to WORKING_DIR. To update the app-bundle use
66# 'run_on_as_app_x20_packager.py'.
67APP_REPOSITORIES = [
68 # ...
69 # Repo({
70 # 'name': ...,
71 # 'url': ...,
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010072 # 'revision': ...,
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +010073 # 'apps': [
74 # {
75 # 'id': ...,
76 # 'dir': ...,
77 # 'module': ... (default app)
78 # 'name': ...,
79 # 'archives_base_name': ... (default same as module)
80 # 'flavor': ... (default no flavor)
81 # 'releaseTarget': ... (default <module>:assemble<flavor>Release
82 # },
83 # ...
84 # ]
85 # }),
86 # ...
87 Repo({
88 'name': 'android-suite',
89 'url': 'https://github.com/christofferqa/android-suite',
90 'revision': '46c96f214711cf6cdcb72cc0c94520ef418e3739',
91 'apps': [
92 App({
93 'id': 'com.numix.calculator',
94 'dir': 'Calculator',
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +010095 'name': 'numix-calculator',
96 'has_instrumentation_tests': True
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +010097 })
98 ]
99 }),
100 Repo({
101 'name': 'AnExplorer',
102 'url': 'https://github.com/christofferqa/AnExplorer',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100103 'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100104 'apps': [
105 App({
106 'id': 'dev.dworks.apps.anexplorer.pro',
107 'flavor': 'googleMobilePro',
108 'signed_apk_name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
109 'min_sdk': 17
110 })
111 ]
112 }),
113 Repo({
114 'name': 'AntennaPod',
115 'url': 'https://github.com/christofferqa/AntennaPod.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100116 'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100117 'apps': [
118 App({
119 'id': 'de.danoeh.antennapod',
120 'flavor': 'play',
121 'min_sdk': 14,
122 'compile_sdk': 26
123 })
124 ]
125 }),
126 Repo({
127 'name': 'apps-android-wikipedia',
128 'url': 'https://github.com/christofferqa/apps-android-wikipedia',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100129 'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100130 'apps': [
131 App({
132 'id': 'org.wikipedia',
133 'flavor': 'prod',
134 'signed_apk_name': 'app-prod-universal-release.apk'
135 })
136 ]
137 }),
138 Repo({
139 'name': 'chanu',
140 'url': 'https://github.com/mkj-gram/chanu.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100141 'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100142 'apps': [
143 App({
144 'id': 'com.chanapps.four.activity'
145 })
146 ]
147 }),
148 Repo({
149 'name': 'friendlyeats-android',
150 'url': 'https://github.com/christofferqa/friendlyeats-android.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100151 'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100152 'apps': [
153 App({
154 'id': 'com.google.firebase.example.fireeats'
155 })
156 ]
157 }),
158 Repo({
159 'name': 'Instabug-Android',
160 'url': 'https://github.com/christofferqa/Instabug-Android.git',
161 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f',
162 'apps': [
163 App({
164 'id': 'com.example.instabug'
165 })
166 ]
167 }),
168 Repo({
169 'name': 'KISS',
170 'url': 'https://github.com/christofferqa/KISS',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100171 'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100172 'apps': [
173 App({
174 'id': 'fr.neamar.kiss'
175 })
176 ]
177 }),
178 Repo({
179 'name': 'materialistic',
180 'url': 'https://github.com/christofferqa/materialistic',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100181 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100182 'apps': [
183 App({
184 'id': 'io.github.hidroh.materialistic'
185 })
186 ]
187 }),
188 Repo({
189 'name': 'Minimal-Todo',
190 'url': 'https://github.com/christofferqa/Minimal-Todo',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100191 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100192 'apps': [
193 App({
194 'id': 'com.avjindersinghsekhon.minimaltodo'
195 })
196 ]
197 }),
198 Repo({
199 'name': 'NewPipe',
200 'url': 'https://github.com/christofferqa/NewPipe',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100201 'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100202 'apps': [
203 App({
204 'id': 'org.schabi.newpipe'
205 })
206 ]
207 }),
208 Repo({
209 'name': 'rover-android',
210 'url': 'https://github.com/mkj-gram/rover-android.git',
Morten Krogh-Jespersen63ab8132019-02-19 12:02:55 +0100211 'revision': '859af82ba56fe9035ae9949156c7a88e6012d930',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100212 'apps': [
213 App({
214 'id': 'io.rover.app.debug',
215 'module': 'debug-app'
216 })
217 ]
218 }),
219 Repo({
220 'name': 'Signal-Android',
221 'url': 'https://github.com/mkj-gram/Signal-Android.git',
Morten Krogh-Jespersen9c861142019-02-19 12:12:23 +0100222 'revision': 'a45d0c1fed20fa39e8b9445fe7790326f46b3166',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100223 'apps': [
224 App({
225 'id': 'org.thoughtcrime.securesms',
226 'module': '',
227 'flavor': 'play',
228 'main_dex_rules': 'multidex-config.pro',
229 'releaseTarget': 'assemblePlayRelease',
230 'signed_apk_name': 'Signal-play-release-4.32.7.apk'
231 })
232 ]
233 }),
234 Repo({
235 'name': 'Simple-Calendar',
236 'url': 'https://github.com/christofferqa/Simple-Calendar',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100237 'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100238 'apps': [
239 App({
240 'id': 'com.simplemobiletools.calendar.pro',
241 'signed_apk_name': 'calendar-release.apk'
242 })
243 ]
244 }),
245 Repo({
246 'name': 'sqldelight',
247 'url': 'https://github.com/christofferqa/sqldelight.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100248 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100249 'apps': [
250 App({
251 'id': 'com.example.sqldelight.hockey',
252 'module': 'sample/android',
253 'archives_base_name': 'android',
254 'min_sdk': 14,
255 'compile_sdk': 28
256 })
257 ]
258 }),
259 Repo({
260 'name': 'tachiyomi',
261 'url': 'https://github.com/sgjesse/tachiyomi.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100262 'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100263 'apps': [
264 App({
265 'id': 'eu.kanade.tachiyomi',
266 'flavor': 'standard',
267 'releaseTarget': 'app:assembleRelease',
268 'min_sdk': 16
269 })
270 ]
271 }),
272 Repo({
273 'name': 'tivi',
274 'url': 'https://github.com/sgjesse/tivi.git',
Søren Gjessedbe6ebc2019-02-14 09:38:47 +0100275 'revision': '25c52e3593e7c98da4e537b49b29f6f67f88754d',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100276 'apps': [
277 App({
278 'id': 'app.tivi',
279 'min_sdk': 23,
280 'compile_sdk': 28
281 })
282 ]
283 }),
284 Repo({
285 'name': 'Tusky',
286 'url': 'https://github.com/mkj-gram/Tusky.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100287 'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100288 'apps': [
289 App({
290 'id': 'com.keylesspalace.tusky',
291 'flavor': 'blue'
292 })
293 ]
294 }),
295 Repo({
296 'name': 'Vungle-Android-SDK',
297 'url': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100298 'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100299 'apps': [
300 App({
301 'id': 'com.publisher.vungle.sample'
302 })
303 ]
304 }),
Søren Gjessecdae8792018-12-12 09:02:43 +0100305 # This does not build yet.
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100306 Repo({
307 'name': 'muzei',
308 'url': 'https://github.com/sgjesse/muzei.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100309 'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100310 'apps': [
311 App({
312 'module': 'main',
313 'archives_base_name': 'muzei',
314 'skip': True
315 })
316 ]
317 })
318]
319
320def GetAllApps():
321 apps = []
322 for repo in APP_REPOSITORIES:
323 for app in repo.apps:
324 apps.append((app, repo))
325 return apps
326
327def GetAllAppNames():
328 return [app.name for (app, repo) in GetAllApps()]
329
330def GetAppWithName(query):
331 for (app, repo) in GetAllApps():
332 if app.name == query:
333 return (app, repo)
334 assert False
Søren Gjessecdae8792018-12-12 09:02:43 +0100335
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100336def ComputeSizeOfDexFilesInApk(apk):
337 dex_size = 0
338 z = zipfile.ZipFile(apk, 'r')
339 for filename in z.namelist():
340 if filename.endswith('.dex'):
341 dex_size += z.getinfo(filename).file_size
342 return dex_size
343
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100344def ExtractMarker(apk, temp_dir, options):
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100345 r8_jar = os.path.join(temp_dir, 'r8.jar')
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100346 r8lib_jar = os.path.join(temp_dir, 'r8lib.jar')
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100347
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100348 # Use the copy of r8.jar or r8lib.jar if one is there.
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100349 if os.path.isfile(r8_jar):
Ian Zerny3f54e222019-02-12 10:51:17 +0100350 cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100351 elif os.path.isfile(r8lib_jar):
352 cmd = [jdk.GetJavaExecutable(), '-ea', '-cp', r8lib_jar,
Morten Krogh-Jespersen220e5702019-02-27 12:57:01 +0100353 'com.android.tools.r8.ExtractMarker', apk]
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100354 else:
355 script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
356 cmd = ['python', script, apk]
357
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100358 utils.PrintCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100359 stdout = subprocess.check_output(cmd)
360
361 # Return the last line.
362 lines = stdout.strip().splitlines()
363 assert len(lines) >= 1
364 return lines[-1]
365
Morten Krogh-Jespersen0de13732019-03-01 08:56:39 +0100366def CheckIsBuiltWithExpectedR8(apk, temp_dir, shrinker, options):
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100367 marker = ExtractMarker(apk, temp_dir, options)
368 expected_version = (
369 options.version
370 if options.version
Morten Krogh-Jespersen0de13732019-03-01 08:56:39 +0100371 else utils.getR8Version(
372 os.path.join(
373 temp_dir,
374 'r8lib.jar' if IsMinifiedR8(shrinker) else 'r8.jar')))
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100375 if marker.startswith('~~R8'):
376 actual_version = json.loads(marker[4:]).get('version')
377 if actual_version == expected_version:
378 return True
379 raise Exception(
380 'Expected APK to be built with R8 version {} (was: {})'.format(
381 expected_version, marker))
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100382
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100383def isR8(shrinker):
384 return 'r8' in shrinker
385
386def isR8FullMode(shrinker):
387 return shrinker == 'r8-full' or shrinker == 'r8-nolib-full'
388
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100389def IsMinifiedR8(shrinker):
390 return 'nolib' not in shrinker
391
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100392def IsTrackedByGit(file):
393 return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
394
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100395def GitClone(repo, checkout_dir, quiet):
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100396 result = subprocess.check_output(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100397 ['git', 'clone', repo.url, checkout_dir]).strip()
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100398 head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100399 if repo.revision == head_rev:
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100400 return result
401 warn('Target revision is not head in {}.'.format(checkout_dir))
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100402 with utils.ChangedWorkingDirectory(checkout_dir, quiet=quiet):
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100403 subprocess.check_output(['git', 'reset', '--hard', repo.revision])
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100404 return result
Søren Gjessecdae8792018-12-12 09:02:43 +0100405
406def GitCheckout(file):
407 return subprocess.check_output(['git', 'checkout', file]).strip()
408
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100409def InstallApkOnEmulator(apk_dest, options):
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100410 cmd = ['adb', '-s', options.emulator_id, 'install', '-r', '-d', apk_dest]
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100411 if options.quiet:
412 with open(os.devnull, 'w') as devnull:
413 subprocess.check_call(cmd, stdout=devnull)
414 else:
415 subprocess.check_call(cmd)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100416
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100417def PercentageDiffAsString(before, after):
418 if after < before:
419 return '-' + str(round((1.0 - after / before) * 100)) + '%'
420 else:
421 return '+' + str(round((after - before) / before * 100)) + '%'
422
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100423def UninstallApkOnEmulator(app, options):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100424 process = subprocess.Popen(
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100425 ['adb', '-s', options.emulator_id, 'uninstall', app.id],
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100426 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
427 stdout, stderr = process.communicate()
428
429 if stdout.strip() == 'Success':
430 # Successfully uninstalled
431 return
432
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100433 if 'Unknown package: {}'.format(app.id) in stderr:
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100434 # Application not installed
435 return
436
437 raise Exception(
438 'Unexpected result from `adb uninstall {}\nStdout: {}\nStderr: {}'.format(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100439 app.id, stdout, stderr))
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100440
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100441def WaitForEmulator(options):
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100442 stdout = subprocess.check_output(['adb', 'devices'])
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100443 if '{}\tdevice'.format(options.emulator_id) in stdout:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100444 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100445
446 print('Emulator \'{}\' not connected; waiting for connection'.format(
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100447 options.emulator_id))
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100448
449 time_waited = 0
450 while True:
451 time.sleep(10)
452 time_waited += 10
453 stdout = subprocess.check_output(['adb', 'devices'])
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100454 if '{}\tdevice'.format(options.emulator_id) not in stdout:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100455 print('... still waiting for connection')
456 if time_waited >= 5 * 60:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100457 return False
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100458 else:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100459 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100460
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100461def GetResultsForApp(app, repo, options, temp_dir):
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100462 # Checkout and build in the build directory.
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100463 repo_name = repo.name
464 repo_checkout_dir = os.path.join(WORKING_DIR, repo_name)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100465
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100466 result = {}
467
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100468 if not os.path.exists(repo_checkout_dir) and not options.golem:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100469 with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100470 GitClone(repo, repo_checkout_dir, options.quiet)
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100471
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100472 checkout_rev = utils.get_HEAD_sha1_for_checkout(repo_checkout_dir)
473 if repo.revision != checkout_rev:
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100474 msg = 'Checkout is not target revision for {} in {}.'.format(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100475 app.name, repo_checkout_dir)
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100476 if options.ignore_versions:
477 warn(msg)
478 else:
479 raise Exception(msg)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100480
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100481 result['status'] = 'success'
482
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100483 app_checkout_dir = os.path.join(repo_checkout_dir, app.dir)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100484 result_per_shrinker = BuildAppWithSelectedShrinkers(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100485 app, repo, options, app_checkout_dir, temp_dir)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100486 for shrinker, shrinker_result in result_per_shrinker.iteritems():
487 result[shrinker] = shrinker_result
488
489 return result
490
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100491def BuildAppWithSelectedShrinkers(
492 app, repo, options, checkout_dir, temp_dir):
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100493 result_per_shrinker = {}
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100494
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100495 with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100496 for shrinker in options.shrinker:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100497 apk_dest = None
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100498
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100499 result = {}
500 try:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100501 out_dir = os.path.join(checkout_dir, 'out', shrinker)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100502 (apk_dest, profile_dest_dir, proguard_config_file) = \
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100503 BuildAppWithShrinker(
504 app, repo, shrinker, checkout_dir, out_dir, temp_dir,
505 options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100506 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100507 result['apk_dest'] = apk_dest
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100508 result['build_status'] = 'success'
509 result['dex_size'] = dex_size
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100510 result['profile_dest_dir'] = profile_dest_dir
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100511
512 profile = as_utils.ParseProfileReport(profile_dest_dir)
513 result['profile'] = {
514 task_name:duration for task_name, duration in profile.iteritems()
515 if as_utils.IsGradleCompilerTask(task_name, shrinker)}
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100516 except Exception as e:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100517 warn('Failed to build {} with {}'.format(app, shrinker))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100518 if e:
519 print('Error: ' + str(e))
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100520 result['build_status'] = 'failed'
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100521
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100522 if result.get('build_status') == 'success':
523 if options.monkey:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100524 result['monkey_status'] = 'success' if RunMonkey(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100525 app, options, apk_dest) else 'failed'
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100526
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100527 if 'r8' in shrinker and options.r8_compilation_steps > 1:
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100528 result['recompilation_results'] = \
529 ComputeRecompilationResults(
530 app, repo, options, checkout_dir, temp_dir, shrinker,
531 proguard_config_file)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100532
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100533 if options.run_tests and app.has_instrumentation_tests:
534 result['instrumentation_test_results'] = \
535 ComputeInstrumentationTestResults(
536 app, options, checkout_dir, out_dir, shrinker)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100537
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100538 result_per_shrinker[shrinker] = result
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100539
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100540 if len(options.apps) > 1:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100541 print('')
542 LogResultsForApp(app, result_per_shrinker, options)
543 print('')
544
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100545 return result_per_shrinker
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100546
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100547def BuildAppWithShrinker(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100548 app, repo, shrinker, checkout_dir, out_dir, temp_dir, options,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100549 keepRuleSynthesisForRecompilation=False):
550 print('Building {} with {}{}'.format(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100551 app.name,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100552 shrinker,
553 ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100554
Morten Krogh-Jespersende566ea2019-02-18 11:59:48 +0100555 # Add settings.gradle file if it is not present to prevent gradle from finding
556 # the settings.gradle file in the r8 root when apps are placed under
557 # $R8/build.
Christoffer Quist Adamsen082351d2019-03-08 13:07:50 +0100558 as_utils.add_settings_gradle(checkout_dir, app.name)
Morten Krogh-Jespersende566ea2019-02-18 11:59:48 +0100559
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100560 # Add 'r8.jar' to top-level build.gradle.
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100561 as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100562
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100563 archives_base_name = app.archives_base_name
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100564
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100565 if not os.path.exists(out_dir):
566 os.makedirs(out_dir)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100567
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100568 # Set -printconfiguration in Proguard rules.
569 proguard_config_dest = os.path.abspath(
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100570 os.path.join(out_dir, 'proguard-rules.pro'))
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100571 as_utils.SetPrintConfigurationDirective(
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100572 app, checkout_dir, proguard_config_dest)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100573
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100574 env_vars = {}
575 env_vars['ANDROID_HOME'] = utils.getAndroidHome()
576 env_vars['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100577
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100578 releaseTarget = app.releaseTarget
Søren Gjesse8c111482018-12-21 14:47:57 +0100579 if not releaseTarget:
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100580 releaseTarget = app.module.replace('/', ':') + ':' + 'assemble' + (
581 app.flavor.capitalize() if app.flavor else '') + 'Release'
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100582
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100583 # Build using gradle.
584 args = [releaseTarget,
585 '--profile',
586 '-Pandroid.enableR8=' + str(isR8(shrinker)).lower(),
587 '-Pandroid.enableR8.fullMode=' + str(isR8FullMode(shrinker)).lower()]
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100588 if keepRuleSynthesisForRecompilation:
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100589 args.append('-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100590 if options.gradle_flags:
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100591 args.extend(options.gradle_flags.split(' '))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100592
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100593 stdout = utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100594
595 apk_base_name = (archives_base_name
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100596 + (('-' + app.flavor) if app.flavor else '') + '-release')
597 signed_apk_name = (
598 app.signed_apk_name
599 if app.signed_apk_name
600 else apk_base_name + '.apk')
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100601 unsigned_apk_name = apk_base_name + '-unsigned.apk'
602
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100603 build_dir = app.build_dir
604 build_output_apks = os.path.join(app.module, build_dir, 'outputs', 'apk')
605 if app.flavor:
606 build_output_apks = os.path.join(build_output_apks, app.flavor, 'release')
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100607 else:
608 build_output_apks = os.path.join(build_output_apks, 'release')
609
610 signed_apk = os.path.join(build_output_apks, signed_apk_name)
611 unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)
612
613 if options.sign_apks and not os.path.isfile(signed_apk):
614 assert os.path.isfile(unsigned_apk)
615 if options.sign_apks:
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100616 apk_utils.sign_with_apksigner(
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100617 unsigned_apk,
618 signed_apk,
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100619 options.keystore,
620 options.keystore_password,
621 quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100622
623 if os.path.isfile(signed_apk):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100624 apk_dest = os.path.join(out_dir, signed_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100625 as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100626 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100627 apk_dest = os.path.join(out_dir, unsigned_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100628 as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100629
Morten Krogh-Jespersen8a3450d2019-03-07 07:29:14 +0000630 assert ('r8' not in shrinker
631 or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100632
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100633 profile_dest_dir = os.path.join(out_dir, 'profile')
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100634 as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100635
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100636 return (apk_dest, profile_dest_dir, proguard_config_dest)
637
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100638def ComputeInstrumentationTestResults(
639 app, options, checkout_dir, out_dir, shrinker):
640 args = ['connectedAndroidTest',
641 '-Pandroid.enableR8=' + str(isR8(shrinker)).lower(),
642 '-Pandroid.enableR8.fullMode=' + str(isR8FullMode(shrinker)).lower()]
643 env_vars = { 'ANDROID_SERIAL': options.emulator_id }
644 stdout = \
645 utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet, fail=False)
646
647 xml_test_result_dest = os.path.join(out_dir, 'test_result')
648 as_utils.MoveXMLTestResultFileTo(
649 xml_test_result_dest, stdout, quiet=options.quiet)
650
651 with open(xml_test_result_dest, 'r') as f:
652 xml_test_result_contents = f.read()
653
654 xml_document = minidom.parseString(xml_test_result_contents)
655 testsuite_element = xml_document.documentElement
656
657 return {
658 'xml_test_result_dest': xml_test_result_dest,
659 'tests': int(testsuite_element.getAttribute('tests')),
660 'failures': int(testsuite_element.getAttribute('failures')),
661 'errors': int(testsuite_element.getAttribute('errors')),
662 'skipped': int(testsuite_element.getAttribute('skipped'))
663 }
664
665def ComputeRecompilationResults(
666 app, repo, options, checkout_dir, temp_dir, shrinker, proguard_config_file):
667 recompilation_results = []
668
669 # Build app with gradle using -D...keepRuleSynthesisForRecompilation=
670 # true.
671 out_dir = os.path.join(checkout_dir, 'out', shrinker + '-1')
672 (apk_dest, profile_dest_dir, ext_proguard_config_file) = \
673 BuildAppWithShrinker(
674 app, repo, shrinker, checkout_dir, out_dir,
675 temp_dir, options, keepRuleSynthesisForRecompilation=True)
676 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
677 recompilation_result = {
678 'apk_dest': apk_dest,
679 'build_status': 'success',
680 'dex_size': ComputeSizeOfDexFilesInApk(apk_dest),
681 'monkey_status': 'skipped'
682 }
683 recompilation_results.append(recompilation_result)
684
685 # Sanity check that keep rules have changed.
686 with open(ext_proguard_config_file) as new:
687 with open(proguard_config_file) as old:
688 assert(
689 sum(1 for line in new
690 if line.strip() and '-printconfiguration' not in line)
691 >
692 sum(1 for line in old
693 if line.strip() and '-printconfiguration' not in line))
694
695 # Extract min-sdk and target-sdk
696 (min_sdk, compile_sdk) = \
697 as_utils.GetMinAndCompileSdk(app, checkout_dir, apk_dest)
698
699 # Now rebuild generated apk.
700 previous_apk = apk_dest
701
702 # We may need main dex rules when re-compiling with R8 as standalone.
703 main_dex_rules = None
704 if app.main_dex_rules:
705 main_dex_rules = os.path.join(checkout_dir, app.main_dex_rules)
706
707 for i in range(1, options.r8_compilation_steps):
708 try:
709 recompiled_apk_dest = os.path.join(
710 checkout_dir, 'out', shrinker, 'app-release-{}.apk'.format(i))
711 RebuildAppWithShrinker(
712 app, previous_apk, recompiled_apk_dest,
713 ext_proguard_config_file, shrinker, min_sdk, compile_sdk,
714 options, temp_dir, main_dex_rules)
715 recompilation_result = {
716 'apk_dest': recompiled_apk_dest,
717 'build_status': 'success',
718 'dex_size': ComputeSizeOfDexFilesInApk(recompiled_apk_dest)
719 }
720 if options.monkey:
721 recompilation_result['monkey_status'] = 'success' if RunMonkey(
722 app, options, recompiled_apk_dest) else 'failed'
723 recompilation_results.append(recompilation_result)
724 previous_apk = recompiled_apk_dest
725 except Exception as e:
726 warn('Failed to recompile {} with {}'.format(
727 app.name, shrinker))
728 recompilation_results.append({ 'build_status': 'failed' })
729 break
730 return recompilation_results
731
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100732def RebuildAppWithShrinker(
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100733 app, apk, apk_dest, proguard_config_file, shrinker, min_sdk, compile_sdk,
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100734 options, temp_dir, main_dex_rules):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100735 assert 'r8' in shrinker
736 assert apk_dest.endswith('.apk')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100737
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100738 print('Rebuilding {} with {}'.format(app.name, shrinker))
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100739
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100740 # Compile given APK with shrinker to temporary zip file.
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100741 android_jar = utils.get_android_jar(compile_sdk)
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100742 r8_jar = os.path.join(
743 temp_dir, 'r8lib.jar' if IsMinifiedR8(shrinker) else 'r8.jar')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100744 zip_dest = apk_dest[:-4] + '.zip'
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100745
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100746 # TODO(christofferqa): Entry point should be CompatProguard if the shrinker
747 # is 'r8'.
748 entry_point = 'com.android.tools.r8.R8'
749
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100750 cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar,
751 entry_point, '--release', '--min-api', str(min_sdk), '--pg-conf',
752 proguard_config_file, '--lib', android_jar, '--output', zip_dest, apk]
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100753
754 for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
755 cmd.append('--lib')
756 cmd.append(android_optional_jar)
757
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100758 if main_dex_rules:
759 cmd.append('--main-dex-rules')
760 cmd.append(main_dex_rules)
761
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100762 utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100763
764 # Make a copy of the given APK, move the newly generated dex files into the
765 # copied APK, and then sign the APK.
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100766 apk_masseur.masseur(
Christoffer Quist Adamsen2dcea102019-02-20 11:31:38 +0100767 apk, dex=zip_dest, resources='META-INF/services/*', out=apk_dest,
768 quiet=options.quiet)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100769
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100770def RunMonkey(app, options, apk_dest):
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100771 if not WaitForEmulator(options):
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100772 return False
773
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100774 UninstallApkOnEmulator(app, options)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100775 InstallApkOnEmulator(apk_dest, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100776
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100777 number_of_events_to_generate = options.monkey_events
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100778
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100779 # Intentionally using a constant seed such that the monkey generates the same
780 # event sequence for each shrinker.
781 random_seed = 42
782
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100783 cmd = ['adb', '-s', options.emulator_id, 'shell', 'monkey', '-p', app.id,
784 '-s', str(random_seed), str(number_of_events_to_generate)]
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100785
786 try:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100787 stdout = utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100788 succeeded = (
789 'Events injected: {}'.format(number_of_events_to_generate) in stdout)
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100790 except subprocess.CalledProcessError as e:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100791 succeeded = False
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100792
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100793 UninstallApkOnEmulator(app, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100794
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100795 return succeeded
796
797def LogResultsForApps(result_per_shrinker_per_app, options):
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100798 print('')
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100799 for (app, result_per_shrinker) in result_per_shrinker_per_app:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100800 LogResultsForApp(app, result_per_shrinker, options)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100801
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100802def LogResultsForApp(app, result_per_shrinker, options):
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100803 if options.print_dexsegments:
804 LogSegmentsForApp(app, result_per_shrinker, options)
805 else:
806 LogComparisonResultsForApp(app, result_per_shrinker, options)
807
808def LogSegmentsForApp(app, result_per_shrinker, options):
809 for shrinker in SHRINKERS:
810 if shrinker not in result_per_shrinker:
811 continue
812 result = result_per_shrinker[shrinker];
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100813 benchmark_name = '{}-{}'.format(options.print_dexsegments, app.name)
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100814 utils.print_dexsegments(benchmark_name, [result.get('apk_dest')])
815 duration = sum(result.get('profile').values())
816 print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration * 1000))
817 print('%s-Total(CodeSize): %s' % (benchmark_name, result.get('dex_size')))
818
819
820def LogComparisonResultsForApp(app, result_per_shrinker, options):
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100821 print(app.name + ':')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100822
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100823 if result_per_shrinker.get('status', 'success') != 'success':
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100824 error_message = result_per_shrinker.get('error_message')
825 print(' skipped ({})'.format(error_message))
826 return
827
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100828 proguard_result = result_per_shrinker.get('pg', {})
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100829 proguard_dex_size = float(proguard_result.get('dex_size', -1))
830 proguard_duration = sum(proguard_result.get('profile', {}).values())
831
832 for shrinker in SHRINKERS:
833 if shrinker not in result_per_shrinker:
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100834 continue
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100835 result = result_per_shrinker.get(shrinker)
836 build_status = result.get('build_status')
837 if build_status != 'success':
838 warn(' {}: {}'.format(shrinker, build_status))
839 else:
840 print(' {}:'.format(shrinker))
841 dex_size = result.get('dex_size')
842 msg = ' dex size: {}'.format(dex_size)
843 if dex_size != proguard_dex_size and proguard_dex_size >= 0:
844 msg = '{} ({}, {})'.format(
845 msg, dex_size - proguard_dex_size,
846 PercentageDiffAsString(proguard_dex_size, dex_size))
847 success(msg) if dex_size < proguard_dex_size else warn(msg)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100848 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100849 print(msg)
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100850
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100851 profile = result.get('profile')
852 duration = sum(profile.values())
853 msg = ' performance: {}s'.format(duration)
854 if duration != proguard_duration and proguard_duration > 0:
855 msg = '{} ({}s, {})'.format(
856 msg, duration - proguard_duration,
857 PercentageDiffAsString(proguard_duration, duration))
858 success(msg) if duration < proguard_duration else warn(msg)
859 else:
860 print(msg)
861 if len(profile) >= 2:
862 for task_name, task_duration in profile.iteritems():
863 print(' {}: {}s'.format(task_name, task_duration))
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100864
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100865 if options.monkey:
866 monkey_status = result.get('monkey_status')
867 if monkey_status != 'success':
868 warn(' monkey: {}'.format(monkey_status))
869 else:
870 success(' monkey: {}'.format(monkey_status))
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100871
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100872 recompilation_results = result.get('recompilation_results', [])
873 i = 0
874 for recompilation_result in recompilation_results:
875 build_status = recompilation_result.get('build_status')
876 if build_status != 'success':
877 print(' recompilation #{}: {}'.format(i, build_status))
878 else:
879 dex_size = recompilation_result.get('dex_size')
880 print(' recompilation #{}'.format(i))
881 print(' dex size: {}'.format(dex_size))
882 if options.monkey:
883 monkey_status = recompilation_result.get('monkey_status')
884 msg = ' monkey: {}'.format(monkey_status)
885 if monkey_status == 'success':
886 success(msg)
887 elif monkey_status == 'skipped':
888 print(msg)
889 else:
890 warn(msg)
891 i += 1
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100892
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100893 if options.run_tests and 'instrumentation_test_results' in result:
894 instrumentation_test_results = \
895 result.get('instrumentation_test_results')
896 succeeded = (
897 instrumentation_test_results.get('failures')
898 + instrumentation_test_results.get('errors')
899 + instrumentation_test_results.get('skipped')) == 0
900 if succeeded:
901 success(' tests: succeeded')
902 else:
903 warn(
904 ' tests: failed (failures: {}, errors: {}, skipped: {})'
905 .format(
906 instrumentation_test_results.get('failures'),
907 instrumentation_test_results.get('errors'),
908 instrumentation_test_results.get('skipped')))
909
Søren Gjessecdae8792018-12-12 09:02:43 +0100910def ParseOptions(argv):
911 result = optparse.OptionParser()
912 result.add_option('--app',
913 help='What app to run on',
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100914 choices=GetAllAppNames())
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100915 result.add_option('--download-only', '--download_only',
916 help='Whether to download apps without any compilation',
917 default=False,
918 action='store_true')
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100919 result.add_option('--emulator-id', '--emulator_id',
920 help='Id of the emulator to use',
921 default='emulator-5554')
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100922 result.add_option('--golem',
923 help='Running on golem, do not download',
924 default=False,
925 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100926 result.add_option('--gradle-flags', '--gradle_flags',
927 help='Flags to pass in to gradle')
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100928 result.add_option('--ignore-versions', '--ignore_versions',
929 help='Allow checked-out app to differ in revision from '
930 'pinned',
931 default=False,
932 action='store_true')
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100933 result.add_option('--keystore',
934 help='Path to app.keystore',
935 default='app.keystore')
936 result.add_option('--keystore-password', '--keystore_password',
937 help='Password for app.keystore',
938 default='android')
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100939 result.add_option('--monkey',
940 help='Whether to install and run app(s) with monkey',
941 default=False,
942 action='store_true')
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100943 result.add_option('--monkey_events',
944 help='Number of events that the monkey should trigger',
945 default=250,
946 type=int)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100947 result.add_option('--no-build', '--no_build',
948 help='Run without building ToT first (only when using ToT)',
949 default=False,
950 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100951 result.add_option('--quiet',
952 help='Disable verbose logging',
953 default=False,
954 action='store_true')
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100955 result.add_option('--print-dexsegments',
956 metavar='BENCHMARKNAME',
957 help='Print the sizes of individual dex segments as ' +
958 '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
959 '<bytes>\'')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100960 result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
961 help='Number of times R8 should be run on each app',
962 default=2,
963 type=int)
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +0100964 result.add_option('--run-tests', '--run_tests',
965 help='Whether to run instrumentation tests',
966 default=False,
967 action='store_true')
Søren Gjesseeed839d2019-01-11 15:19:16 +0100968 result.add_option('--sign-apks', '--sign_apks',
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100969 help='Whether the APKs should be signed',
970 default=False,
971 action='store_true')
972 result.add_option('--shrinker',
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100973 help='The shrinkers to use (by default, all are run)',
974 action='append')
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100975 result.add_option('--version',
976 help='The version of R8 to use (e.g., 1.4.51)')
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100977 (options, args) = result.parse_args(argv)
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +0100978 if options.app:
979 options.apps = [GetAppWithName(options.app)]
980 del options.app
981 else:
982 options.apps = GetAllApps()
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100983 if options.shrinker:
984 for shrinker in options.shrinker:
985 assert shrinker in SHRINKERS
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100986 else:
987 options.shrinker = [shrinker for shrinker in SHRINKERS]
988 if options.version:
989 # No need to build R8 if a specific release version should be used.
990 options.no_build = True
991 if 'r8-nolib' in options.shrinker:
992 warn('Skipping shrinker r8-nolib because a specific release version '
993 + 'of r8 was specified')
994 options.shrinker.remove('r8-nolib')
995 if 'r8-nolib-full' in options.shrinker:
996 warn('Skipping shrinker r8-nolib-full because a specific release version '
997 + 'of r8 was specified')
998 options.shrinker.remove('r8-nolib-full')
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100999 return (options, args)
Søren Gjessecdae8792018-12-12 09:02:43 +01001000
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +01001001def clone_repositories(quiet):
1002 # Clone repositories into WORKING_DIR.
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001003 with utils.ChangedWorkingDirectory(WORKING_DIR):
Christoffer Quist Adamsene57b73c2019-03-07 15:13:03 +01001004 for repo in APP_REPOSITORIES:
1005 repo_dir = os.path.join(WORKING_DIR, repo.name)
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +01001006 if not os.path.exists(repo_dir):
1007 GitClone(repo, repo_dir, quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001008
1009
Søren Gjessecdae8792018-12-12 09:02:43 +01001010def main(argv):
1011 (options, args) = ParseOptions(argv)
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +01001012
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001013 if options.golem:
Morten Krogh-Jespersenc2fbde22019-02-07 13:59:55 +01001014 golem.link_third_party()
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001015 if os.path.exists(WORKING_DIR):
1016 shutil.rmtree(WORKING_DIR)
1017 shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
Morten Krogh-Jespersen220e5702019-02-27 12:57:01 +01001018 os.environ[utils.ANDROID_HOME_ENVIROMENT_NAME] = os.path.join(
1019 utils.ANDROID_SDK)
1020 os.environ[utils.ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME] = '28.0.3'
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001021
1022 if not os.path.exists(WORKING_DIR):
1023 os.makedirs(WORKING_DIR)
1024
1025 if options.download_only:
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +01001026 clone_repositories(options.quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +01001027 return
1028
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +01001029 with utils.TempDir() as temp_dir:
Morten Krogh-Jespersen2bd5ec82019-02-25 16:01:23 +01001030 if not (options.no_build or options.golem):
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +01001031 gradle.RunGradle(['r8', 'r8lib'])
Søren Gjessecdae8792018-12-12 09:02:43 +01001032
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +01001033 if options.version:
1034 # Download r8-<version>.jar from
1035 # http://storage.googleapis.com/r8-releases/raw/.
1036 target = 'r8-{}.jar'.format(options.version)
1037 update_prebuilds_in_android.download_version(
1038 temp_dir, 'com/android/tools/r8/' + options.version, target)
1039 as_utils.MoveFile(
1040 os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
1041 quiet=options.quiet)
1042 else:
1043 # Make a copy of r8.jar and r8lib.jar such that they stay the same for
1044 # the entire execution of this script.
1045 if 'r8-nolib' in options.shrinker:
1046 assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
1047 shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
1048 if 'r8' in options.shrinker:
1049 assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
1050 shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +01001051
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +01001052 result_per_shrinker_per_app = []
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +01001053
Christoffer Quist Adamsenb899c112019-03-06 15:25:00 +01001054 for (app, repo) in options.apps:
1055 if app.skip:
1056 continue
Christoffer Quist Adamsen7cf4c562019-03-07 10:57:33 +01001057 result_per_shrinker_per_app.append(
1058 (app, GetResultsForApp(app, repo, options, temp_dir)))
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +01001059
1060 LogResultsForApps(result_per_shrinker_per_app, options)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +01001061
1062def success(message):
1063 CGREEN = '\033[32m'
1064 CEND = '\033[0m'
1065 print(CGREEN + message + CEND)
1066
1067def warn(message):
1068 CRED = '\033[91m'
1069 CEND = '\033[0m'
1070 print(CRED + message + CEND)
Søren Gjessecdae8792018-12-12 09:02:43 +01001071
1072if __name__ == '__main__':
1073 sys.exit(main(sys.argv[1:]))