blob: 9ca17ace8bd4f78b3581a86171e712bf88547c6b [file] [log] [blame]
Rico Wind3d369b42021-01-12 10:26:24 +01001#!/usr/bin/env python3
Christoffer Quist Adamsenf2e8db72020-11-07 14:09:49 +01002# Copyright (c) 2020, 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.
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01005
Ian Zerny161ff742022-01-20 12:39:40 +01006import argparse
7import hashlib
8import os
9import shutil
10import sys
11import time
12import zipfile
13from datetime import datetime
14
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +010015import adb
16import apk_masseur
Christoffer Quist Adamsenf79f1a22021-01-12 16:24:34 +010017import as_utils
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010018import compiledump
19import gradle
Rico Wind61a034f2021-03-16 09:37:11 +010020import jdk
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +020021import thread_utils
22from thread_utils import print_thread
Morten Krogh-Jespersen47764032020-11-11 15:09:39 +010023import update_prebuilds_in_android
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010024import utils
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010025
Ian Zernyed1f8512023-11-07 11:07:57 +010026GOLEM_BUILD_TARGETS = [utils.GRADLE_TASK_R8LIB]
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010027SHRINKERS = ['r8', 'r8-full', 'r8-nolib', 'r8-nolib-full']
28
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020029
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010030class AttrDict(dict):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020031
32 def __getattr__(self, name):
33 return self.get(name, None)
34
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010035
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010036# To generate the files for a new app, navigate to the app source folder and
37# run:
38# ./gradlew clean :app:assembleRelease -Dcom.android.tools.r8.dumpinputtodirectory=<path>
39# and store the dump and the apk.
40# If the app has instrumented tests, adding `testBuildType "release"` and
41# running:
42# ./gradlew assembleAndroidTest -Dcom.android.tools.r8.dumpinputtodirectory=<path>
43# will also generate dumps and apk for tests.
44
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020045
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010046class App(object):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020047
48 def __init__(self, fields):
49 defaults = {
50 'id': None,
51 'name': None,
52 'collections': [],
53 'dump_app': None,
54 'apk_app': None,
55 'dump_test': None,
56 'apk_test': None,
57 'skip': False,
58 'url': None, # url is not used but nice to have for updating apps
59 'revision': None,
60 'folder': None,
61 'skip_recompilation': False,
62 'compiler_properties': [],
63 'internal': False,
64 'golem_duration': None,
65 }
66 # This below does not work in python3
67 defaults.update(fields.items())
68 self.__dict__ = defaults
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010069
70
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +010071class AppCollection(object):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020072
73 def __init__(self, fields):
74 defaults = {'name': None}
75 # This below does not work in python3
76 defaults.update(fields.items())
77 self.__dict__ = defaults
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +010078
79
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010080APPS = [
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020081 App({
82 'id':
83 'com.numix.calculator',
84 'name':
85 'Calculator',
86 'dump_app':
87 'dump_app.zip',
88 'apk_app':
89 'app-release.apk',
90 # Compiling tests fail: Library class android.content.res.XmlResourceParser
91 # implements program class org.xmlpull.v1.XmlPullParser. Nothing to really
92 # do about that.
93 'id_test':
94 'com.numix.calculator.test',
95 'dump_test':
96 'dump_test.zip',
97 'apk_test':
98 'app-release-androidTest.apk',
99 'url':
100 'https://github.com/numixproject/android-suite/tree/master/Calculator',
101 'revision':
102 'f58e1b53f7278c9b675d5855842c6d8a44cccb1f',
103 'folder':
104 'android-suite-calculator',
105 }),
106 App({
107 'id': 'dev.dworks.apps.anexplorer.pro',
108 'name': 'AnExplorer',
109 'dump_app': 'dump_app.zip',
110 'apk_app': 'AnExplorer-googleMobileProRelease-4.0.3.apk',
111 'url': 'https://github.com/christofferqa/AnExplorer',
112 'revision': '365927477b8eab4052a1882d5e358057ae3dee4d',
113 'folder': 'anexplorer',
114 }),
115 App({
116 'id': 'de.danoeh.antennapod',
117 'name': 'AntennaPod',
118 'dump_app': 'dump_app.zip',
119 'apk_app': 'app-free-release.apk',
120 # TODO(b/172452102): Tests and monkey do not work
121 'id_test': 'de.danoeh.antennapod.test',
122 'dump_test': 'dump_test.zip',
123 'apk_test': 'app-free-release-androidTest.apk',
124 'url': 'https://github.com/christofferqa/AntennaPod.git',
125 'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
126 'folder': 'antennapod',
127 }),
128 App({
129 'id': 'com.example.applymapping',
130 'name': 'applymapping',
131 'dump_app': 'dump_app.zip',
132 'apk_app': 'app-release.apk',
133 'id_test': 'com.example.applymapping.test',
134 'dump_test': 'dump_test.zip',
135 'apk_test': 'app-release-androidTest.apk',
136 'url': 'https://github.com/mkj-gram/applymapping',
137 'revision': 'e3ae14b8c16fa4718e5dea8f7ad00937701b3c48',
138 'folder': 'applymapping',
139 }),
140 App({
141 'id':
142 'com.chanapps.four.activity',
143 'name':
144 'chanu',
145 'dump_app':
146 'dump_app.zip',
147 'apk_app':
148 'app-release.apk',
149 'url':
150 'https://github.com/mkj-gram/chanu.git',
151 'revision':
152 '6e53458f167b6d78398da60c20fd0da01a232617',
153 'folder':
154 'chanu',
155 # The app depends on a class file that has access flags interface but
156 # not abstract
157 'compiler_properties': [
158 '-Dcom.android.tools.r8.allowInvalidCfAccessFlags=true'
159 ]
160 }),
161 App({
162 'id': 'com.example.myapplication',
163 'name': 'empty-activity',
164 'dump_app': 'dump_app.zip',
165 'apk_app': 'app-release.apk',
166 'url': 'https://github.com/christofferqa/empty_android_activity.git',
167 'revision': '2d297ec3373dadb03cbae916b9feba4792563156',
168 'folder': 'empty-activity',
169 }),
170 App({
171 'id':
172 'com.example.emptycomposeactivity',
173 'name':
174 'empty-compose-activity',
175 'dump_app':
176 'dump_app.zip',
177 'apk_app':
178 'app-release.apk',
179 'url':
180 'https://github.com/christofferqa/empty_android_compose_activity.git',
181 'revision':
182 '3c8111b8b7d6e9184049a07e2b96702d7b33d03e',
183 'folder':
184 'empty-compose-activity',
185 }),
186 # TODO(b/172539375): Monkey runner fails on recompilation.
187 App({
188 'id': 'com.google.firebase.example.fireeats',
189 'name': 'FriendlyEats',
190 'dump_app': 'dump_app.zip',
191 'apk_app': 'app-release-unsigned.apk',
192 'url': 'https://github.com/firebase/friendlyeats-android',
193 'revision': '7c6dd016fc31ea5ecb948d5166b8479efc3775cc',
194 'folder': 'friendlyeats',
195 }),
196 App({
197 'id': 'com.google.samples.apps.sunflower',
198 'name': 'Sunflower',
199 'dump_app': 'dump_app.zip',
200 'apk_app': 'app-debug.apk',
201 # TODO(b/172549283): Compiling tests fails
202 'id_test': 'com.google.samples.apps.sunflower.test',
203 'dump_test': 'dump_test.zip',
204 'apk_test': 'app-debug-androidTest.apk',
205 'url': 'https://github.com/android/sunflower',
206 'revision': '0c4c88fdad2a74791199dffd1a6559559b1dbd4a',
207 'folder': 'sunflower',
208 }),
209 # TODO(b/172565385): Monkey runner fails on recompilation
210 App({
211 'id': 'com.google.samples.apps.iosched',
212 'name': 'iosched',
213 'dump_app': 'dump_app.zip',
214 'apk_app': 'mobile-release.apk',
215 'url': 'https://github.com/christofferqa/iosched.git',
216 'revision': '581cbbe2253711775dbccb753cdb53e7e506cb02',
217 'folder': 'iosched',
218 }),
219 App({
220 'id': 'fr.neamar.kiss',
221 'name': 'KISS',
222 'dump_app': 'dump_app.zip',
223 'apk_app': 'app-release.apk',
224 # TODO(b/172569220): Running tests fails due to missing keep rules
225 'id_test': 'fr.neamar.kiss.test',
226 'dump_test': 'dump_test.zip',
227 'apk_test': 'app-release-androidTest.apk',
228 'url': 'https://github.com/Neamar/KISS',
229 'revision': '8ccffaadaf0d0b8fc4418ed2b4281a0935d3d971',
230 'folder': 'kiss',
231 }),
232 # TODO(b/172577344): Monkey runner not working.
233 App({
234 'id': 'io.github.hidroh.materialistic',
235 'name': 'materialistic',
236 'dump_app': 'dump_app.zip',
237 'apk_app': 'app-release.apk',
238 'url': 'https://github.com/christofferqa/materialistic.git',
239 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
240 'folder': 'materialistic',
241 }),
242 App({
243 'id': 'com.avjindersinghsekhon.minimaltodo',
244 'name': 'MinimalTodo',
245 'dump_app': 'dump_app.zip',
246 'apk_app': 'app-release.apk',
247 'url': 'https://github.com/christofferqa/Minimal-Todo',
248 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
249 'folder': 'minimal-todo',
250 }),
251 App({
252 'id': 'net.nurik.roman.muzei',
253 'name': 'muzei',
254 'dump_app': 'dump_app.zip',
255 'apk_app': 'muzei-release.apk',
256 'url': 'https://github.com/romannurik/muzei',
257 'revision': '9eac6e98aebeaf0ae40bdcd85f16dd2886551138',
258 'folder': 'muzei',
259 }),
260 # TODO(b/172806281): Monkey runner does not work.
261 App({
262 'id': 'org.schabi.newpipe',
263 'name': 'NewPipe',
264 'dump_app': 'dump_app.zip',
265 'apk_app': 'app-release-unsigned.apk',
266 'url': 'https://github.com/TeamNewPipe/NewPipe',
267 'revision': 'f4435f90313281beece70c544032f784418d85fa',
268 'folder': 'newpipe',
269 }),
270 # TODO(b/172806808): Monkey runner does not work.
271 App({
272 'id': 'io.rover.app.debug',
273 'name': 'Rover',
274 'dump_app': 'dump_app.zip',
275 'apk_app': 'example-app-release-unsigned.apk',
276 'url': 'https://github.com/RoverPlatform/rover-android',
277 'revision': '94342117097770ea3ca2c6df6ab496a1a55c3ce7',
278 'folder': 'rover-android',
279 }),
280 # TODO(b/172808159): Monkey runner does not work
281 App({
282 'id': 'com.google.android.apps.santatracker',
283 'name': 'SantaTracker',
284 'dump_app': 'dump_app.zip',
285 'apk_app': 'santa-tracker-release.apk',
286 'url': 'https://github.com/christofferqa/santa-tracker-android',
287 'revision': '8dee74be7d9ee33c69465a07088c53087d24a6dd',
288 'folder': 'santa-tracker',
289 }),
290 App({
291 'id': 'org.thoughtcrime.securesms',
292 'name': 'Signal',
293 'dump_app': 'dump_app.zip',
294 'apk_app': 'Signal-Android-play-prod-universal-release-4.76.2.apk',
295 # TODO(b/172812839): Instrumentation test fails.
296 'id_test': 'org.thoughtcrime.securesms.test',
297 'dump_test': 'dump_test.zip',
298 'apk_test': 'Signal-Android-play-prod-release-androidTest.apk',
299 'url': 'https://github.com/signalapp/Signal-Android',
300 'revision': '91ca19f294362ccee2c2b43c247eba228e2b30a1',
301 'folder': 'signal-android',
Rico Wind685d48e2024-01-18 08:39:48 +0100302 'golem_duration': 300
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200303 }),
304 # TODO(b/172815827): Monkey runner does not work
305 App({
306 'id': 'com.simplemobiletools.calendar.pro',
307 'name': 'Simple-Calendar',
308 'dump_app': 'dump_app.zip',
309 'apk_app': 'calendar-release.apk',
310 'url': 'https://github.com/SimpleMobileTools/Simple-Calendar',
311 'revision': '906209874d0a091c7fce5a57972472f272d6b068',
312 'folder': 'simple-calendar',
313 }),
314 # TODO(b/172815534): Monkey runner does not work
315 App({
316 'id': 'com.simplemobiletools.camera.pro',
317 'name': 'Simple-Camera',
318 'dump_app': 'dump_app.zip',
319 'apk_app': 'camera-release.apk',
320 'url': 'https://github.com/SimpleMobileTools/Simple-Camera',
321 'revision': 'ebf9820c51e960912b3238287e30a131244fdee6',
322 'folder': 'simple-camera',
323 }),
324 App({
325 'id': 'com.simplemobiletools.filemanager.pro',
326 'name': 'Simple-File-Manager',
327 'dump_app': 'dump_app.zip',
328 'apk_app': 'file-manager-release.apk',
329 'url': 'https://github.com/SimpleMobileTools/Simple-File-Manager',
330 'revision': '2b7fa68ea251222cc40cf6d62ad1de260a6f54d9',
331 'folder': 'simple-file-manager',
332 }),
333 App({
334 'id': 'com.simplemobiletools.gallery.pro',
335 'name': 'Simple-Gallery',
336 'dump_app': 'dump_app.zip',
337 'apk_app': 'gallery-326-foss-release.apk',
338 'url': 'https://github.com/SimpleMobileTools/Simple-Gallery',
339 'revision': '564e56b20d33b28d0018c8087ec705beeb60785e',
340 'folder': 'simple-gallery',
341 }),
342 App({
343 'id': 'com.example.sqldelight.hockey',
344 'name': 'SQLDelight',
345 'dump_app': 'dump_app.zip',
346 'apk_app': 'android-release.apk',
347 'url': 'https://github.com/christofferqa/sqldelight',
348 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
349 'folder': 'sqldelight',
350 }),
351 # TODO(b/172824096): Monkey runner does not work.
352 App({
353 'id': 'eu.kanade.tachiyomi',
354 'name': 'Tachiyomi',
355 'dump_app': 'dump_app.zip',
356 'apk_app': 'app-dev-release.apk',
357 'url': 'https://github.com/inorichi/tachiyomi',
358 'revision': '8aa6486bf76ab9a61a5494bee284b1a5e9180bf3',
359 'folder': 'tachiyomi',
360 }),
361 # TODO(b/172862042): Monkey runner does not work.
362 App({
363 'id': 'app.tivi',
364 'name': 'Tivi',
365 'dump_app': 'dump_app.zip',
366 'apk_app': 'app-release.apk',
367 'url': 'https://github.com/chrisbanes/tivi',
368 'revision': '5c6d9ed338885c59b1fc64050d92d056417bb4de',
369 'folder': 'tivi',
370 'golem_duration': 300
371 }),
372 App({
373 'id': 'com.keylesspalace.tusky',
374 'name': 'Tusky',
375 'dump_app': 'dump_app.zip',
376 'apk_app': 'app-blue-release.apk',
377 'url': 'https://github.com/tuskyapp/Tusky',
378 'revision': '814a9b8f9bacf8d26f712b06a0313a3534a2be95',
379 'folder': 'tusky',
380 }),
381 App({
382 'id': 'org.wikipedia',
383 'name': 'Wikipedia',
384 'dump_app': 'dump_app.zip',
385 'apk_app': 'app-prod-release.apk',
386 'url': 'https://github.com/wikimedia/apps-android-wikipedia',
387 'revision': '0fa7cad843c66313be8e25790ef084cf1a1fa67e',
388 'folder': 'wikipedia',
389 }),
390 # TODO(b/173167253): Check if monkey testing works.
391 App({
392 'id': 'androidx.compose.samples.crane',
393 'name': 'compose-crane',
394 'collections': ['compose-samples'],
395 'dump_app': 'dump_app.zip',
396 'apk_app': 'app-release-unsigned.apk',
397 'url': 'https://github.com/android/compose-samples',
398 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
399 'folder': 'android/compose-samples/crane',
400 'golem_duration': 240
401 }),
402 # TODO(b/173167253): Check if monkey testing works.
403 App({
404 'id': 'com.example.jetcaster',
405 'name': 'compose-jetcaster',
406 'collections': ['compose-samples'],
407 'dump_app': 'dump_app.zip',
408 'apk_app': 'app-release-unsigned.apk',
409 'url': 'https://github.com/android/compose-samples',
410 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
411 'folder': 'android/compose-samples/jetcaster',
412 }),
413 # TODO(b/173167253): Check if monkey testing works.
414 App({
415 'id': 'com.example.compose.jetchat',
416 'name': 'compose-jetchat',
417 'collections': ['compose-samples'],
418 'dump_app': 'dump_app.zip',
419 'apk_app': 'app-release-unsigned.apk',
420 'url': 'https://github.com/android/compose-samples',
421 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
422 'folder': 'android/compose-samples/jetchat',
423 }),
424 # TODO(b/173167253): Check if monkey testing works.
425 App({
426 'id': 'com.example.jetnews',
427 'name': 'compose-jetnews',
428 'collections': ['compose-samples'],
429 'dump_app': 'dump_app.zip',
430 'apk_app': 'app-release-unsigned.apk',
431 'url': 'https://github.com/android/compose-samples',
432 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
433 'folder': 'android/compose-samples/jetnews',
434 }),
435 # TODO(b/173167253): Check if monkey testing works.
436 App({
437 'id': 'com.example.jetsnack',
438 'name': 'compose-jetsnack',
439 'collections': ['compose-samples'],
440 'dump_app': 'dump_app.zip',
441 'apk_app': 'app-release-unsigned.apk',
442 'url': 'https://github.com/android/compose-samples',
443 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
444 'folder': 'android/compose-samples/jetsnack',
445 }),
446 # TODO(b/173167253): Check if monkey testing works.
447 App({
448 'id': 'com.example.compose.jetsurvey',
449 'name': 'compose-jetsurvey',
450 'collections': ['compose-samples'],
451 'dump_app': 'dump_app.zip',
452 'apk_app': 'app-release-unsigned.apk',
453 'url': 'https://github.com/android/compose-samples',
454 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
455 'folder': 'android/compose-samples/jetsurvey',
456 }),
457 # TODO(b/173167253): Check if monkey testing works.
458 App({
459 'id': 'com.example.owl',
460 'name': 'compose-owl',
461 'collections': ['compose-samples'],
462 'dump_app': 'dump_app.zip',
463 'apk_app': 'app-release-unsigned.apk',
464 'url': 'https://github.com/android/compose-samples',
465 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
466 'folder': 'android/compose-samples/owl',
467 }),
468 # TODO(b/173167253): Check if monkey testing works.
469 App({
470 'id': 'com.example.compose.rally',
471 'name': 'compose-rally',
472 'collections': ['compose-samples'],
473 'dump_app': 'dump_app.zip',
474 'apk_app': 'app-release-unsigned.apk',
475 'url': 'https://github.com/android/compose-samples',
476 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
477 'folder': 'android/compose-samples/rally',
478 }),
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +0100479]
480
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200481APP_COLLECTIONS = [AppCollection({
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +0100482 'name': 'compose-samples',
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200483})]
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100484
Morten Krogh-Jespersenf8e77032020-11-09 23:44:58 +0100485
Morten Krogh-Jespersendfeb0e32020-11-04 14:55:55 +0100486def remove_print_lines(file):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200487 with open(file) as f:
488 lines = f.readlines()
489 with open(file, 'w') as f:
490 for line in lines:
491 if '-printconfiguration' not in line:
492 f.write(line)
Morten Krogh-Jespersendfeb0e32020-11-04 14:55:55 +0100493
494
Rico Wind61a034f2021-03-16 09:37:11 +0100495def download_sha(app_sha, internal, quiet=False):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200496 if internal:
497 utils.DownloadFromX20(app_sha)
498 else:
499 utils.DownloadFromGoogleCloudStorage(app_sha, quiet=quiet)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100500
501
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +0100502def is_logging_enabled_for(app, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200503 if options.no_logging:
504 return False
505 if options.app_logging_filter and app.name not in options.app_logging_filter:
506 return False
507 return True
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +0100508
509
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100510def is_minified_r8(shrinker):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200511 return '-nolib' not in shrinker
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100512
513
514def is_full_r8(shrinker):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200515 return '-full' in shrinker
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100516
517
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100518def version_is_built_jar(version):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200519 return version != 'main' and version != 'source'
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100520
521
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100522def compute_size_of_dex_files_in_package(path):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200523 dex_size = 0
524 z = zipfile.ZipFile(path, 'r')
525 for filename in z.namelist():
526 if filename.endswith('.dex'):
527 dex_size += z.getinfo(filename).file_size
528 return dex_size
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100529
530
531def dump_for_app(app_dir, app):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200532 return os.path.join(app_dir, app.dump_app)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100533
534
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100535def dump_test_for_app(app_dir, app):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200536 return os.path.join(app_dir, app.dump_test)
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100537
538
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100539def get_r8_jar(options, temp_dir, shrinker):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200540 if (options.version == 'source'):
541 return None
542 jar = os.path.abspath(
543 os.path.join(temp_dir, '..',
544 'r8lib.jar' if is_minified_r8(shrinker) else 'r8.jar'))
545 return jar
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100546
547
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200548def get_results_for_app(app, options, temp_dir, worker_id):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200549 app_folder = app.folder if app.folder else app.name + "_" + app.revision
550 # Golem extraction will extract to the basename under the benchmarks dir.
551 app_location = os.path.basename(app_folder) if options.golem else app_folder
552 opensource_basedir = (os.path.join('benchmarks', app.name)
553 if options.golem else utils.OPENSOURCE_DUMPS_DIR)
554 app_dir = (os.path.join(utils.INTERNAL_DUMPS_DIR, app_location) if
555 app.internal else os.path.join(opensource_basedir, app_location))
556 if not os.path.exists(app_dir) and not options.golem:
557 # Download the app from google storage.
558 download_sha(app_dir + ".tar.gz.sha1", app.internal)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100559
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200560 # Ensure that the dumps are in place
561 assert os.path.isfile(dump_for_app(app_dir, app)), "Could not find dump " \
562 "for app " + app.name
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100563
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200564 result = {}
565 result['status'] = 'success'
566 result_per_shrinker = build_app_with_shrinkers(app,
567 options,
568 temp_dir,
569 app_dir,
570 worker_id=worker_id)
571 for shrinker, shrinker_result in result_per_shrinker.items():
572 result[shrinker] = shrinker_result
573 return result
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100574
575
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200576def build_app_with_shrinkers(app, options, temp_dir, app_dir, worker_id):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200577 result_per_shrinker = {}
578 for shrinker in options.shrinker:
579 results = []
580 build_app_and_run_with_shrinker(app,
581 options,
582 temp_dir,
583 app_dir,
584 shrinker,
585 results,
586 worker_id=worker_id)
587 result_per_shrinker[shrinker] = results
588 if len(options.apps) > 1:
589 print_thread('', worker_id)
590 log_results_for_app(app,
591 result_per_shrinker,
592 options,
593 worker_id=worker_id)
594 print_thread('', worker_id)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100595
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200596 return result_per_shrinker
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100597
598
599def is_last_build(index, compilation_steps):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200600 return index == compilation_steps - 1
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100601
602
603def build_app_and_run_with_shrinker(app, options, temp_dir, app_dir, shrinker,
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200604 results, worker_id):
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200605 print_thread(
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200606 '[{}] Building {} with {}'.format(datetime.now().strftime("%H:%M:%S"),
607 app.name, shrinker), worker_id)
608 print_thread(
609 'To compile locally: '
610 'tools/run_on_app_dump.py --shrinker {} --r8-compilation-steps {} '
611 '--app {} --minify {} --optimize {} --shrink {}'.format(
612 shrinker, options.r8_compilation_steps, app.name, options.minify,
613 options.optimize, options.shrink), worker_id)
614 print_thread(
615 'HINT: use --shrinker r8-nolib --no-build if you have a local R8.jar',
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200616 worker_id)
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200617 recomp_jar = None
618 status = 'success'
619 if options.r8_compilation_steps < 1:
620 return
621 compilation_steps = 1 if app.skip_recompilation else options.r8_compilation_steps
622 for compilation_step in range(0, compilation_steps):
623 if status != 'success':
624 break
625 print_thread(
626 'Compiling {} of {}'.format(compilation_step + 1,
627 compilation_steps), worker_id)
628 result = {}
629 try:
630 start = time.time()
631 (app_jar, mapping, new_recomp_jar) = \
632 build_app_with_shrinker(
633 app, options, temp_dir, app_dir, shrinker, compilation_step,
634 compilation_steps, recomp_jar, worker_id=worker_id)
635 end = time.time()
636 dex_size = compute_size_of_dex_files_in_package(app_jar)
637 result['build_status'] = 'success'
638 result['recompilation_status'] = 'success'
639 result['output_jar'] = app_jar
640 result['output_mapping'] = mapping
641 result['dex_size'] = dex_size
642 result['duration'] = int((end - start) * 1000) # Wall time
643 if (new_recomp_jar is None and
644 not is_last_build(compilation_step, compilation_steps)):
645 result['recompilation_status'] = 'failed'
646 warn('Failed to build {} with {}'.format(app.name, shrinker))
647 recomp_jar = new_recomp_jar
648 except Exception as e:
649 warn('Failed to build {} with {}'.format(app.name, shrinker))
650 if e:
651 print_thread('Error: ' + str(e), worker_id)
652 result['build_status'] = 'failed'
653 status = 'failed'
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100654
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200655 original_app_apk = os.path.join(app_dir, app.apk_app)
656 app_apk_destination = os.path.join(
657 temp_dir, "{}_{}.apk".format(app.id, compilation_step))
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100658
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200659 if result.get('build_status') == 'success' and options.monkey:
660 # Make a copy of the given APK, move the newly generated dex files into the
661 # copied APK, and then sign the APK.
662 apk_masseur.masseur(original_app_apk,
663 dex=app_jar,
664 resources='META-INF/services/*',
665 out=app_apk_destination,
666 quiet=options.quiet,
667 logging=is_logging_enabled_for(app, options),
668 keystore=options.keystore)
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +0100669
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200670 result['monkey_status'] = 'success' if adb.run_monkey(
671 app.id, options.emulator_id, app_apk_destination,
672 options.monkey_events, options.quiet,
673 is_logging_enabled_for(app, options)) else 'failed'
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +0100674
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200675 if (result.get('build_status') == 'success' and options.run_tests and
676 app.dump_test):
677 if not os.path.isfile(app_apk_destination):
678 apk_masseur.masseur(original_app_apk,
679 dex=app_jar,
680 resources='META-INF/services/*',
681 out=app_apk_destination,
682 quiet=options.quiet,
683 logging=is_logging_enabled_for(
684 app, options),
685 keystore=options.keystore)
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100686
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200687 # Compile the tests with the mapping file.
688 test_jar = build_test_with_shrinker(app, options, temp_dir, app_dir,
689 shrinker, compilation_step,
690 result['output_mapping'])
691 if not test_jar:
692 result['instrumentation_test_status'] = 'compilation_failed'
693 else:
694 original_test_apk = os.path.join(app_dir, app.apk_test)
695 test_apk_destination = os.path.join(
696 temp_dir, "{}_{}.test.apk".format(app.id_test,
697 compilation_step))
698 apk_masseur.masseur(original_test_apk,
699 dex=test_jar,
700 resources='META-INF/services/*',
701 out=test_apk_destination,
702 quiet=options.quiet,
703 logging=is_logging_enabled_for(
704 app, options),
705 keystore=options.keystore)
706 result[
707 'instrumentation_test_status'] = 'success' if adb.run_instrumented(
708 app.id, app.id_test, options.emulator_id,
709 app_apk_destination,
710 test_apk_destination, options.quiet,
711 is_logging_enabled_for(app, options)) else 'failed'
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100712
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200713 results.append(result)
714 if result.get('recompilation_status') != 'success':
715 break
716
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100717
Rico Wind33936d12021-04-07 08:04:14 +0200718def get_jdk_home(options, app):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200719 if options.golem:
720 return os.path.join('benchmarks', app.name, 'linux')
721 return None
722
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100723
724def build_app_with_shrinker(app, options, temp_dir, app_dir, shrinker,
725 compilation_step_index, compilation_steps,
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200726 prev_recomp_jar, worker_id):
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100727
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200728 def config_files_consumer(files):
729 for file in files:
730 compiledump.clean_config(file, options)
731 remove_print_lines(file)
Morten Krogh-Jespersenc9d23462020-11-12 15:36:57 +0100732
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200733 args = AttrDict({
734 'dump': dump_for_app(app_dir, app),
735 'r8_jar': get_r8_jar(options, temp_dir, shrinker),
736 'r8_flags': options.r8_flags,
737 'disable_assertions': options.disable_assertions,
738 'version': options.version,
739 'compiler': 'r8full' if is_full_r8(shrinker) else 'r8',
740 'debug_agent': options.debug_agent,
741 'program_jar': prev_recomp_jar,
742 'nolib': not is_minified_r8(shrinker),
743 'config_files_consumer': config_files_consumer,
744 'properties': app.compiler_properties,
745 'disable_desugared_lib': False,
746 'print_times': options.print_times,
Søren Gjesse7a4225c2023-11-24 13:40:22 +0100747 'java_opts': [],
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200748 })
Morten Krogh-Jespersenc9d23462020-11-12 15:36:57 +0100749
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200750 app_jar = os.path.join(
751 temp_dir, '{}_{}_{}_dex_out.jar'.format(app.name, shrinker,
752 compilation_step_index))
753 app_mapping = os.path.join(
754 temp_dir, '{}_{}_{}_dex_out.jar.map'.format(app.name, shrinker,
755 compilation_step_index))
756 recomp_jar = None
757 jdkhome = get_jdk_home(options, app)
758 with utils.TempDir() as compile_temp_dir:
759 compile_result = compiledump.run1(compile_temp_dir,
760 args, [],
761 jdkhome,
762 worker_id=worker_id)
763 out_jar = os.path.join(compile_temp_dir, "out.jar")
764 out_mapping = os.path.join(compile_temp_dir, "out.jar.map")
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100765
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200766 if compile_result != 0 or not os.path.isfile(out_jar):
767 assert False, 'Compilation of {} failed'.format(
768 dump_for_app(app_dir, app))
769 shutil.move(out_jar, app_jar)
770 shutil.move(out_mapping, app_mapping)
771
772 if compilation_step_index < compilation_steps - 1:
773 args['classfile'] = True
774 args['min_api'] = "10000"
775 args['disable_desugared_lib'] = True
776 compile_result = compiledump.run1(compile_temp_dir, args, [],
777 jdkhome)
778 if compile_result == 0:
779 recomp_jar = os.path.join(
780 temp_dir,
781 '{}_{}_{}_cf_out.jar'.format(app.name, shrinker,
782 compilation_step_index))
783 shutil.move(out_jar, recomp_jar)
784
785 return (app_jar, app_mapping, recomp_jar)
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100786
Morten Krogh-Jespersen162b3452020-11-05 13:07:10 +0100787
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100788def build_test_with_shrinker(app, options, temp_dir, app_dir, shrinker,
789 compilation_step_index, mapping):
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100790
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200791 def rewrite_files(files):
792 add_applymapping = True
793 for file in files:
794 compiledump.clean_config(file, options)
795 remove_print_lines(file)
796 with open(file) as f:
797 lines = f.readlines()
798 with open(file, 'w') as f:
799 for line in lines:
800 if '-applymapping' not in line:
801 f.write(line + '\n')
802 if add_applymapping:
803 f.write("-applymapping " + mapping + '\n')
804 add_applymapping = False
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100805
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200806 args = AttrDict({
807 'dump': dump_test_for_app(app_dir, app),
808 'r8_jar': get_r8_jar(options, temp_dir, shrinker),
809 'disable_assertions': options.disable_assertions,
810 'version': options.version,
811 'compiler': 'r8full' if is_full_r8(shrinker) else 'r8',
812 'debug_agent': options.debug_agent,
813 'nolib': not is_minified_r8(shrinker),
814 # The config file will have an -applymapping reference to an old map.
815 # Update it to point to mapping file build in the compilation of the app.
816 'config_files_consumer': rewrite_files,
817 })
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100818
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200819 test_jar = os.path.join(
820 temp_dir, '{}_{}_{}_test_out.jar'.format(app.name, shrinker,
821 compilation_step_index))
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100822
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200823 with utils.TempDir() as compile_temp_dir:
824 jdkhome = get_jdk_home(options, app)
825 compile_result = compiledump.run1(compile_temp_dir, args, [], jdkhome)
826 out_jar = os.path.join(compile_temp_dir, "out.jar")
827 if compile_result != 0 or not os.path.isfile(out_jar):
828 return None
829 shutil.move(out_jar, test_jar)
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100830
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200831 return test_jar
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100832
833
834def log_results_for_apps(result_per_shrinker_per_app, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200835 print('')
836 app_errors = 0
837 for (app, result_per_shrinker) in result_per_shrinker_per_app:
838 app_errors += (1 if log_results_for_app(app, result_per_shrinker,
839 options) else 0)
840 return app_errors
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100841
842
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200843def log_results_for_app(app, result_per_shrinker, options, worker_id=None):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200844 if options.print_dexsegments:
845 log_segments_for_app(app,
846 result_per_shrinker,
847 options,
848 worker_id=worker_id)
849 return False
850 else:
851 return log_comparison_results_for_app(app,
852 result_per_shrinker,
853 options,
854 worker_id=worker_id)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100855
856
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +0200857def log_segments_for_app(app, result_per_shrinker, options, worker_id):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200858 for shrinker in SHRINKERS:
859 if shrinker not in result_per_shrinker:
860 continue
861 for result in result_per_shrinker.get(shrinker):
862 benchmark_name = '{}-{}'.format(options.print_dexsegments, app.name)
863 utils.print_dexsegments(benchmark_name, [result.get('output_jar')],
864 worker_id=worker_id)
865 duration = result.get('duration')
866 print_thread(
867 '%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration),
868 worker_id)
869 print_thread(
870 '%s-Total(CodeSize): %s' %
871 (benchmark_name, result.get('dex_size')), worker_id)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100872
873
874def percentage_diff_as_string(before, after):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200875 if after < before:
876 return '-' + str(round((1.0 - after / before) * 100)) + '%'
877 else:
878 return '+' + str(round((after - before) / before * 100)) + '%'
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100879
880
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200881def log_comparison_results_for_app(app, result_per_shrinker, options,
882 worker_id):
883 print_thread(app.name + ':', worker_id)
884 app_error = False
885 if result_per_shrinker.get('status', 'success') != 'success':
886 error_message = result_per_shrinker.get('error_message')
887 print_thread(' skipped ({})'.format(error_message), worker_id)
888 return
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100889
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200890 proguard_result = result_per_shrinker.get('pg', {})
891 proguard_dex_size = float(proguard_result.get('dex_size', -1))
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100892
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200893 for shrinker in SHRINKERS:
894 if shrinker not in result_per_shrinker:
895 continue
896 compilation_index = 1
897 for result in result_per_shrinker.get(shrinker):
898 build_status = result.get('build_status')
899 if build_status != 'success' and build_status is not None:
900 app_error = True
901 warn(' {}-#{}: {}'.format(shrinker, compilation_index,
902 build_status))
903 continue
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100904
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200905 if options.golem:
906 print_thread(
907 '%s(RunTimeRaw): %s ms' %
908 (app.name, result.get('duration')), worker_id)
909 print_thread(
910 '%s(CodeSize): %s' % (app.name, result.get('dex_size')),
911 worker_id)
912 continue
Rico Wind59593922021-03-03 09:12:36 +0100913
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200914 print_thread(' {}-#{}:'.format(shrinker, compilation_index),
915 worker_id)
916 dex_size = result.get('dex_size')
917 msg = ' dex size: {}'.format(dex_size)
918 if options.print_runtimeraw:
919 print_thread(
920 ' run time raw: {} ms'.format(result.get('duration')),
921 worker_id)
922 if dex_size != proguard_dex_size and proguard_dex_size >= 0:
923 msg = '{} ({}, {})'.format(
924 msg, dex_size - proguard_dex_size,
925 percentage_diff_as_string(proguard_dex_size, dex_size))
926 success(msg) if dex_size < proguard_dex_size else warn(msg)
927 else:
928 print_thread(msg, worker_id)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100929
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200930 if options.monkey:
931 monkey_status = result.get('monkey_status')
932 if monkey_status != 'success':
933 app_error = True
934 warn(' monkey: {}'.format(monkey_status))
935 else:
936 success(' monkey: {}'.format(monkey_status))
Morten Krogh-Jespersencd55f812020-11-04 09:13:31 +0100937
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200938 if options.run_tests and 'instrumentation_test_status' in result:
939 test_status = result.get('instrumentation_test_status')
940 if test_status != 'success':
941 warn(' instrumentation_tests: {}'.format(test_status))
942 else:
943 success(' instrumentation_tests: {}'.format(test_status))
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100944
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200945 recompilation_status = result.get('recompilation_status', '')
946 if recompilation_status == 'failed':
947 app_error = True
948 warn(' recompilation {}-#{}: failed'.format(
949 shrinker, compilation_index))
950 continue
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100951
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200952 compilation_index += 1
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100953
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200954 return app_error
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100955
956
957def parse_options(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200958 result = argparse.ArgumentParser(description='Run/compile dump artifacts.')
959 result.add_argument('--app',
960 help='What app to run on',
961 choices=[app.name for app in APPS],
962 action='append')
963 result.add_argument(
964 '--app-collection',
965 '--app_collection',
966 help='What app collection to run',
967 choices=[collection.name for collection in APP_COLLECTIONS],
968 action='append')
969 result.add_argument('--app-logging-filter',
970 '--app_logging_filter',
971 help='The apps for which to turn on logging',
972 action='append')
973 result.add_argument('--bot',
974 help='Running on bot, use third_party dependency.',
975 default=False,
976 action='store_true')
977 result.add_argument('--generate-golem-config',
978 '--generate_golem_config',
979 help='Generate a new config for golem.',
980 default=False,
981 action='store_true')
982 result.add_argument('--debug-agent',
983 help='Enable Java debug agent and suspend compilation '
984 '(default disabled)',
985 default=False,
986 action='store_true')
987 result.add_argument(
988 '--disable-assertions',
989 '--disable_assertions',
990 '-da',
991 help='Disable Java assertions when running the compiler '
992 '(default enabled)',
993 default=False,
994 action='store_true')
995 result.add_argument('--emulator-id',
996 '--emulator_id',
997 help='Id of the emulator to use',
998 default='emulator-5554')
999 result.add_argument('--golem',
1000 help='Running on golem, do not download',
1001 default=False,
1002 action='store_true')
1003 result.add_argument('--hash', help='The commit of R8 to use')
1004 result.add_argument(
1005 '--internal',
1006 help='Run internal apps if set, otherwise run opensource',
1007 default=False,
1008 action='store_true')
1009 result.add_argument('--keystore',
1010 help='Path to app.keystore',
1011 default=os.path.join(utils.TOOLS_DIR, 'debug.keystore'))
1012 result.add_argument('--keystore-password',
1013 '--keystore_password',
1014 help='Password for app.keystore',
1015 default='android')
1016 result.add_argument('--minify',
1017 help='Force enable/disable minification' +
1018 ' (defaults to app proguard config)',
1019 choices=['default', 'force-enable', 'force-disable'],
1020 default='default')
1021 result.add_argument('--monkey',
1022 help='Whether to install and run app(s) with monkey',
1023 default=False,
1024 action='store_true')
1025 result.add_argument('--monkey-events',
1026 '--monkey_events',
1027 help='Number of events that the monkey should trigger',
1028 default=250,
1029 type=int)
1030 result.add_argument('--no-build',
1031 '--no_build',
1032 help='Run without building first (only when using ToT)',
1033 default=False,
1034 action='store_true')
1035 result.add_argument('--no-logging',
1036 '--no_logging',
1037 help='Disable logging except for errors',
1038 default=False,
1039 action='store_true')
1040 result.add_argument('--optimize',
1041 help='Force enable/disable optimizations' +
1042 ' (defaults to app proguard config)',
1043 choices=['default', 'force-enable', 'force-disable'],
1044 default='default')
1045 result.add_argument('--print-times',
1046 help='Print timing information from r8',
1047 default=False,
1048 action='store_true')
1049 result.add_argument('--print-dexsegments',
1050 metavar='BENCHMARKNAME',
1051 help='Print the sizes of individual dex segments as ' +
1052 '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
1053 '<bytes>\'')
1054 result.add_argument('--print-runtimeraw',
1055 metavar='BENCHMARKNAME',
1056 help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
1057 ' <elapsed> ms\' at the end where <elapsed> is' +
1058 ' the elapsed time in milliseconds.')
1059 result.add_argument('--quiet',
1060 help='Disable verbose logging',
1061 default=False,
1062 action='store_true')
1063 result.add_argument('--r8-compilation-steps',
1064 '--r8_compilation_steps',
1065 help='Number of times R8 should be run on each app',
1066 default=2,
1067 type=int)
1068 result.add_argument('--r8-flags',
1069 '--r8_flags',
1070 help='Additional option(s) for the compiler.')
1071 result.add_argument('--run-tests',
1072 '--run_tests',
1073 help='Whether to run instrumentation tests',
1074 default=False,
1075 action='store_true')
1076 result.add_argument('--shrink',
1077 help='Force enable/disable shrinking' +
1078 ' (defaults to app proguard config)',
1079 choices=['default', 'force-enable', 'force-disable'],
1080 default='default')
1081 result.add_argument('--sign-apks',
1082 '--sign_apks',
1083 help='Whether the APKs should be signed',
1084 default=False,
1085 action='store_true')
1086 result.add_argument('--shrinker',
1087 help='The shrinkers to use (by default, all are run)',
1088 action='append')
1089 result.add_argument('--temp',
1090 help='A directory to use for temporaries and outputs.',
1091 default=None)
1092 result.add_argument('--version',
1093 default='main',
1094 help='The version of R8 to use (e.g., 1.4.51)')
1095 result.add_argument('--workers',
1096 help='Number of workers to use',
1097 default=1,
1098 type=int)
1099 (options, args) = result.parse_known_args(argv)
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +01001100
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001101 if options.app or options.app_collection:
1102 if not options.app:
1103 options.app = []
1104 if not options.app_collection:
1105 options.app_collection = []
1106 options.apps = [
1107 app for app in APPS if app.name in options.app or any(
1108 collection in options.app_collection
1109 for collection in app.collections)
1110 ]
1111 del options.app
1112 del options.app_collection
1113 else:
1114 options.apps = [app for app in APPS if app.internal == options.internal]
Christoffer Quist Adamsen53e29472020-11-13 10:53:06 +01001115
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001116 if options.app_logging_filter:
1117 for app_name in options.app_logging_filter:
1118 assert any(app.name == app_name for app in options.apps)
1119 if options.shrinker:
1120 for shrinker in options.shrinker:
1121 assert shrinker in SHRINKERS, ('Shrinker must be one of %s' %
1122 ', '.join(SHRINKERS))
1123 else:
1124 options.shrinker = [shrinker for shrinker in SHRINKERS]
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001125
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001126 if options.hash or version_is_built_jar(options.version):
1127 # No need to build R8 if a specific version should be used.
1128 options.no_build = True
1129 if 'r8-nolib' in options.shrinker:
1130 warn('Skipping shrinker r8-nolib because a specific version ' +
1131 'of r8 was specified')
1132 options.shrinker.remove('r8-nolib')
1133 if 'r8-nolib-full' in options.shrinker:
1134 warn('Skipping shrinker r8-nolib-full because a specific version ' +
1135 'of r8 was specified')
1136 options.shrinker.remove('r8-nolib-full')
1137 return (options, args)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001138
1139
Rico Wind59593922021-03-03 09:12:36 +01001140def print_indented(s, indent):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001141 print(' ' * indent + s)
Rico Wind59593922021-03-03 09:12:36 +01001142
1143
1144def get_sha256(gz_file):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001145 with open(gz_file, 'rb') as f:
1146 bytes = f.read() # read entire file as bytes
1147 return hashlib.sha256(bytes).hexdigest()
Rico Wind59593922021-03-03 09:12:36 +01001148
1149
1150def get_sha_from_file(sha_file):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001151 with open(sha_file, 'r') as f:
1152 return f.readlines()[0]
Rico Wind59593922021-03-03 09:12:36 +01001153
1154
1155def print_golem_config(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001156 print('// AUTOGENERATED FILE from tools/run_on_app_dump.py in R8 repo')
1157 print('part of r8_config;')
1158 print('')
1159 print('final Suite dumpsSuite = Suite("OpenSourceAppDumps");')
1160 print('')
1161 print('createOpenSourceAppBenchmarks() {')
1162 print_indented('final cpus = ["Lenovo M90"];', 2)
1163 print_indented('final targetsCompat = ["R8"];', 2)
1164 print_indented('final targetsFull = ["R8-full-minify-optimize-shrink"];', 2)
1165 # Avoid calculating this for every app
1166 jdk_gz = jdk.GetJdkHome() + '.tar.gz'
1167 add_golem_resource(2, jdk_gz, 'openjdk')
1168 for app in options.apps:
1169 if app.folder and not app.internal:
1170 indentation = 2
1171 print_indented('{', indentation)
1172 indentation = 4
1173 print_indented('final name = "%s";' % app.name, indentation)
1174 print_indented('final benchmark =', indentation)
1175 print_indented(
1176 'StandardBenchmark(name, [Metric.RunTimeRaw, Metric.CodeSize]);',
1177 indentation + 4)
1178 if app.golem_duration != None:
1179 print_indented(
1180 'final timeout = const Duration(seconds: %s);' %
1181 app.golem_duration, indentation)
1182 print_indented(
1183 'ExecutionManagement.addTimeoutConstraint'
1184 '(timeout, benchmark: benchmark);', indentation)
1185 app_gz = os.path.join(utils.OPENSOURCE_DUMPS_DIR,
1186 app.folder + '.tar.gz')
1187 name = 'appResource'
1188 add_golem_resource(indentation, app_gz, name)
1189 print_golem_config_target('Compat', 'r8', app, indentation)
1190 print_golem_config_target('Full',
1191 'r8-full',
1192 app,
1193 indentation,
1194 minify='force-enable',
1195 optimize='force-enable',
1196 shrink='force-enable')
1197 print_indented('dumpsSuite.addBenchmark(name);', indentation)
1198 indentation = 2
1199 print_indented('}', indentation)
1200 print('}')
Rico Wind59593922021-03-03 09:12:36 +01001201
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001202
1203def print_golem_config_target(target,
1204 shrinker,
1205 app,
1206 indentation,
1207 minify='default',
1208 optimize='default',
1209 shrink='default'):
1210 options = "options" + target
1211 print_indented(
1212 'final %s = benchmark.addTargets(noImplementation, targets%s);' %
1213 (options, target), indentation)
1214 print_indented('%s.cpus = cpus;' % options, indentation)
1215 print_indented('%s.isScript = true;' % options, indentation)
1216 print_indented('%s.fromRevision = 9700;' % options, indentation)
1217 print_indented('%s.mainFile = "tools/run_on_app_dump.py "' % options,
1218 indentation)
1219 print_indented(
1220 '"--golem --disable-assertions --quiet --shrinker %s --app %s "' %
1221 (shrinker, app.name), indentation + 4)
1222 print_indented(
1223 '"--minify %s --optimize %s --shrink %s";' % (minify, optimize, shrink),
1224 indentation + 4)
1225 print_indented('%s.resources.add(appResource);' % options, indentation)
1226 print_indented('%s.resources.add(openjdk);' % options, indentation)
1227
Christoffer Quist Adamsen8e9f7982021-12-10 13:10:05 +01001228
Rico Wind61a034f2021-03-16 09:37:11 +01001229def add_golem_resource(indentation, gz, name, sha256=None):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001230 sha = gz + '.sha1'
1231 if not sha256:
1232 # Golem uses a sha256 of the file in the cache, and you need to specify that.
1233 download_sha(sha, False, quiet=True)
1234 sha256 = get_sha256(gz)
1235 sha = get_sha_from_file(sha)
1236 print_indented('final %s = BenchmarkResource("",' % name, indentation)
1237 print_indented('type: BenchmarkResourceType.storage,', indentation + 4)
1238 print_indented('uri: "gs://r8-deps/%s",' % sha, indentation + 4)
1239 # Make dart formatter happy.
1240 if indentation > 2:
1241 print_indented('hash:', indentation + 4)
1242 print_indented('"%s",' % sha256, indentation + 8)
1243 else:
1244 print_indented('hash: "%s",' % sha256, indentation + 4)
1245 print_indented('extract: "gz");', indentation + 4)
1246
Rico Wind61a034f2021-03-16 09:37:11 +01001247
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001248def main(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001249 (options, args) = parse_options(argv)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001250
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001251 if options.bot:
1252 options.no_logging = True
1253 options.shrinker = ['r8', 'r8-full']
1254 print(options.shrinker)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001255
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001256 if options.golem:
1257 options.disable_assertions = True
1258 options.no_build = True
1259 options.r8_compilation_steps = 1
1260 options.quiet = True
1261 options.no_logging = True
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001262
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001263 if options.generate_golem_config:
1264 print_golem_config(options)
1265 return 0
Rico Wind59593922021-03-03 09:12:36 +01001266
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001267 with utils.TempDir() as temp_dir:
1268 if options.temp:
1269 temp_dir = options.temp
1270 os.makedirs(temp_dir, exist_ok=True)
1271 if options.hash:
1272 # Download r8-<hash>.jar from
1273 # https://storage.googleapis.com/r8-releases/raw/.
1274 target = 'r8-{}.jar'.format(options.hash)
1275 update_prebuilds_in_android.download_hash(
1276 temp_dir, 'com/android/tools/r8/' + options.hash, target)
1277 as_utils.MoveFile(os.path.join(temp_dir, target),
1278 os.path.join(temp_dir, 'r8lib.jar'),
1279 quiet=options.quiet)
1280 elif version_is_built_jar(options.version):
1281 # Download r8-<version>.jar from
1282 # https://storage.googleapis.com/r8-releases/raw/.
1283 target = 'r8-{}.jar'.format(options.version)
1284 update_prebuilds_in_android.download_version(
1285 temp_dir, 'com/android/tools/r8/' + options.version, target)
1286 as_utils.MoveFile(os.path.join(temp_dir, target),
1287 os.path.join(temp_dir, 'r8lib.jar'),
1288 quiet=options.quiet)
1289 elif options.version == 'main':
1290 if not options.no_build:
1291 gradle.RunGradle([
Ian Zernyed1f8512023-11-07 11:07:57 +01001292 utils.GRADLE_TASK_R8,
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001293 '-Pno_internal'
1294 ])
1295 build_r8lib = False
1296 for shrinker in options.shrinker:
1297 if is_minified_r8(shrinker):
1298 build_r8lib = True
1299 if build_r8lib:
1300 gradle.RunGradle([utils.GRADLE_TASK_R8LIB, '-Pno_internal'])
1301 # Make a copy of r8.jar and r8lib.jar such that they stay the same for
1302 # the entire execution of this script.
1303 if 'r8-nolib' in options.shrinker or 'r8-nolib-full' in options.shrinker:
1304 assert os.path.isfile(
1305 utils.R8_JAR), 'Cannot build without r8.jar'
1306 shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
1307 if 'r8' in options.shrinker or 'r8-full' in options.shrinker:
1308 assert os.path.isfile(
1309 utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
1310 shutil.copyfile(utils.R8LIB_JAR,
1311 os.path.join(temp_dir, 'r8lib.jar'))
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001312
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001313 jobs = []
1314 result_per_shrinker_per_app = []
1315 for app in options.apps:
1316 if app.skip:
1317 continue
1318 result = {}
1319 result_per_shrinker_per_app.append((app, result))
1320 jobs.append(create_job(app, options, result, temp_dir))
1321 thread_utils.run_in_parallel(jobs,
1322 number_of_workers=options.workers,
1323 stop_on_first_failure=False)
1324 errors = log_results_for_apps(result_per_shrinker_per_app, options)
1325 if errors > 0:
1326 dest = 'gs://r8-test-results/r8-libs/' + str(int(time.time()))
1327 utils.upload_file_to_cloud_storage(
1328 os.path.join(temp_dir, 'r8lib.jar'), dest)
1329 print('R8lib saved to %s' % dest)
1330 return errors
1331
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001332
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +02001333def create_job(app, options, result, temp_dir):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001334 return lambda worker_id: run_job(app, options, result, temp_dir, worker_id)
1335
Christoffer Quist Adamsenbe6661d2023-08-24 11:01:12 +02001336
1337def run_job(app, options, result, temp_dir, worker_id):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001338 job_temp_dir = os.path.join(temp_dir, str(worker_id or 0))
1339 os.makedirs(job_temp_dir, exist_ok=True)
1340 result.update(get_results_for_app(app, options, job_temp_dir, worker_id))
1341 return 0
1342
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001343
1344def success(message):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001345 CGREEN = '\033[32m'
1346 CEND = '\033[0m'
1347 print(CGREEN + message + CEND)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001348
1349
1350def warn(message):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001351 CRED = '\033[91m'
1352 CEND = '\033[0m'
1353 print(CRED + message + CEND)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +01001354
1355
1356if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +02001357 sys.exit(main(sys.argv[1:]))