Extend run_on_as_app.py to output resulting dex size

Change-Id: I6215a9da3614fbc8e67562420d8f92eed0a533b3
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index a213dcc..b56f96d 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -9,6 +9,7 @@
 import subprocess
 import sys
 import utils
+import zipfile
 
 import as_utils
 
@@ -78,6 +79,14 @@
 android_build_tools = os.path.join(
     android_home, 'build-tools', android_build_tools_version)
 
+def ComputeSizeOfDexFilesInApk(apk):
+  dex_size = 0
+  z = zipfile.ZipFile(apk, 'r')
+  for filename in z.namelist():
+    if filename.endswith('.dex'):
+      dex_size += z.getinfo(filename).file_size
+  return dex_size
+
 def IsBuiltWithR8(apk):
   script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
   return '~~R8' in subprocess.check_output(['python', script, apk]).strip()
@@ -119,16 +128,24 @@
   else:
     as_utils.remove_r8_dependency(checkout_dir)
 
+  dex_size_per_shrinker = {}
+
   with utils.ChangedWorkingDirectory(checkout_dir):
     for shrinker in SHRINKERS:
       if options.shrinker is not None and shrinker != options.shrinker:
         continue
 
-      BuildAppWithShrinker(app, config, shrinker, checkout_dir, options)
+      apk_dest = BuildAppWithShrinker(
+        app, config, shrinker, checkout_dir, options)
+
+      dex_size = ComputeSizeOfDexFilesInApk(apk_dest)
+      dex_size_per_shrinker[shrinker] = dex_size
 
     if IsTrackedByGit('gradle.properties'):
       GitCheckout('gradle.properties')
 
+  return dex_size_per_shrinker
+
 def BuildAppWithShrinker(app, config, shrinker, checkout_dir, options):
   print('Building {} with {}'.format(app, shrinker))
 
@@ -200,6 +217,23 @@
   assert IsBuiltWithR8(apk_dest) == ('r8' in shrinker), (
       'Unexpected marker in generated APK for {}'.format(shrinker))
 
+  return apk_dest
+
+def LogResults(dex_size_per_shrinker_per_app):
+  for app, dex_size_per_shrinker in dex_size_per_shrinker_per_app.iteritems():
+    print(app + ':')
+    baseline = dex_size_per_shrinker.get('proguard', -1)
+    for shrinker, dex_size in dex_size_per_shrinker.iteritems():
+      if dex_size != baseline and baseline >= 0:
+        if dex_size < baseline:
+          success('  {}: {} ({})'.format(
+            shrinker, dex_size, dex_size - baseline))
+        elif dex_size > baseline:
+          warn('  {}: {} ({})'.format(
+            shrinker, dex_size, dex_size - baseline))
+      else:
+        print('  {}: {}'.format(shrinker, dex_size))
+
 def ParseOptions(argv):
   result = optparse.OptionParser()
   result.add_option('--app',
@@ -223,12 +257,28 @@
   assert not options.use_tot or os.path.isfile(utils.R8_JAR), (
       'Cannot build from ToT without r8.jar')
 
+  dex_size_per_shrinker_per_app = {}
+
   if options.app:
-    BuildAppWithSelectedShrinkers(options.app, APPS.get(options.app), options)
+    dex_size_per_shrinker_per_app[options.app] = BuildAppWithSelectedShrinkers(
+        options.app, APPS.get(options.app), options)
   else:
     for app, config in APPS.iteritems():
       if not config.get('skip', False):
-        BuildAppWithSelectedShrinkers(app, config, options)
+        dex_size_per_shrinker_per_app[app] = BuildAppWithSelectedShrinkers(
+            app, config, options)
+
+  LogResults(dex_size_per_shrinker_per_app)
+
+def success(message):
+  CGREEN = '\033[32m'
+  CEND = '\033[0m'
+  print(CGREEN + message + CEND)
+
+def warn(message):
+  CRED = '\033[91m'
+  CEND = '\033[0m'
+  print(CRED + message + CEND)
 
 if __name__ == '__main__':
   sys.exit(main(sys.argv[1:]))