Add some gradle benchmarks

Adds tools/test_gradle_benchmarks.py which
outputs information about Gradle tasks duration
for Golem.
Adds an init-script which is used to override
Gradle-based benchmark project configuration
so that the desired versions of r8 and android
Gradle plugin is used.

Change-Id: I050671bd8f62820d8108eb53ef38407a3ac6462a
Bug: 77613196
diff --git a/tools/test_gradle_benchmarks.py b/tools/test_gradle_benchmarks.py
new file mode 100755
index 0000000..e843753
--- /dev/null
+++ b/tools/test_gradle_benchmarks.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+
+from __future__ import print_function
+import argparse
+import os
+import sys
+import utils
+import gradle
+from enum import Enum
+
+
+BENCHMARKS_ROOT_DIR = os.path.join(utils.REPO_ROOT, 'third_party', 'benchmarks')
+
+def parse_arguments():
+  parser = argparse.ArgumentParser(
+    description='Run D8 or DX on gradle apps located in'
+                ' third_party/benchmarks/.'
+                ' Report Golem-compatible RunTimeRaw values.')
+  parser.add_argument('--tool',
+                      choices=['dx', 'd8'],
+                      required=True,
+                      help='Compiler tool to use.')
+  return parser.parse_args()
+
+
+class Benchmark:
+  class Tools(Enum):
+    D8 = 1
+    DX = 2
+
+  class DesugarMode(Enum):
+    D8_DESUGARING = 1
+    DESUGAR_TOOL = 2
+
+  displayName = ""
+  rootDirPath = ""
+  appPath = ""
+  moduleName = ""
+  buildCommand = ""
+  cleanCommand = ""
+
+  def __init__(self, displayName, benchmarkDir, moduleName, buildCommand, cleanCommand):
+    self.displayName = displayName
+    self.rootDirPath = os.path.join(BENCHMARKS_ROOT_DIR, benchmarkDir.split(os.sep)[0])
+    self.appPath = os.path.join(BENCHMARKS_ROOT_DIR, benchmarkDir)
+    self.moduleName = moduleName
+    self.buildCommand = buildCommand
+    self.cleanCommand = cleanCommand
+
+  def RunGradle(self, command, tool, desugarMode):
+
+    args = ['-Dr8.root.dir=' + utils.REPO_ROOT, '--init-script',
+            os.path.join(BENCHMARKS_ROOT_DIR, 'init-script.gradle')]
+
+    if tool == self.Tools.D8:
+      args.append('-Dandroid.enableD8=true')
+    elif tool == self.Tools.DX:
+      args.append('-Dandroid.enableD8=false')
+    else:
+      raise AssertionError("Unknown tool: " + repr(tool))
+
+    if desugarMode == self.DesugarMode.D8_DESUGARING:
+      args.append('-Dandroid.enableDesugar=false')
+    elif desugarMode == self.DesugarMode.DESUGAR_TOOL:
+      args.append('-Dandroid.enableDesugar=true')
+    else:
+      raise AssertionError("Unknown desugar mode: " + repr(desugarMode))
+
+    args.extend(command)
+
+    return gradle.RunGradleWrapperInGetOutput(args, self.appPath)
+
+  def Build(self, tool, desugarMode):
+    return self.RunGradle(self.buildCommand, tool, desugarMode)
+
+  def Clean(self):
+    # tools and desugar mode not relevant for clean
+    return self.RunGradle(self.cleanCommand, self.Tools.D8, self.DesugarMode.D8_DESUGARING)
+
+  def EnsurePresence(self):
+    EnsurePresence(self.rootDirPath, self.displayName)
+
+
+def EnsurePresence(dir, displayName):
+  if not os.path.exists(dir) or os.path.getmtime(dir + '.tar.gz')\
+          < os.path.getmtime(dir + '.tar.gz.sha1'):
+    utils.DownloadFromX20(dir + '.tar.gz.sha1')
+    # Update the mtime of the tar file to make sure we do not run again unless
+    # there is an update.
+    os.utime(dir + '.tar.gz', None)
+  else:
+    print('test_gradle_benchmarks.py: benchmark {} is present'.format(displayName))
+
+def TaskFilter(taskname):
+  acceptedGradleTasks = [
+    'dex',
+    'Dex',
+    'proguard',
+    'Proguard',
+    'kotlin',
+    'Kotlin',
+  ]
+
+  return any(namePattern in taskname for namePattern in acceptedGradleTasks)
+
+
+def PrintBuildTimeForGolem(benchmark, stdOut):
+  for line in stdOut.splitlines():
+    if 'BENCH' in line and benchmark.moduleName in line:
+      commaSplit = line.split(',')
+      assert len(commaSplit) == 3
+
+      # Keep only module that have been configured to use R8
+      if benchmark.moduleName + ':' not in commaSplit[1]:
+        continue
+
+      # remove <module-name> + ':'
+      taskName = commaSplit[1][(len(benchmark.moduleName) + 1):]
+
+      # Just a temporary assumption.
+      # This means we have submodules, so we'll need to check their configuration
+      # so that the right r8/d8 is taken. For now it shouldn't be the case.
+      assert taskName.find(':') == -1, taskName
+
+      # Output example:
+      # SantaTracker-transformClassesWithDexBuilderForDevelopmentDebug(RunTimeRaw): 748 ms
+
+      golemBenchmarkValue = benchmark.displayName + '-' + taskName + '(RunTimeRaw): '
+      if TaskFilter(taskName):
+        print('{}(RunTimeRaw): {} ms'
+              .format(benchmark.displayName + '-' + taskName, commaSplit[2]))
+
+
+def Main():
+  args = parse_arguments()
+
+  if args.tool == 'd8':
+    tool = Benchmark.Tools.D8
+    desugarMode = Benchmark.DesugarMode.D8_DESUGARING
+  else:
+    tool = Benchmark.Tools.DX
+    desugarMode = Benchmark.DesugarMode.DESUGAR_TOOL
+
+  buildTimeBenchmarks = [
+    Benchmark('AntennaPod',
+              os.path.join('antenna-pod', 'AntennaPod'),
+              ':app',
+              [':app:assembleDebug'],
+              ['clean']),
+    Benchmark('Maps',
+              'gradle-java-1.6',
+              ':maps',
+              [':maps:assembleDebug', '--settings-file', 'settings.gradle.maps'],
+              ['clean']),
+    Benchmark('Music2',
+              'gradle-java-1.6',
+              ':music2Old',
+              [':music2Old:assembleDebug', '--settings-file', 'settings.gradle.music2Old'],
+              ['clean']),
+    Benchmark('Velvet',
+              'gradle-java-1.6',
+              ':velvet',
+              [':velvet:assembleDebug', '--settings-file', 'settings.gradle.velvet'],
+              ['clean']),
+    Benchmark('SantaTracker',
+              'santa-tracker',
+              ':santa-tracker',
+              [':santa-tracker:assembleDebug'],
+              ['clean']),
+
+    # disabled for now, apparently because of b/74227571
+    # Benchmark('Tachiyomi',
+    #           'tachiyomi',
+    #           ':app',
+    #           ['assembleStandardDebug'],
+    #           ['clean']),
+
+    Benchmark('WordPress',
+              'wordpress',
+              ':WordPress',
+              ['assembleVanillaDebug'],
+              ['clean']),
+
+  ]
+
+  EnsurePresence(os.path.join('third_party', 'benchmarks', 'android-sdk'), 'android SDK')
+  EnsurePresence(os.path.join('third_party', 'gradle-plugin'), 'Android Gradle plugin')
+
+  for benchmark in buildTimeBenchmarks:
+    benchmark.EnsurePresence()
+    benchmark.Clean()
+    stdOut = benchmark.Build(tool, desugarMode)
+    PrintBuildTimeForGolem(benchmark, stdOut)
+
+
+if __name__ == '__main__':
+  sys.exit(Main())