Rewrite create-jctf-tests.sh in python

Bug:
Change-Id: I6d79c852b5118d2e5b9ae8f46823961ac364e7ed
diff --git a/build.gradle b/build.gradle
index 695e898..6667647 100644
--- a/build.gradle
+++ b/build.gradle
@@ -326,12 +326,11 @@
 
 task createJctfTests(type: Exec) {
     def outputDir = "build/generated/test/java/com/android/tools/r8/jctf"
-    def script = "scripts/create-jctf-tests.sh"
+    def script = "tools/create_jctf_tests.py"
     inputs.file script
     outputs.dir outputDir
     dependsOn downloadDeps
-    executable "bash"
-    args "${projectDir}/${script}"
+    commandLine "python", script
     workingDir = projectDir
 }
 
diff --git a/scripts/create-jctf-tests.sh b/scripts/create-jctf-tests.sh
deleted file mode 100755
index 2f4e91e..0000000
--- a/scripts/create-jctf-tests.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2017, 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.
-
-function generate_test() {
-  local name=$1
-  local testClassName=$2
-  local fileName=$3
-  local compilerUnderTest=$4
-  local classFile=$5
-  local relativePackage=$6
-  # The bash uppercase substitution ^^ is not supported on the bash version on Mac OS.
-  local compilerUnderTestEnum=$(echo ${compilerUnderTest} | tr /a-z/ /A-Z/)
-
-  PACKAGE_PREFIX="com.google.jctf.test.lib.java."
-
-  if [[ ! $name =~ ^$PACKAGE_PREFIX ]]; then
-    echo "Test full class name expected to start with \"$PACKAGE_PREFIX\" but it's \"$name\"" >&2
-    exit 1
-  fi
-
-  mkdir -p $(dirname $fileName)
-
-  cat <<EOF > $fileName
-// Copyright (c) 2017, 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.
-package com.android.tools.r8.jctf.${compilerUnderTest}.${relativePackage};
-
-import org.junit.Test;
-import com.android.tools.r8.R8RunArtTestsTest;
-
-/**
- * Auto-generated test for the jctf test:
- * ${name}
- *
- * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN scripts/create-jctf-tests.sh INSTEAD!
- */
-public class ${testClassName} extends R8RunArtTestsTest {
-
-  public ${testClassName}() {
-    super("${name:${#PACKAGE_PREFIX}}", DexTool.NONE);
-  }
-
-  @Test
-  public void run${testClassName}() throws Exception {
-    // For testing with other Art VMs than the default set the system property 'dex_vm'
-    // to the desired VM string (e.g. '4.4.4', see ToolHelper.DexVm)
-    runJctfTest(CompilerUnderTest.${compilerUnderTestEnum},
-      "$classFile",
-      "$name"
-    );
-  }
-}
-EOF
-}
-
-JCTFROOT="third_party/jctf"
-DESTINATIONDIR="build/generated/test/java/com/android/tools/r8/jctf"
-
-if [ ! -e $JCTFROOT ]; then
-  echo "Missing jctf tests in $JCTFROOT."
-  exit
-fi
-
-for d in $DESTINATIONDIR/r8 $DESTINATIONDIR/d8; do
-  rm -rf $d
-  mkdir -p $d
-done
-
-RELATIVE_TESTDIR="LibTests/src/com/google/jctf/test/lib/java"
-TESTDIR="$JCTFROOT/$RELATIVE_TESTDIR"
-
-COUNT=$(grep -r "@Test" "$TESTDIR" --include=*.java -l | wc -l)
-echo "JCTF: $COUNT files to generate"
-
-grep -r "@Test" "$TESTDIR" --include=*.java -l | while read -r line; do
-  TESTNAME=$(basename $line .java)
-  TESTCLASSNAME="${TESTNAME//-/_}"
-
-  RELATIVE_TEST_PATH=$(expr "$line" : ".*$RELATIVE_TESTDIR/\(.*$\)")
-
-  PACKAGE=$(grep package $line | grep -o -m 1 "com[^;]*")
-  CLASSFILE="$(echo $PACKAGE | tr '.' '/')/$TESTNAME.class"
-  RELATIVE_PACKAGE=$(expr "$PACKAGE" : ".*\.java\.\(.*$\)")
-
-  generate_test $PACKAGE.$TESTNAME $TESTCLASSNAME \
-    "$DESTINATIONDIR/r8/$RELATIVE_TEST_PATH" r8 \
-    $CLASSFILE $RELATIVE_PACKAGE
-  generate_test $PACKAGE.$TESTNAME $TESTCLASSNAME \
-    "$DESTINATIONDIR/d8/$RELATIVE_TEST_PATH" d8 \
-    $CLASSFILE $RELATIVE_PACKAGE
-done
-
diff --git a/tools/create_jctf_tests.py b/tools/create_jctf_tests.py
new file mode 100755
index 0000000..0f1ca98
--- /dev/null
+++ b/tools/create_jctf_tests.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# Copyright (c) 2017, 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
+from glob import glob
+from itertools import chain
+from os import makedirs
+from os.path import exists, join, dirname
+from shutil import rmtree
+from string import Template, upper
+import os
+import re
+import sys
+
+import utils
+
+JCTFROOT = 'third_party/jctf'
+DESTINATION_DIR = 'build/generated/test/java/com/android/tools/r8/jctf'
+PACKAGE_PREFIX = 'com.google.jctf.test.lib.java.'
+RELATIVE_TESTDIR = 'LibTests/src/com/google/jctf/test/lib/java'
+TESTDIR = join(JCTFROOT, RELATIVE_TESTDIR)
+TEMPLATE = Template(
+"""// Copyright (c) 2017, 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.
+package com.android.tools.r8.jctf.${compilerUnderTest}.${relativePackage};
+
+import org.junit.Test;
+import com.android.tools.r8.R8RunArtTestsTest;
+
+/**
+ * Auto-generated test for the jctf test:
+ * ${name}
+ *
+ * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN
+ * tools/create_jctf_tests.py INSTEAD!
+ */
+public class ${testClassName} extends R8RunArtTestsTest {
+
+  public ${testClassName}() {
+    super("${nameWithoutPackagePrefix}", DexTool.NONE);
+  }
+
+  @Test
+  public void run${testClassName}() throws Exception {
+    // For testing with other Art VMs than the default set the system property
+    // 'dex_vm' to the desired VM string (e.g. '4.4.4', see ToolHelper.DexVm)
+    runJctfTest(CompilerUnderTest.${compilerUnderTestEnum},
+      "$classFile",
+      "$name"
+    );
+  }
+}
+""")
+
+EXIT_FAILURE = 1
+RE_PACKAGE = re.compile('package\\s+(com[^\\s;]*)')
+
+def file_contains_string(filepath, search_string):
+  with open(filepath) as f:
+    return search_string in f.read()
+
+def read_package_from_java_file(filepath):
+  with open(filepath) as f:
+    for line in f:
+      m = RE_PACKAGE.search(line)
+      if m:
+        return m.groups()[0]
+  raise IOError("Can't find package statement in java file: " + filepath)
+
+
+def generate_test(class_name, compiler_under_test, relative_package):
+  filename = join(DESTINATION_DIR, compiler_under_test,
+      relative_package.replace('.', '/'), class_name + '.java')
+  utils.makedirs_if_needed(dirname(filename))
+
+  full_class_name = '{}{}.{}'.format(PACKAGE_PREFIX, relative_package,
+      class_name)
+  contents = TEMPLATE.substitute(
+      compilerUnderTest = compiler_under_test,
+      relativePackage = relative_package,
+      name = full_class_name,
+      testClassName = class_name,
+      compilerUnderTestEnum = compiler_under_test.upper(),
+      classFile = full_class_name.replace('.', '/') + '.class',
+      nameWithoutPackagePrefix = '{}.{}'.format(relative_package, class_name))
+
+  with open(filename, 'w') as f:
+    f.write(contents)
+
+def Main():
+  if not exists(JCTFROOT):
+    print('JCTF test package not found in {}'.format(JCTFROOT),
+        file = sys.stderr)
+    return EXIT_FAILURE
+
+  for tool in ['d8', 'r8']:
+    p = join(DESTINATION_DIR, tool)
+    if exists(p):
+      rmtree(p)
+    makedirs(p)
+
+  java_files = (chain.from_iterable(glob(join(x[0], '*.java'))
+      for x in os.walk(TESTDIR)))
+
+  dot_java_dot = '.java.'
+
+  for f in java_files:
+    if not file_contains_string(f, '@Test'):
+      continue
+
+    class_name = os.path.splitext(os.path.basename(f))[0]
+    assert class_name.find('-') < 0
+
+    package = read_package_from_java_file(f)
+
+    idx = package.find(dot_java_dot)
+    assert idx >= 0
+    relative_package = package[idx + len(dot_java_dot):]
+
+    for d in ['r8', 'd8']:
+      generate_test(class_name, d, relative_package)
+
+
+if __name__ == '__main__':
+  sys.exit(Main())