blob: aa7ed2956674b325b042abca1b978fc0d1cdcafc [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
Søren Gjessecdae8792018-12-12 09:02:43 +010020
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010021import as_utils
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +010022import create_maven_release
23import update_prebuilds_in_android
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010024
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +010025SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full', 'pg']
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +010026WORKING_DIR = os.path.join(utils.BUILD, 'opensource_apps')
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010027
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +010028if ('R8_BENCHMARK_DIR' in os.environ
29 and os.path.isdir(os.environ['R8_BENCHMARK_DIR'])):
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010030 WORKING_DIR = os.environ['R8_BENCHMARK_DIR']
Søren Gjessecdae8792018-12-12 09:02:43 +010031
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +010032# For running on Golem all APPS are bundled as an x20-dependency and then copied
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +010033# to WORKING_DIR. To update the app-bundle use 'run_on_as_app_x20_packager.py'.
Søren Gjessecdae8792018-12-12 09:02:43 +010034APPS = {
35 # 'app-name': {
36 # 'git_repo': ...
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010037 # 'revision': ...,
Søren Gjessecdae8792018-12-12 09:02:43 +010038 # 'app_module': ... (default app)
39 # 'archives_base_name': ... (default same as app_module)
40 # 'flavor': ... (default no flavor)
Søren Gjesse8c111482018-12-21 14:47:57 +010041 # 'releaseTarget': ... (default <app_module>:assemble<flavor>Release
Søren Gjessecdae8792018-12-12 09:02:43 +010042 # },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010043 'AnExplorer': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010044 'app_id': 'dev.dworks.apps.anexplorer.pro',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010045 'git_repo': 'https://github.com/christofferqa/AnExplorer',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010046 'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010047 'flavor': 'googleMobilePro',
48 'signed-apk-name': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +010049 'min_sdk': 17
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010050 },
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010051 'AntennaPod': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010052 'app_id': 'de.danoeh.antennapod',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010053 'git_repo': 'https://github.com/christofferqa/AntennaPod.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010054 'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010055 'flavor': 'play',
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +010056 'min_sdk': 14,
57 'compile_sdk': 26
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +010058 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010059 'apps-android-wikipedia': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010060 'app_id': 'org.wikipedia',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010061 'git_repo': 'https://github.com/christofferqa/apps-android-wikipedia',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010062 'revision': '686e8aa5682af8e6a905054b935dd2daa57e63ee',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010063 'flavor': 'prod',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010064 'signed-apk-name': 'app-prod-universal-release.apk',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010065 },
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +010066 'chanu': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010067 'app_id': 'com.chanapps.four.activity',
68 'git_repo': 'https://github.com/mkj-gram/chanu.git',
69 'revision': '04ade1e9c33d707f0850d5eb9d6fa5e8af814a26',
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +010070 },
Christoffer Quist Adamsen61b8c802018-12-20 08:18:40 +010071 'friendlyeats-android': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010072 'app_id': 'com.google.firebase.example.fireeats',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010073 'git_repo': 'https://github.com/christofferqa/friendlyeats-android.git',
74 'revision': '10091fa0ec37da12e66286559ad1b6098976b07b',
Christoffer Quist Adamsen61b8c802018-12-20 08:18:40 +010075 },
Christoffer Quist Adamsena2d16cd2019-02-07 10:41:20 +010076 'Instabug-Android': {
77 'app_id': 'com.example.instabug',
78 'git_repo': 'https://github.com/christofferqa/Instabug-Android.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010079 'revision': 'b8df78c96630a6537fbc07787b4990afc030cc0f'
Christoffer Quist Adamsena2d16cd2019-02-07 10:41:20 +010080 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010081 'KISS': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010082 'app_id': 'fr.neamar.kiss',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010083 'git_repo': 'https://github.com/christofferqa/KISS',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010084 'revision': '093da9ee0512e67192f62951c45a07a616fc3224',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010085 },
86 'materialistic': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010087 'app_id': 'io.github.hidroh.materialistic',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010088 'git_repo': 'https://github.com/christofferqa/materialistic',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010089 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010090 },
91 'Minimal-Todo': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010092 'app_id': 'com.avjindersinghsekhon.minimaltodo',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010093 'git_repo': 'https://github.com/christofferqa/Minimal-Todo',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010094 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +010095 },
96 'NewPipe': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +010097 'app_id': 'org.schabi.newpipe',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +010098 'git_repo': 'https://github.com/christofferqa/NewPipe',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +010099 'revision': 'ed543099c7823be00f15d9340f94bdb7cb37d1e6',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100100 },
Morten Krogh-Jespersen4293dcd2019-01-28 11:38:50 +0100101 'rover-android': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100102 'app_id': 'io.rover.app.debug',
103 'app_module': 'debug-app',
104 'git_repo': 'https://github.com/mkj-gram/rover-android.git',
Morten Krogh-Jespersen63ab8132019-02-19 12:02:55 +0100105 'revision': '859af82ba56fe9035ae9949156c7a88e6012d930',
Morten Krogh-Jespersen4293dcd2019-01-28 11:38:50 +0100106 },
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +0100107 'Signal-Android': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100108 'app_id': 'org.thoughtcrime.securesms',
109 'app_module': '',
110 'flavor': 'play',
111 'git_repo': 'https://github.com/mkj-gram/Signal-Android.git',
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100112 'main_dex_rules': 'multidex-config.pro',
Morten Krogh-Jespersen9c861142019-02-19 12:12:23 +0100113 'revision': 'a45d0c1fed20fa39e8b9445fe7790326f46b3166',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100114 'releaseTarget': 'assemblePlayRelease',
115 'signed-apk-name': 'Signal-play-release-4.32.7.apk',
Morten Krogh-Jespersen2dbbc1b2019-01-24 16:31:52 +0100116 },
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100117 'Simple-Calendar': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100118 'app_id': 'com.simplemobiletools.calendar.pro',
Christoffer Quist Adamsen4e661df2019-01-22 10:03:06 +0100119 'git_repo': 'https://github.com/christofferqa/Simple-Calendar',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100120 'revision': '82dad8c203eea5a0f0ddb513506d8f1de986ef2b',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100121 'signed-apk-name': 'calendar-release.apk'
122 },
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100123 'sqldelight': {
124 'app_id': 'com.example.sqldelight.hockey',
125 'git_repo': 'https://github.com/christofferqa/sqldelight.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100126 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100127 'app_module': 'sample/android',
128 'archives_base_name': 'android',
129 'min_sdk': 14,
130 'compile_sdk': 28,
131 },
Søren Gjessecdae8792018-12-12 09:02:43 +0100132 'tachiyomi': {
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100133 'app_id': 'eu.kanade.tachiyomi',
Søren Gjessecdae8792018-12-12 09:02:43 +0100134 'git_repo': 'https://github.com/sgjesse/tachiyomi.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100135 'revision': 'b15d2fe16864645055af6a745a62cc5566629798',
Søren Gjessecdae8792018-12-12 09:02:43 +0100136 'flavor': 'standard',
Søren Gjesse8c111482018-12-21 14:47:57 +0100137 'releaseTarget': 'app:assembleRelease',
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100138 'min_sdk': 16
Søren Gjessecdae8792018-12-12 09:02:43 +0100139 },
Søren Gjesse9fb48802019-01-18 11:00:00 +0100140 'tivi': {
141 'app_id': 'app.tivi',
Søren Gjesse9fb48802019-01-18 11:00:00 +0100142 'git_repo': 'https://github.com/sgjesse/tivi.git',
Søren Gjessedbe6ebc2019-02-14 09:38:47 +0100143 'revision': '25c52e3593e7c98da4e537b49b29f6f67f88754d',
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100144 'min_sdk': 23,
145 'compile_sdk': 28,
Søren Gjesse9fb48802019-01-18 11:00:00 +0100146 },
Morten Krogh-Jespersen580dc642019-01-25 10:18:22 +0100147 'Tusky': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100148 'app_id': 'com.keylesspalace.tusky',
149 'git_repo': 'https://github.com/mkj-gram/Tusky.git',
150 'revision': 'b794f3ab90388add98461ffe70edb65c39351c33',
151 'flavor': 'blue'
Morten Krogh-Jespersen580dc642019-01-25 10:18:22 +0100152 },
Morten Krogh-Jespersenaf6377d2019-01-29 11:18:31 +0100153 'Vungle-Android-SDK': {
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100154 'app_id': 'com.publisher.vungle.sample',
155 'git_repo': 'https://github.com/mkj-gram/Vungle-Android-SDK.git',
156 'revision': '3e231396ea7ce97b2655e03607497c75730e45f6',
Morten Krogh-Jespersenaf6377d2019-01-29 11:18:31 +0100157 },
Søren Gjessecdae8792018-12-12 09:02:43 +0100158 # This does not build yet.
159 'muzei': {
160 'git_repo': 'https://github.com/sgjesse/muzei.git',
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100161 'revision': 'bed2a5f79c6e08b0a21e3e3f9242232d0848ef74',
Søren Gjessecdae8792018-12-12 09:02:43 +0100162 'app_module': 'main',
163 'archives_base_name': 'muzei',
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100164 'skip': True,
Søren Gjessecdae8792018-12-12 09:02:43 +0100165 },
166}
167
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100168# TODO(christofferqa): Do not rely on 'emulator-5554' name
169emulator_id = 'emulator-5554'
170
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100171def ComputeSizeOfDexFilesInApk(apk):
172 dex_size = 0
173 z = zipfile.ZipFile(apk, 'r')
174 for filename in z.namelist():
175 if filename.endswith('.dex'):
176 dex_size += z.getinfo(filename).file_size
177 return dex_size
178
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100179def ExtractMarker(apk, temp_dir, options):
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100180 r8_jar = os.path.join(temp_dir, 'r8.jar')
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100181 r8lib_jar = os.path.join(temp_dir, 'r8lib.jar')
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100182
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100183 # Use the copy of r8.jar or r8lib.jar if one is there.
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100184 if os.path.isfile(r8_jar):
Ian Zerny3f54e222019-02-12 10:51:17 +0100185 cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100186 elif os.path.isfile(r8lib_jar):
187 cmd = [jdk.GetJavaExecutable(), '-ea', '-cp', r8lib_jar,
Morten Krogh-Jespersen220e5702019-02-27 12:57:01 +0100188 'com.android.tools.r8.ExtractMarker', apk]
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100189 else:
190 script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
191 cmd = ['python', script, apk]
192
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100193 utils.PrintCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100194 stdout = subprocess.check_output(cmd)
195
196 # Return the last line.
197 lines = stdout.strip().splitlines()
198 assert len(lines) >= 1
199 return lines[-1]
200
Morten Krogh-Jespersen0de13732019-03-01 08:56:39 +0100201def CheckIsBuiltWithExpectedR8(apk, temp_dir, shrinker, options):
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100202 marker = ExtractMarker(apk, temp_dir, options)
203 expected_version = (
204 options.version
205 if options.version
Morten Krogh-Jespersen0de13732019-03-01 08:56:39 +0100206 else utils.getR8Version(
207 os.path.join(
208 temp_dir,
209 'r8lib.jar' if IsMinifiedR8(shrinker) else 'r8.jar')))
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100210 if marker.startswith('~~R8'):
211 actual_version = json.loads(marker[4:]).get('version')
212 if actual_version == expected_version:
213 return True
214 raise Exception(
215 'Expected APK to be built with R8 version {} (was: {})'.format(
216 expected_version, marker))
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100217
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100218def IsMinifiedR8(shrinker):
219 return 'nolib' not in shrinker
220
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100221def IsTrackedByGit(file):
222 return subprocess.check_output(['git', 'ls-files', file]).strip() != ''
223
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100224def GitClone(git_url, revision, checkout_dir, quiet):
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100225 result = subprocess.check_output(
226 ['git', 'clone', git_url, checkout_dir]).strip()
227 head_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
228 if revision == head_rev:
229 return result
230 warn('Target revision is not head in {}.'.format(checkout_dir))
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100231 with utils.ChangedWorkingDirectory(checkout_dir, quiet=quiet):
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100232 subprocess.check_output(['git', 'reset', '--hard', revision])
233 return result
Søren Gjessecdae8792018-12-12 09:02:43 +0100234
235def GitCheckout(file):
236 return subprocess.check_output(['git', 'checkout', file]).strip()
237
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100238def InstallApkOnEmulator(apk_dest, options):
239 cmd = ['adb', '-s', emulator_id, 'install', '-r', '-d', apk_dest]
240 if options.quiet:
241 with open(os.devnull, 'w') as devnull:
242 subprocess.check_call(cmd, stdout=devnull)
243 else:
244 subprocess.check_call(cmd)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100245
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100246def PercentageDiffAsString(before, after):
247 if after < before:
248 return '-' + str(round((1.0 - after / before) * 100)) + '%'
249 else:
250 return '+' + str(round((after - before) / before * 100)) + '%'
251
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100252def UninstallApkOnEmulator(app, config, options):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100253 app_id = config.get('app_id')
254 process = subprocess.Popen(
Christoffer Quist Adamsen67608982019-02-08 10:09:35 +0100255 ['adb', '-s', emulator_id, 'uninstall', app_id],
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100256 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
257 stdout, stderr = process.communicate()
258
259 if stdout.strip() == 'Success':
260 # Successfully uninstalled
261 return
262
263 if 'Unknown package: {}'.format(app_id) in stderr:
264 # Application not installed
265 return
266
267 raise Exception(
268 'Unexpected result from `adb uninstall {}\nStdout: {}\nStderr: {}'.format(
269 app_id, stdout, stderr))
270
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100271def WaitForEmulator():
272 stdout = subprocess.check_output(['adb', 'devices'])
273 if '{}\tdevice'.format(emulator_id) in stdout:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100274 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100275
276 print('Emulator \'{}\' not connected; waiting for connection'.format(
277 emulator_id))
278
279 time_waited = 0
280 while True:
281 time.sleep(10)
282 time_waited += 10
283 stdout = subprocess.check_output(['adb', 'devices'])
284 if '{}\tdevice'.format(emulator_id) not in stdout:
285 print('... still waiting for connection')
286 if time_waited >= 5 * 60:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100287 return False
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100288 else:
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100289 return True
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100290
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100291def GetResultsForApp(app, config, options, temp_dir):
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100292 # Checkout and build in the build directory.
293 checkout_dir = os.path.join(WORKING_DIR, app)
294
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100295 result = {}
296
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100297 if not os.path.exists(checkout_dir) and not options.golem:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100298 with utils.ChangedWorkingDirectory(WORKING_DIR, quiet=options.quiet):
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100299 GitClone(
300 config['git_repo'], config['revision'], checkout_dir, options.quiet)
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100301
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100302 checkout_rev = utils.get_HEAD_sha1_for_checkout(checkout_dir)
303 if config['revision'] != checkout_rev:
304 msg = 'Checkout is not target revision for {} in {}.'.format(
305 app, checkout_dir)
306 if options.ignore_versions:
307 warn(msg)
308 else:
309 raise Exception(msg)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100310
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100311 result['status'] = 'success'
312
313 result_per_shrinker = BuildAppWithSelectedShrinkers(
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100314 app, config, options, checkout_dir, temp_dir)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100315 for shrinker, shrinker_result in result_per_shrinker.iteritems():
316 result[shrinker] = shrinker_result
317
318 return result
319
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100320def BuildAppWithSelectedShrinkers(app, config, options, checkout_dir, temp_dir):
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100321 result_per_shrinker = {}
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100322
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100323 with utils.ChangedWorkingDirectory(checkout_dir, quiet=options.quiet):
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100324 for shrinker in options.shrinker:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100325 apk_dest = None
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100326
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100327 result = {}
328 try:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100329 out_dir = os.path.join(checkout_dir, 'out', shrinker)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100330 (apk_dest, profile_dest_dir, proguard_config_file) = \
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100331 BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100332 temp_dir, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100333 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100334 result['apk_dest'] = apk_dest
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100335 result['build_status'] = 'success'
336 result['dex_size'] = dex_size
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100337 result['profile_dest_dir'] = profile_dest_dir
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100338
339 profile = as_utils.ParseProfileReport(profile_dest_dir)
340 result['profile'] = {
341 task_name:duration for task_name, duration in profile.iteritems()
342 if as_utils.IsGradleCompilerTask(task_name, shrinker)}
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100343 except Exception as e:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100344 warn('Failed to build {} with {}'.format(app, shrinker))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100345 if e:
346 print('Error: ' + str(e))
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100347 result['build_status'] = 'failed'
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100348
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100349 if result.get('build_status') == 'success':
350 if options.monkey:
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100351 result['monkey_status'] = 'success' if RunMonkey(
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100352 app, config, options, apk_dest) else 'failed'
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100353
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100354 if 'r8' in shrinker and options.r8_compilation_steps > 1:
355 recompilation_results = []
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100356
357 # Build app with gradle using -D...keepRuleSynthesisForRecompilation=
358 # true.
359 out_dir = os.path.join(checkout_dir, 'out', shrinker + '-1')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100360 (apk_dest, profile_dest_dir, ext_proguard_config_file) = \
361 BuildAppWithShrinker(app, config, shrinker, checkout_dir, out_dir,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100362 temp_dir, options, keepRuleSynthesisForRecompilation=True)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100363 dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
364 recompilation_result = {
365 'apk_dest': apk_dest,
366 'build_status': 'success',
367 'dex_size': ComputeSizeOfDexFilesInApk(apk_dest),
368 'monkey_status': 'skipped'
369 }
370 recompilation_results.append(recompilation_result)
371
372 # Sanity check that keep rules have changed.
373 with open(ext_proguard_config_file) as new:
374 with open(proguard_config_file) as old:
Christoffer Quist Adamsenb16c6702019-02-19 09:58:38 +0100375 assert(
376 sum(1 for line in new
377 if line.strip() and '-printconfiguration' not in line)
378 >
379 sum(1 for line in old
380 if line.strip() and '-printconfiguration' not in line))
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100381
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100382 # Extract min-sdk and target-sdk
383 (min_sdk, compile_sdk) = as_utils.GetMinAndCompileSdk(app, config,
384 checkout_dir, apk_dest)
385
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100386 # Now rebuild generated apk.
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100387 previous_apk = apk_dest
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100388
389 # We may need main dex rules when re-compiling with R8 as standalone.
390 main_dex_rules = None
391 if config.get('main_dex_rules'):
392 main_dex_rules = os.path.join(
393 checkout_dir, config.get('main_dex_rules'))
394
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100395 for i in range(1, options.r8_compilation_steps):
396 try:
397 recompiled_apk_dest = os.path.join(
398 checkout_dir, 'out', shrinker, 'app-release-{}.apk'.format(i))
399 RebuildAppWithShrinker(
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100400 app, previous_apk, recompiled_apk_dest,
401 ext_proguard_config_file, shrinker, min_sdk, compile_sdk,
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100402 options, temp_dir, main_dex_rules)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100403 recompilation_result = {
404 'apk_dest': recompiled_apk_dest,
405 'build_status': 'success',
406 'dex_size': ComputeSizeOfDexFilesInApk(recompiled_apk_dest)
407 }
408 if options.monkey:
409 recompilation_result['monkey_status'] = 'success' if RunMonkey(
410 app, config, options, recompiled_apk_dest) else 'failed'
411 recompilation_results.append(recompilation_result)
412 previous_apk = recompiled_apk_dest
413 except Exception as e:
414 warn('Failed to recompile {} with {}'.format(app, shrinker))
415 recompilation_results.append({ 'build_status': 'failed' })
416 break
417 result['recompilation_results'] = recompilation_results
418
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100419 result_per_shrinker[shrinker] = result
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100420
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100421 if not options.app:
422 print('')
423 LogResultsForApp(app, result_per_shrinker, options)
424 print('')
425
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100426 return result_per_shrinker
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100427
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100428def BuildAppWithShrinker(
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100429 app, config, shrinker, checkout_dir, out_dir, temp_dir, options,
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100430 keepRuleSynthesisForRecompilation=False):
431 print('Building {} with {}{}'.format(
432 app,
433 shrinker,
434 ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100435
Morten Krogh-Jespersende566ea2019-02-18 11:59:48 +0100436 # Add settings.gradle file if it is not present to prevent gradle from finding
437 # the settings.gradle file in the r8 root when apps are placed under
438 # $R8/build.
439 as_utils.add_settings_gradle(checkout_dir, app)
440
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100441 # Add 'r8.jar' to top-level build.gradle.
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100442 as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100443
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100444 app_module = config.get('app_module', 'app')
Morten Krogh-Jespersen47d62ba2019-01-22 08:35:49 +0100445 archives_base_name = config.get('archives_base_name', app_module)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100446 flavor = config.get('flavor')
447
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100448 if not os.path.exists(out_dir):
449 os.makedirs(out_dir)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100450
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100451 # Set -printconfiguration in Proguard rules.
452 proguard_config_dest = os.path.abspath(
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100453 os.path.join(out_dir, 'proguard-rules.pro'))
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100454 as_utils.SetPrintConfigurationDirective(
455 app, config, checkout_dir, proguard_config_dest)
456
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100457 env = {}
Morten Krogh-Jespersen220e5702019-02-27 12:57:01 +0100458 env['ANDROID_HOME'] = utils.getAndroidHome()
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100459 env['JAVA_OPTS'] = '-ea:com.android.tools.r8...'
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100460
Søren Gjesse8c111482018-12-21 14:47:57 +0100461 releaseTarget = config.get('releaseTarget')
462 if not releaseTarget:
Christoffer Quist Adamsenc479a422019-02-07 10:23:50 +0100463 releaseTarget = app_module.replace('/', ':') + ':' + 'assemble' + (
Søren Gjesse8c111482018-12-21 14:47:57 +0100464 flavor.capitalize() if flavor else '') + 'Release'
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100465
Søren Gjesse9fb48802019-01-18 11:00:00 +0100466 # Value for property android.enableR8.
467 enableR8 = 'r8' in shrinker
468 # Value for property android.enableR8.fullMode.
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100469 enableR8FullMode = shrinker == 'r8-full' or shrinker == 'r8-nolib-full'
Søren Gjesse9fb48802019-01-18 11:00:00 +0100470 # Build gradlew command line.
471 cmd = ['./gradlew', '--no-daemon', 'clean', releaseTarget,
472 '--profile', '--stacktrace',
473 '-Pandroid.enableR8=' + str(enableR8).lower(),
474 '-Pandroid.enableR8.fullMode=' + str(enableR8FullMode).lower()]
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100475 if keepRuleSynthesisForRecompilation:
476 cmd.append('-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100477 if options.gradle_flags:
478 cmd.extend(options.gradle_flags.split(' '))
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100479
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100480 stdout = utils.RunCmd(cmd, env, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100481
482 apk_base_name = (archives_base_name
483 + (('-' + flavor) if flavor else '') + '-release')
484 signed_apk_name = config.get('signed-apk-name', apk_base_name + '.apk')
485 unsigned_apk_name = apk_base_name + '-unsigned.apk'
486
487 build_dir = config.get('build_dir', 'build')
488 build_output_apks = os.path.join(app_module, build_dir, 'outputs', 'apk')
489 if flavor:
490 build_output_apks = os.path.join(build_output_apks, flavor, 'release')
491 else:
492 build_output_apks = os.path.join(build_output_apks, 'release')
493
494 signed_apk = os.path.join(build_output_apks, signed_apk_name)
495 unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)
496
497 if options.sign_apks and not os.path.isfile(signed_apk):
498 assert os.path.isfile(unsigned_apk)
499 if options.sign_apks:
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100500 apk_utils.sign_with_apksigner(
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100501 unsigned_apk,
502 signed_apk,
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100503 options.keystore,
504 options.keystore_password,
505 quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100506
507 if os.path.isfile(signed_apk):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100508 apk_dest = os.path.join(out_dir, signed_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100509 as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100510 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100511 apk_dest = os.path.join(out_dir, unsigned_apk_name)
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100512 as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100513
Morten Krogh-Jespersen86f87332019-03-04 09:25:58 +0100514 # TODO(mkroghj) Re-enable this assertion when fix works in Golem.
515 # assert ('r8' not in shrinker
516 # or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
Christoffer Quist Adamsen3aa8d252018-12-13 14:56:40 +0100517
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100518 profile_dest_dir = os.path.join(out_dir, 'profile')
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100519 as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)
Christoffer Quist Adamsen1d87ca62019-01-11 12:22:26 +0100520
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100521 return (apk_dest, profile_dest_dir, proguard_config_dest)
522
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100523def RebuildAppWithShrinker(
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100524 app, apk, apk_dest, proguard_config_file, shrinker, min_sdk, compile_sdk,
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100525 options, temp_dir, main_dex_rules):
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100526 assert 'r8' in shrinker
527 assert apk_dest.endswith('.apk')
Søren Gjesse9fb48802019-01-18 11:00:00 +0100528
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100529 print('Rebuilding {} with {}'.format(app, shrinker))
530
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100531 # Compile given APK with shrinker to temporary zip file.
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100532 android_jar = utils.get_android_jar(compile_sdk)
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100533 r8_jar = os.path.join(
534 temp_dir, 'r8lib.jar' if IsMinifiedR8(shrinker) else 'r8.jar')
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100535 zip_dest = apk_dest[:-4] + '.zip'
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100536
Christoffer Quist Adamsene59840b2019-01-22 15:25:15 +0100537 # TODO(christofferqa): Entry point should be CompatProguard if the shrinker
538 # is 'r8'.
539 entry_point = 'com.android.tools.r8.R8'
540
Christoffer Quist Adamsenbe2593f2019-02-19 13:01:56 +0100541 cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar,
542 entry_point, '--release', '--min-api', str(min_sdk), '--pg-conf',
543 proguard_config_file, '--lib', android_jar, '--output', zip_dest, apk]
Christoffer Quist Adamsen17879c12019-01-22 16:13:54 +0100544
545 for android_optional_jar in utils.get_android_optional_jars(compile_sdk):
546 cmd.append('--lib')
547 cmd.append(android_optional_jar)
548
Morten Krogh-Jespersen03627262019-02-18 14:30:22 +0100549 if main_dex_rules:
550 cmd.append('--main-dex-rules')
551 cmd.append(main_dex_rules)
552
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100553 utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsen4038bbe2019-01-15 14:31:46 +0100554
555 # Make a copy of the given APK, move the newly generated dex files into the
556 # copied APK, and then sign the APK.
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100557 apk_masseur.masseur(
Christoffer Quist Adamsen2dcea102019-02-20 11:31:38 +0100558 apk, dex=zip_dest, resources='META-INF/services/*', out=apk_dest,
559 quiet=options.quiet)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100560
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100561def RunMonkey(app, config, options, apk_dest):
562 if not WaitForEmulator():
563 return False
564
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100565 UninstallApkOnEmulator(app, config, options)
566 InstallApkOnEmulator(apk_dest, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100567
568 app_id = config.get('app_id')
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100569 number_of_events_to_generate = options.monkey_events
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100570
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100571 # Intentionally using a constant seed such that the monkey generates the same
572 # event sequence for each shrinker.
573 random_seed = 42
574
575 cmd = ['adb', 'shell', 'monkey', '-p', app_id, '-s', str(random_seed),
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100576 str(number_of_events_to_generate)]
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100577
578 try:
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100579 stdout = utils.RunCmd(cmd, quiet=options.quiet)
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100580 succeeded = (
581 'Events injected: {}'.format(number_of_events_to_generate) in stdout)
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100582 except subprocess.CalledProcessError as e:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100583 succeeded = False
Christoffer Quist Adamsen88b7ebe2019-01-14 11:22:17 +0100584
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100585 UninstallApkOnEmulator(app, config, options)
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100586
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100587 return succeeded
588
589def LogResultsForApps(result_per_shrinker_per_app, options):
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100590 print('')
591 for app, result_per_shrinker in sorted(
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100592 result_per_shrinker_per_app.iteritems(), key=lambda s: s[0].lower()):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100593 LogResultsForApp(app, result_per_shrinker, options)
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100594
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100595def LogResultsForApp(app, result_per_shrinker, options):
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100596 if options.print_dexsegments:
597 LogSegmentsForApp(app, result_per_shrinker, options)
598 else:
599 LogComparisonResultsForApp(app, result_per_shrinker, options)
600
601def LogSegmentsForApp(app, result_per_shrinker, options):
602 for shrinker in SHRINKERS:
603 if shrinker not in result_per_shrinker:
604 continue
605 result = result_per_shrinker[shrinker];
606 benchmark_name = '{}-{}'.format(options.print_dexsegments, app)
607 utils.print_dexsegments(benchmark_name, [result.get('apk_dest')])
608 duration = sum(result.get('profile').values())
609 print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration * 1000))
610 print('%s-Total(CodeSize): %s' % (benchmark_name, result.get('dex_size')))
611
612
613def LogComparisonResultsForApp(app, result_per_shrinker, options):
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100614 print(app + ':')
615
Christoffer Quist Adamsen1de3dde2019-01-24 13:17:46 +0100616 if result_per_shrinker.get('status', 'success') != 'success':
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100617 error_message = result_per_shrinker.get('error_message')
618 print(' skipped ({})'.format(error_message))
619 return
620
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100621 proguard_result = result_per_shrinker.get('pg', {})
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100622 proguard_dex_size = float(proguard_result.get('dex_size', -1))
623 proguard_duration = sum(proguard_result.get('profile', {}).values())
624
625 for shrinker in SHRINKERS:
626 if shrinker not in result_per_shrinker:
Christoffer Quist Adamsen724bfb02019-01-10 09:54:38 +0100627 continue
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100628 result = result_per_shrinker.get(shrinker)
629 build_status = result.get('build_status')
630 if build_status != 'success':
631 warn(' {}: {}'.format(shrinker, build_status))
632 else:
633 print(' {}:'.format(shrinker))
634 dex_size = result.get('dex_size')
635 msg = ' dex size: {}'.format(dex_size)
636 if dex_size != proguard_dex_size and proguard_dex_size >= 0:
637 msg = '{} ({}, {})'.format(
638 msg, dex_size - proguard_dex_size,
639 PercentageDiffAsString(proguard_dex_size, dex_size))
640 success(msg) if dex_size < proguard_dex_size else warn(msg)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100641 else:
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100642 print(msg)
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100643
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100644 profile = result.get('profile')
645 duration = sum(profile.values())
646 msg = ' performance: {}s'.format(duration)
647 if duration != proguard_duration and proguard_duration > 0:
648 msg = '{} ({}s, {})'.format(
649 msg, duration - proguard_duration,
650 PercentageDiffAsString(proguard_duration, duration))
651 success(msg) if duration < proguard_duration else warn(msg)
652 else:
653 print(msg)
654 if len(profile) >= 2:
655 for task_name, task_duration in profile.iteritems():
656 print(' {}: {}s'.format(task_name, task_duration))
Christoffer Quist Adamsen81321b62019-01-15 14:39:53 +0100657
Christoffer Quist Adamsend0e0a7c2019-01-22 10:00:30 +0100658 if options.monkey:
659 monkey_status = result.get('monkey_status')
660 if monkey_status != 'success':
661 warn(' monkey: {}'.format(monkey_status))
662 else:
663 success(' monkey: {}'.format(monkey_status))
664 recompilation_results = result.get('recompilation_results', [])
665 i = 0
666 for recompilation_result in recompilation_results:
667 build_status = recompilation_result.get('build_status')
668 if build_status != 'success':
669 print(' recompilation #{}: {}'.format(i, build_status))
670 else:
671 dex_size = recompilation_result.get('dex_size')
672 print(' recompilation #{}'.format(i))
673 print(' dex size: {}'.format(dex_size))
674 if options.monkey:
675 monkey_status = recompilation_result.get('monkey_status')
676 msg = ' monkey: {}'.format(monkey_status)
677 if monkey_status == 'success':
678 success(msg)
679 elif monkey_status == 'skipped':
680 print(msg)
681 else:
682 warn(msg)
683 i += 1
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100684
Søren Gjessecdae8792018-12-12 09:02:43 +0100685def ParseOptions(argv):
686 result = optparse.OptionParser()
687 result.add_option('--app',
688 help='What app to run on',
689 choices=APPS.keys())
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100690 result.add_option('--download-only', '--download_only',
691 help='Whether to download apps without any compilation',
692 default=False,
693 action='store_true')
694 result.add_option('--golem',
695 help='Running on golem, do not download',
696 default=False,
697 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100698 result.add_option('--gradle-flags', '--gradle_flags',
699 help='Flags to pass in to gradle')
Morten Krogh-Jespersene0ce6a32019-02-07 11:44:45 +0100700 result.add_option('--ignore-versions', '--ignore_versions',
701 help='Allow checked-out app to differ in revision from '
702 'pinned',
703 default=False,
704 action='store_true')
Christoffer Quist Adamsen3b6f1062019-02-07 09:49:43 +0100705 result.add_option('--keystore',
706 help='Path to app.keystore',
707 default='app.keystore')
708 result.add_option('--keystore-password', '--keystore_password',
709 help='Password for app.keystore',
710 default='android')
Christoffer Quist Adamsen1d0a0fe2018-12-21 14:28:56 +0100711 result.add_option('--monkey',
712 help='Whether to install and run app(s) with monkey',
713 default=False,
714 action='store_true')
Christoffer Quist Adamsenbae36612019-01-15 12:23:51 +0100715 result.add_option('--monkey_events',
716 help='Number of events that the monkey should trigger',
717 default=250,
718 type=int)
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100719 result.add_option('--no-build', '--no_build',
720 help='Run without building ToT first (only when using ToT)',
721 default=False,
722 action='store_true')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100723 result.add_option('--quiet',
724 help='Disable verbose logging',
725 default=False,
726 action='store_true')
Morten Krogh-Jespersenaeb665e2019-02-07 12:29:03 +0100727 result.add_option('--print-dexsegments',
728 metavar='BENCHMARKNAME',
729 help='Print the sizes of individual dex segments as ' +
730 '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
731 '<bytes>\'')
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100732 result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
733 help='Number of times R8 should be run on each app',
734 default=2,
735 type=int)
Søren Gjesseeed839d2019-01-11 15:19:16 +0100736 result.add_option('--sign-apks', '--sign_apks',
Christoffer Quist Adamsen10b7db82018-12-13 14:50:38 +0100737 help='Whether the APKs should be signed',
738 default=False,
739 action='store_true')
740 result.add_option('--shrinker',
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100741 help='The shrinkers to use (by default, all are run)',
742 action='append')
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100743 result.add_option('--version',
744 help='The version of R8 to use (e.g., 1.4.51)')
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100745 (options, args) = result.parse_args(argv)
746 if options.shrinker:
747 for shrinker in options.shrinker:
748 assert shrinker in SHRINKERS
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100749 else:
750 options.shrinker = [shrinker for shrinker in SHRINKERS]
751 if options.version:
752 # No need to build R8 if a specific release version should be used.
753 options.no_build = True
754 if 'r8-nolib' in options.shrinker:
755 warn('Skipping shrinker r8-nolib because a specific release version '
756 + 'of r8 was specified')
757 options.shrinker.remove('r8-nolib')
758 if 'r8-nolib-full' in options.shrinker:
759 warn('Skipping shrinker r8-nolib-full because a specific release version '
760 + 'of r8 was specified')
761 options.shrinker.remove('r8-nolib-full')
Christoffer Quist Adamsen860fa932019-01-10 14:27:39 +0100762 return (options, args)
Søren Gjessecdae8792018-12-12 09:02:43 +0100763
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100764def download_apps(quiet):
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100765 # Download apps and place in build
766 with utils.ChangedWorkingDirectory(WORKING_DIR):
767 for app, config in APPS.iteritems():
768 app_dir = os.path.join(WORKING_DIR, app)
769 if not os.path.exists(app_dir):
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100770 GitClone(config['git_repo'], config['revision'], app_dir, quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100771
772
Søren Gjessecdae8792018-12-12 09:02:43 +0100773def main(argv):
774 (options, args) = ParseOptions(argv)
Christoffer Quist Adamsenf8ad4792019-01-09 13:19:19 +0100775
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100776 if options.golem:
Morten Krogh-Jespersenc2fbde22019-02-07 13:59:55 +0100777 golem.link_third_party()
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100778 if os.path.exists(WORKING_DIR):
779 shutil.rmtree(WORKING_DIR)
780 shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
Morten Krogh-Jespersen220e5702019-02-27 12:57:01 +0100781 os.environ[utils.ANDROID_HOME_ENVIROMENT_NAME] = os.path.join(
782 utils.ANDROID_SDK)
783 os.environ[utils.ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME] = '28.0.3'
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100784
785 if not os.path.exists(WORKING_DIR):
786 os.makedirs(WORKING_DIR)
787
788 if options.download_only:
Morten Krogh-Jespersen54090862019-02-19 11:31:10 +0100789 download_apps(options.quiet)
Morten Krogh-Jespersen64740202019-02-07 13:06:04 +0100790 return
791
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100792 with utils.TempDir() as temp_dir:
Morten Krogh-Jespersen2bd5ec82019-02-25 16:01:23 +0100793 if not (options.no_build or options.golem):
Morten Krogh-Jespersend45d95e2019-02-07 13:27:17 +0100794 gradle.RunGradle(['r8', 'r8lib'])
Søren Gjessecdae8792018-12-12 09:02:43 +0100795
Christoffer Quist Adamsen2a902752019-02-19 12:33:48 +0100796 if options.version:
797 # Download r8-<version>.jar from
798 # http://storage.googleapis.com/r8-releases/raw/.
799 target = 'r8-{}.jar'.format(options.version)
800 update_prebuilds_in_android.download_version(
801 temp_dir, 'com/android/tools/r8/' + options.version, target)
802 as_utils.MoveFile(
803 os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
804 quiet=options.quiet)
805 else:
806 # Make a copy of r8.jar and r8lib.jar such that they stay the same for
807 # the entire execution of this script.
808 if 'r8-nolib' in options.shrinker:
809 assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
810 shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
811 if 'r8' in options.shrinker:
812 assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
813 shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100814
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100815 result_per_shrinker_per_app = {}
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100816
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100817 if options.app:
818 result_per_shrinker_per_app[options.app] = GetResultsForApp(
819 options.app, APPS.get(options.app), options, temp_dir)
820 else:
Morten Krogh-Jespersend35065e2019-02-07 10:28:52 +0100821 for app, config in sorted(APPS.iteritems(), key=lambda s: s[0].lower()):
Christoffer Quist Adamsenbbe5d7d2019-01-23 13:15:21 +0100822 if not config.get('skip', False):
823 result_per_shrinker_per_app[app] = GetResultsForApp(
824 app, config, options, temp_dir)
825
826 LogResultsForApps(result_per_shrinker_per_app, options)
Christoffer Quist Adamsen404aade2018-12-20 13:00:09 +0100827
828def success(message):
829 CGREEN = '\033[32m'
830 CEND = '\033[0m'
831 print(CGREEN + message + CEND)
832
833def warn(message):
834 CRED = '\033[91m'
835 CEND = '\033[0m'
836 print(CRED + message + CEND)
Søren Gjessecdae8792018-12-12 09:02:43 +0100837
838if __name__ == '__main__':
839 sys.exit(main(sys.argv[1:]))