New branch for vm-tests that use cts-tf framework.

Change-Id: I9927dc09027fc2298a1b1f7ee6c918ed1d36723a
diff --git a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
new file mode 100644
index 0000000..f584687
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import com.android.dx.util.FileUtils;
+
+import dot.junit.AllTests;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Map.Entry;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Main class to generate data from the test suite to later run from a shell
+ * script. the project's home folder.<br>
+ * <project-home>/src must contain the java sources<br>
+ * <project-home>/data/scriptdata will be generated<br>
+ * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
+ * (one Main class for each test method in the Test_... class
+ */
+public class BuildDalvikSuite {
+
+    public static boolean DEBUG = true;
+
+    private static String JAVASRC_FOLDER = "";
+    private static String MAIN_SRC_OUTPUT_FOLDER = "";
+
+    // the folder for the generated junit-files for the cts host (which in turn
+    // execute the real vm tests using adb push/shell etc)
+    private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = "";
+    private static String OUTPUT_FOLDER = "";
+    private static String COMPILED_CLASSES_FOLDER = "";
+
+    private static String CLASSES_OUTPUT_FOLDER = "";
+    private static String HOSTJUNIT_CLASSES_OUTPUT_FOLDER = "";
+
+    private static String CLASS_PATH = "";
+
+    private static String restrictTo = null; // e.g. restrict to
+    // "opcodes.add_double"
+
+    private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests";
+
+    private int testClassCnt = 0;
+    private int testMethodsCnt = 0;
+
+    /*
+     * using a linked hashmap to keep the insertion order for iterators.
+     * the junit suite/tests adding order is used to generate the order of the
+     * report.
+     * a map. key: fully qualified class name, value: a list of test methods for
+     * the given class
+     */
+    private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String,
+    List<String>>();
+
+    private class MethodData {
+        String methodBody, constraint, title;
+    }
+
+    /**
+     * @param args
+     *            args 0 must be the project root folder (where src, lib etc.
+     *            resides)
+     * @throws IOException
+     */
+    public static void main(String[] args) throws IOException {
+
+        if (args.length > 5) {
+            JAVASRC_FOLDER = args[0];
+            OUTPUT_FOLDER = args[1];
+            CLASS_PATH = args[2];
+            MAIN_SRC_OUTPUT_FOLDER = args[3];
+            CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes";
+
+            COMPILED_CLASSES_FOLDER = args[4];
+
+            HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5];
+            HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes";
+
+            if (args.length > 6) {
+                // optional: restrict to e.g. "opcodes.add_double"
+                restrictTo = args[6];
+                System.out.println("restricting build to: " + restrictTo);
+            }
+
+        } else {
+            System.out.println("usage: java-src-folder output-folder classpath " +
+                    "generated-main-files compiled_output generated-main-files " +
+            "[restrict-to-opcode]");
+            System.exit(-1);
+        }
+
+        long start = System.currentTimeMillis();
+        BuildDalvikSuite cat = new BuildDalvikSuite();
+        cat.compose();
+        long end = System.currentTimeMillis();
+
+        System.out.println("elapsed seconds: " + (end - start) / 1000);
+    }
+
+    public void compose() throws IOException {
+        System.out.println("Collecting all junit tests...");
+        new TestRunner() {
+            @Override
+            protected TestResult createTestResult() {
+                return new TestResult() {
+                    @Override
+                    protected void run(TestCase test) {
+                        addToTests(test);
+                    }
+
+                };
+            }
+        }.doRun(AllTests.suite());
+
+        // for each combination of TestClass and method, generate a Main_testN1
+        // etc.
+        // class in the respective package.
+        // for the report make sure all N... tests are called first, then B,
+        // then
+        // E, then VFE test methods.
+        // so we need x Main_xxxx methods in a package, and x entries in the
+        // global scriptdata file (read by a bash script for the tests)
+        // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
+        // File Main_testN1.java in package dxc.junit.opcodes.aaload
+        // and entry dxc.junit.opcodes.aaload.Main_testN1 in class execution
+        // table.
+        //
+        handleTests();
+    }
+
+    private void addToTests(TestCase test) {
+
+        String packageName = test.getClass().getPackage().getName();
+        packageName = packageName.substring(packageName.lastIndexOf('.'));
+
+
+        String method = test.getName(); // e.g. testVFE2
+        String fqcn = test.getClass().getName(); // e.g.
+        // dxc.junit.opcodes.iload_3.Test_iload_3
+
+        // ignore all tests not belonging to the given restriction
+        if (restrictTo != null && !fqcn.contains(restrictTo)) return;
+
+        testMethodsCnt++;
+        List<String> li = map.get(fqcn);
+        if (li == null) {
+            testClassCnt++;
+            li = new ArrayList<String>();
+            map.put(fqcn, li);
+        }
+        li.add(method);
+    }
+
+    private static final String ctsAllTestsB =
+        "package dot.junit;\n" +
+        "import junit.framework.Test;\n" +
+        "import junit.framework.TestSuite;\n" +
+        "import com.android.hosttest.DeviceTestSuite;\n" +
+        "\n" +
+        "public class AllJunitHostTests extends DeviceTestSuite {\n" +
+        "    public static final Test suite() {\n" +
+        "        TestSuite suite = new TestSuite(\"CTS Host tests for all " +
+        " dalvik vm opcodes\");\n";
+
+    private String curAllTestsData = ctsAllTestsB;
+    private String curJunitFileName = null;
+    private String curJunitFileData = "";
+
+    private JavacBuildStep javacHostJunitBuildStep;
+
+    private void flushHostJunitFile() {
+        if (curJunitFileName != null) {
+            File toWrite = new File(curJunitFileName);
+            String absPath = toWrite.getAbsolutePath();
+            // add to java source files for later compilation
+            javacHostJunitBuildStep.addSourceFile(absPath);
+            // write file
+            curJunitFileData += "\n}\n";
+            writeToFileMkdir(toWrite, curJunitFileData);
+            curJunitFileName = null;
+            curJunitFileData = "";
+        }
+    }
+
+    private void ctsFinish() {
+        flushHostJunitFile();
+        curAllTestsData += "return suite;\n}\n}\n";
+        // suite is in package dot.junit.
+        String allTestsFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/dot/junit/AllJunitHostTests.java";
+        File toWrite = new File(allTestsFileName);
+        writeToFileMkdir(toWrite, curAllTestsData);
+        javacHostJunitBuildStep.addSourceFile(toWrite.getAbsolutePath());
+        javacHostJunitBuildStep.addSourceFile(new File(
+                HOSTJUNIT_SRC_OUTPUT_FOLDER + "/dot/junit/DeviceUtil.java").
+                getAbsolutePath());
+    }
+
+    private void openCTSHostFileFor(String pName, String classOnlyName) {
+        String sourceName = "JUnit_" + classOnlyName;
+        // Add to AllTests.java
+        String suiteline = "suite.addTestSuite(" + pName + "." + sourceName +
+        ".class);\n";
+        curAllTestsData += suiteline;
+        // flush previous JunitFile
+        flushHostJunitFile();
+
+        // prepare current testcase-file
+        curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" +
+        sourceName + ".java";
+        curJunitFileData = getWarningMessage() +
+        "package " + pName + ";\n" +
+        "import java.io.IOException;\n" +
+        "import junit.framework.TestCase;\n" +
+        "import com.android.hosttest.DeviceTestCase;\n" +
+        "import dot.junit.DeviceUtil;\n" +
+        "\n" +
+        "public class " + sourceName + " extends DeviceTestCase {\n";
+    }
+
+    private String getADBExecJavaLine(String classpath, String mainclass) {
+        return "DeviceUtil.adbExec(getDevice(), \"" + TARGET_JAR_ROOT_PATH + "\", \"" + classpath +
+        "\", \"" + mainclass + "\");";
+    }
+
+    private String getWarningMessage() {
+        return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
+    }
+
+    private void addCTSHostMethod(String pName, String method, MethodData md,
+            Set<String> dependentTestClassNames) {
+        curJunitFileData += "public void " + method + "() throws Exception {\n";
+        final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
+                TARGET_JAR_ROOT_PATH);
+
+        // push class with Main jar.
+        String mjar = "Main_" + method + ".jar";
+        String pPath = pName.replaceAll("\\.","/");
+        String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
+
+        // for each dependency:
+        // adb push dot/junit/opcodes/add_double_2addr/Main_testN2.jar
+        // /data/local/tmp/Main_testN2.jar
+        String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
+        for (String depFqcn : dependentTestClassNames) {
+            int lastDotPos = depFqcn.lastIndexOf('.');
+            String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
+            String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
+                    sourceName);
+            cp += ":" + targetName;
+            // dot.junit.opcodes.invoke_interface_range.ITest
+            // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
+        }
+
+        //"dot.junit.opcodes.add_double_2addr.Main_testN2";
+        String mainclass = pName + ".Main_" + method;
+        curJunitFileData += "    " + getADBExecJavaLine(cp, mainclass);
+        curJunitFileData += "}\n\n";
+    }
+
+    private void handleTests() throws IOException {
+        System.out.println("collected " + testMethodsCnt + " test methods in " +
+                testClassCnt + " junit test classes");
+        String datafileContent = "";
+        Set<BuildStep> targets = new TreeSet<BuildStep>();
+
+        javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
+
+
+        JavacBuildStep javacBuildStep = new JavacBuildStep(
+                CLASSES_OUTPUT_FOLDER, CLASS_PATH);
+
+        for (Entry<String, List<String>> entry : map.entrySet()) {
+
+            String fqcn = entry.getKey();
+            int lastDotPos = fqcn.lastIndexOf('.');
+            String pName = fqcn.substring(0, lastDotPos);
+            String classOnlyName = fqcn.substring(lastDotPos + 1);
+            String instPrefix = "new " + classOnlyName + "()";
+
+            openCTSHostFileFor(pName, classOnlyName);
+
+            List<String> methods = entry.getValue();
+            Collections.sort(methods, new Comparator<String>() {
+                public int compare(String s1, String s2) {
+                    // TODO sort according: test ... N, B, E, VFE
+                    return s1.compareTo(s2);
+                }
+            });
+            for (String method : methods) {
+                // e.g. testN1
+                if (!method.startsWith("test")) {
+                    throw new RuntimeException("no test method: " + method);
+                }
+
+                // generate the Main_xx java class
+
+                // a Main_testXXX.java contains:
+                // package <packagenamehere>;
+                // public class Main_testxxx {
+                // public static void main(String[] args) {
+                // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
+                // }
+                // }
+                MethodData md = parseTestMethod(pName, classOnlyName, method);
+                String methodContent = md.methodBody;
+
+                Set<String> dependentTestClassNames = parseTestClassName(pName,
+                        classOnlyName, methodContent);
+
+                addCTSHostMethod(pName, method, md, dependentTestClassNames);
+
+
+                if (dependentTestClassNames.isEmpty()) {
+                    continue;
+                }
+
+
+                String content = getWarningMessage() +
+                "package " + pName + ";\n" +
+                "import " + pName + ".d.*;\n" +
+                "import dot.junit.*;\n" +
+                "public class Main_" + method + " extends DxAbstractMain {\n" +
+                "    public static void main(String[] args) throws Exception {" +
+                methodContent + "\n}\n";
+
+                String fileName = getFileName(pName, method, ".java");
+                File sourceFile = getFileFromPackage(pName, method);
+
+                File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
+                        getFileName(pName, method, ".class"));
+                // if (sourceFile.lastModified() > classFile.lastModified()) {
+                writeToFile(sourceFile, content);
+                javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
+
+                BuildStep dexBuildStep = generateDexBuildStep(
+                        CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
+                targets.add(dexBuildStep);
+                // }
+
+
+                // prepare the entry in the data file for the bash script.
+                // e.g.
+                // main class to execute; opcode/constraint; test purpose
+                // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
+                // (#1)
+
+                char ca = method.charAt("test".length()); // either N,B,E,
+                // or V (VFE)
+                String comment;
+                switch (ca) {
+                    case 'N':
+                        comment = "Normal #" + method.substring(5);
+                        break;
+                    case 'B':
+                        comment = "Boundary #" + method.substring(5);
+                        break;
+                    case 'E':
+                        comment = "Exception #" + method.substring(5);
+                        break;
+                    case 'V':
+                        comment = "Verifier #" + method.substring(7);
+                        break;
+                    default:
+                        throw new RuntimeException("unknown test abbreviation:" + method + " for " +
+                                fqcn);
+                }
+
+                String line = pName + ".Main_" + method + ";";
+                for (String className : dependentTestClassNames) {
+                    line += className + " ";
+                }
+
+
+                // test description
+                String[] pparts = pName.split("\\.");
+                // detail e.g. add_double
+                String detail = pparts[pparts.length-1];
+                // type := opcode | verify
+                String type = pparts[pparts.length-2];
+
+                String description;
+                if ("format".equals(type)) {
+                    description = "format";
+                } else if ("opcodes".equals(type)) {
+                    // Beautify name, so it matches the actual mnemonic
+                    detail = detail.replaceAll("_", "-");
+                    detail = detail.replace("-from16", "/from16");
+                    detail = detail.replace("-high16", "/high16");
+                    detail = detail.replace("-lit8", "/lit8");
+                    detail = detail.replace("-lit16", "/lit16");
+                    detail = detail.replace("-4", "/4");
+                    detail = detail.replace("-16", "/16");
+                    detail = detail.replace("-32", "/32");
+                    detail = detail.replace("-jumbo", "/jumbo");
+                    detail = detail.replace("-range", "/range");
+                    detail = detail.replace("-2addr", "/2addr");
+
+                    // Unescape reserved words
+                    detail = detail.replace("opc-", "");
+
+                    description = detail;
+                } else if ("verify".equals(type)) {
+                    description = "verifier";
+                } else {
+                    description = type + " " + detail;
+                }
+
+                String details = (md.title != null ? md.title : "");
+                if (md.constraint != null) {
+                    details = " Constraint " + md.constraint + ", " + details;
+                }
+                if (details.length() != 0) {
+                    details = details.substring(0, 1).toUpperCase() + details.substring(1);
+                }
+
+                line += ";" + description + ";" + comment + ";" + details;
+
+                datafileContent += line + "\n";
+                generateBuildStepFor(pName, method, dependentTestClassNames,
+                        targets);
+            }
+
+
+        }
+
+        // write latest HOSTJUNIT generated file and AllTests.java
+        ctsFinish();
+
+        File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
+        scriptDataDir.mkdirs();
+        writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
+
+        if (!javacHostJunitBuildStep.build()) {
+            System.out.println("main javac cts-host-hostjunit-classes build step failed");
+            System.exit(1);
+        }
+
+        if (javacBuildStep.build()) {
+            for (BuildStep buildStep : targets) {
+                if (!buildStep.build()) {
+                    System.out.println("building failed. buildStep: " +
+                            buildStep.getClass().getName() + ", " + buildStep);
+                    System.exit(1);
+                }
+            }
+        } else {
+            System.out.println("main javac dalvik-cts-buildutil build step failed");
+            System.exit(1);
+        }
+    }
+
+    private void generateBuildStepFor(String pName, String method,
+            Set<String> dependentTestClassNames, Set<BuildStep> targets) {
+
+
+        for (String dependentTestClassName : dependentTestClassNames) {
+            generateBuildStepForDependant(dependentTestClassName, targets);
+        }
+    }
+
+    private void generateBuildStepForDependant(String dependentTestClassName,
+            Set<BuildStep> targets) {
+
+        File sourceFolder = new File(JAVASRC_FOLDER);
+        String fileName = dependentTestClassName.replace('.', '/').trim();
+
+        if (new File(sourceFolder, fileName + ".dfh").exists()) {
+
+            BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
+                    JAVASRC_FOLDER, fileName + ".dfh");
+            BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".dex");
+
+            DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
+
+            BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".jar");
+            JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
+                    "classes.dex", jarFile, true);
+            jarBuildStep.addChild(buildStep);
+
+            targets.add(jarBuildStep);
+            return;
+        }
+
+        if (new File(sourceFolder, fileName + ".d").exists()) {
+
+            BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
+                    JAVASRC_FOLDER, fileName + ".d");
+            BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".dex");
+
+            DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
+
+            BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".jar");
+
+            JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
+                    "classes.dex", jarFile, true);
+            jarBuildStep.addChild(buildStep);
+            targets.add(jarBuildStep);
+            return;
+        }
+
+        if (new File(sourceFolder, fileName + ".java").exists()) {
+
+            BuildStep dexBuildStep = generateDexBuildStep(
+                    COMPILED_CLASSES_FOLDER, fileName);
+            targets.add(dexBuildStep);
+            return;
+        }
+
+        try {
+            if (Class.forName(dependentTestClassName) != null) {
+
+                BuildStep dexBuildStep = generateDexBuildStep(
+                        COMPILED_CLASSES_FOLDER, fileName);
+                targets.add(dexBuildStep);
+                return;
+            }
+        } catch (ClassNotFoundException e) {
+            // do nothing
+        }
+
+        throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
+                dependentTestClassName + ";" + fileName);
+    }
+
+    private BuildStep generateDexBuildStep(String classFileFolder,
+            String classFileName) {
+        BuildStep.BuildFile classFile = new BuildStep.BuildFile(
+                classFileFolder, classFileName + ".class");
+
+        BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
+                classFileName + "_tmp.jar");
+
+        JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
+                ".class", tmpJarFile, false);
+
+        BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
+                classFileName + ".jar");
+
+        DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
+                true);
+
+        dexBuildStep.addChild(jarBuildStep);
+        return dexBuildStep;
+
+    }
+
+    /**
+     * @param pName
+     * @param classOnlyName
+     * @param methodSource
+     * @return testclass names
+     */
+    private Set<String> parseTestClassName(String pName, String classOnlyName,
+            String methodSource) {
+        Set<String> entries = new HashSet<String>();
+        String opcodeName = classOnlyName.substring(5);
+
+        Scanner scanner = new Scanner(methodSource);
+
+        String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
+                "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
+
+        String token = null;
+        for (String pattern : patterns) {
+            token = scanner.findWithinHorizon(pattern, methodSource.length());
+            if (token != null) {
+                break;
+            }
+        }
+
+        if (token == null) {
+            System.err.println("warning: failed to find dependent test class name: " + pName +
+                    ", " + classOnlyName + " in methodSource:\n" + methodSource);
+            return entries;
+        }
+
+        MatchResult result = scanner.match();
+
+        entries.add((pName + ".d." + result.group(1)).trim());
+
+        // search additional @uses directives
+        Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
+        Matcher m = p.matcher(methodSource);
+        while (m.find()) {
+            String res = m.group(1);
+            entries.add(res.trim());
+        }
+
+        // lines with the form @uses
+        // dot.junit.opcodes.add_double.jm.T_add_double_2
+        // one dependency per one @uses
+        // TODO
+
+        return entries;
+    }
+
+    private MethodData parseTestMethod(String pname, String classOnlyName,
+            String method) {
+
+        String path = pname.replaceAll("\\.", "/");
+        String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
+        File f = new File(absPath);
+
+        Scanner scanner;
+        try {
+            scanner = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+        
+        String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
+        
+        String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
+        if (token == null) {
+            throw new RuntimeException("cannot find method source of 'public void " + method +
+                    "' in file '" + absPath + "'");
+        }
+
+        MatchResult result = scanner.match();
+        result.start();
+        result.end();
+
+        StringBuilder builder = new StringBuilder();
+        //builder.append(token);
+
+        try {
+            FileReader reader = new FileReader(f);
+            reader.skip(result.end());
+
+            char currentChar;
+            int blocks = 1;
+            while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
+                switch (currentChar) {
+                    case '}': {
+                        blocks--;
+                        builder.append(currentChar);
+                        break;
+                    }
+                    case '{': {
+                        blocks++;
+                        builder.append(currentChar);
+                        break;
+                    }
+                    default: {
+                        builder.append(currentChar);
+                        break;
+                    }
+                }
+            }
+            if (reader != null) {
+                reader.close();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("failed to parse", e);
+        }
+
+        // find the @title/@constraint in javadoc comment for this method
+        Scanner scanner2;
+        try {
+            // using platform's default charset
+            scanner2 = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+        // using platform's default charset
+        String all = new String(FileUtils.readFile(f));
+        // System.out.println("grepping javadoc found for method " + method +
+        // " in " + pname + "," + classOnlyName);
+        String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
+        Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
+        Matcher m = p.matcher(all);
+        String title = null, constraint = null;
+        if (m.find()) {
+            String res = m.group(1);
+            // System.out.println("res: " + res);
+            // now grep @title and @constraint
+            Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
+            .matcher(res);
+            if (titleM.find()) {
+                title = titleM.group(1).replaceAll("\\n     \\*", "");
+                title = title.replaceAll("\\n", " ");
+                title = title.trim();
+                // System.out.println("title: " + title);
+            } else {
+                System.err.println("warning: no @title found for method " + method + " in " + pname +
+                        "," + classOnlyName);
+            }
+            // constraint can be one line only
+            Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
+                    res);
+            if (constraintM.find()) {
+                constraint = constraintM.group(1);
+                constraint = constraint.trim();
+                // System.out.println("constraint: " + constraint);
+            } else if (method.contains("VFE")) {
+                System.err
+                .println("warning: no @constraint for for a VFE method:" + method + " in " +
+                        pname + "," + classOnlyName);
+            }
+        } else {
+            System.err.println("warning: no javadoc found for method " + method + " in " + pname +
+                    "," + classOnlyName);
+        }
+        MethodData md = new MethodData();
+        md.methodBody = builder.toString();
+        md.constraint = constraint;
+        md.title = title;
+        if (scanner != null) {
+            scanner.close();
+        }
+        if (scanner2 != null) {
+            scanner.close();
+        }
+        return md;
+    }
+
+    private void writeToFileMkdir(File file, String content) {
+        File parent = file.getParentFile();
+        if (!parent.exists() && !parent.mkdirs()) {
+            throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
+        }
+        writeToFile(file, content);
+    }
+
+    private void writeToFile(File file, String content) {
+        try {
+            if (file.length() == content.length()) {
+                FileReader reader = new FileReader(file);
+                char[] charContents = new char[(int) file.length()];
+                reader.read(charContents);
+                String contents = new String(charContents);
+                if (contents.equals(content)) {
+                    // System.out.println("skipping identical: "
+                    // + file.getAbsolutePath());
+                    return;
+                }
+            }
+
+            //System.out.println("writing file " + file.getAbsolutePath());
+
+            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
+                    new FileOutputStream(file), "utf-8"));
+            bw.write(content);
+            bw.close();
+        } catch (Exception e) {
+            throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+    }
+
+    private File getFileFromPackage(String pname, String methodName)
+    throws IOException {
+        // e.g. dxc.junit.argsreturns.pargsreturn
+        String path = getFileName(pname, methodName, ".java");
+        String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
+        File dirPath = new File(absPath);
+        File parent = dirPath.getParentFile();
+        if (!parent.exists() && !parent.mkdirs()) {
+            throw new IOException("failed to create directory: " + absPath);
+        }
+        return dirPath;
+    }
+
+    private String getFileName(String pname, String methodName,
+            String extension) {
+        String path = pname.replaceAll("\\.", "/");
+        return new File(path, "Main_" + methodName + extension).getPath();
+    }
+}
diff --git a/tools/vm-tests-tf/src/util/build/BuildStep.java b/tools/vm-tests-tf/src/util/build/BuildStep.java
new file mode 100644
index 0000000..dbc6bca
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/BuildStep.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.HashSet;
+import java.util.Set;
+
+abstract class BuildStep implements Comparable<BuildStep> {
+
+    BuildFile inputFile;
+    BuildFile outputFile;
+
+    static class BuildFile {
+        final File folder;
+        final File fileName;
+
+        BuildFile(String folder, String fileName) {
+            this.folder = new File(folder);
+            this.fileName = new File(this.folder, fileName);
+        }
+
+        String getPath() {
+            return fileName.getAbsolutePath();
+        }
+
+        @Override
+        public int hashCode() {
+            return fileName.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) return false;
+            if (this == obj) return true;
+            if (getClass() == obj.getClass()) {
+                BuildFile other = (BuildFile) obj;
+                return fileName.equals(other.fileName);
+            }
+            return false;
+        }
+    }
+
+    BuildStep(BuildFile inputFile, BuildFile outputFile) {
+        if (inputFile == null) {
+            throw new NullPointerException("inputFile is null");
+        }
+        if (outputFile == null) {
+            throw new NullPointerException("outputFile is null");
+        }
+        this.inputFile = inputFile;
+        this.outputFile = outputFile;
+    }
+
+    BuildStep() {
+    }
+
+    private Set<BuildStep> children;
+
+    boolean build() {
+        if (children != null) {
+            for (BuildStep child : children) {
+                if (!child.build()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) return false;
+        if (obj == this) return true;
+        return this.getClass() == obj.getClass();
+    }
+
+    @Override
+    public abstract int hashCode();
+
+    public void addChild(BuildStep child) {
+        if (children == null) {
+            children = new HashSet<BuildStep>();
+        }
+        children.add(child);
+    }
+
+    public static void copyFile(File in, File out) throws IOException {
+        FileChannel inChannel = new FileInputStream(in).getChannel();
+        FileChannel outChannel = new FileOutputStream(out).getChannel();
+        try {
+            inChannel.transferTo(0, inChannel.size(), outChannel);
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            if (inChannel != null) inChannel.close();
+            if (outChannel != null) outChannel.close();
+        }
+    }
+
+    public int compareTo(BuildStep o) {
+        return (inputFile == o.inputFile ? 0 : (inputFile != null
+                ? (o.inputFile != null ? inputFile.getPath().compareTo(
+                        o.inputFile.getPath()) : 1) : -1));
+    }
+}
diff --git a/tools/vm-tests-tf/src/util/build/DFHBuildStep.java b/tools/vm-tests-tf/src/util/build/DFHBuildStep.java
new file mode 100644
index 0000000..47a81bf
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/DFHBuildStep.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import dxconvext.ClassFileAssembler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.OutputStream;
+import java.io.Reader;
+
+public class DFHBuildStep extends BuildStep {
+
+    public DFHBuildStep(BuildFile inputFile, BuildFile outputFile) {
+        super(inputFile, outputFile);
+    }
+
+    @Override
+    boolean build() {
+        if (super.build()) {
+            File out_dir = outputFile.fileName.getParentFile();
+            if (!out_dir.exists() && !out_dir.mkdirs()) {
+                System.err.println("failed to create dir: "
+                        + out_dir.getAbsolutePath());
+                return false;
+            }
+
+            ClassFileAssembler cfAssembler = new ClassFileAssembler();
+            Reader r;
+            OutputStream os;
+            try {
+                r = new FileReader(inputFile.fileName);
+                os = new FileOutputStream(outputFile.fileName);
+            } catch (FileNotFoundException e) {
+                System.err.println(e);
+                return false;
+            }
+            try {
+                // cfAssembler throws a runtime exception
+                cfAssembler.writeClassFile(r, os, true);
+            } catch (RuntimeException e) {
+                System.err.println("error in DFHBuildStep for inputfile "+inputFile.fileName+", outputfile "+outputFile.fileName);
+                throw e;
+            }
+            
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+
+        if (super.equals(obj)) {
+            return inputFile.equals(((DFHBuildStep) obj).inputFile)
+                    && outputFile.equals(((DFHBuildStep) obj).outputFile);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return (inputFile == null ? 31 : inputFile.hashCode())
+                ^ (outputFile == null ? 37 : outputFile.hashCode());
+    }
+}
diff --git a/tools/vm-tests-tf/src/util/build/DasmBuildStep.java b/tools/vm-tests-tf/src/util/build/DasmBuildStep.java
new file mode 100644
index 0000000..b77a491
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/DasmBuildStep.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import dasm.DAsm;
+import dasm.DasmError;
+import dasm.Utils;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+class DasmBuildStep extends BuildStep {
+
+
+    boolean generate_linenum = false;
+
+    DasmBuildStep(BuildFile inputFile, BuildFile outputFile) {
+        super(inputFile, outputFile);
+
+    }
+
+    @Override
+    boolean build() {
+        if (super.build()) {
+            return assemble(inputFile.fileName);
+        }
+        return false;
+    }
+
+    private static Reader createReader(String fname) throws IOException {
+        FileInputStream fs = new FileInputStream(fname);
+        InputStreamReader ir;
+        ir = new InputStreamReader(fs);
+        return new BufferedReader(ir);
+    }
+    
+    private boolean assemble(File file) {
+        DAsm dAsm = new DAsm();
+        String fname = file.getAbsolutePath();
+        
+        // read and parse .d file
+        Reader inp = null; 
+        try {
+            inp = createReader(fname);
+            dAsm.readD(inp, new File(fname).getName(), generate_linenum);
+            close(inp);
+        } catch(DasmError e) {
+            if(BuildDalvikSuite.DEBUG)
+                e.printStackTrace();
+            System.err.println("DASM Error: " + e.getMessage());
+        } catch(Exception e) {
+             if(BuildDalvikSuite.DEBUG)
+                 e.printStackTrace();
+             System.err.println("Exception <" + e.getClass().getName() + ">" + e.getMessage() + 
+                         " while reading and parsing " + fname);
+             return false;
+             
+        }
+        finally {
+            close(inp);
+        }
+        
+        if(dAsm.errorCount() > 0) {
+            System.err.println("Found " + dAsm.errorCount() + " errors " +
+                    " while reading and parsing " + fname);
+                return false;
+        }
+
+        String class_path[] = Utils.getClassFieldFromString(dAsm.getClassName());
+        String class_name = class_path[1];
+
+        // determine where to place .dex file
+        String dest_dir = outputFile.folder.getAbsolutePath();
+        if (class_path[0] != null) {
+            String class_dir = class_path[0].replaceAll("/|\\.", Character.toString(File.separatorChar));                                           
+            if (dest_dir != null) {
+                dest_dir = dest_dir + File.separator + class_dir;
+            } else {
+                dest_dir = class_dir;
+            }
+        }
+            
+        File out_file = null;
+        
+        if (dest_dir == null) {
+            out_file = new File(class_name + ".dex");
+        } else {
+            out_file = new File(dest_dir, class_name + ".dex");
+
+            // check that dest_dir exists
+            File dest = new File(dest_dir);
+            if (!dest.exists()) {
+                dest.mkdirs();
+            }
+
+            if (!dest.isDirectory()) {
+                System.err.println("Cannot create directory " + dest_dir);
+                return false;
+            }
+        }
+         
+        // write output
+        FileOutputStream outp = null;
+
+        try {
+            outp = new FileOutputStream(out_file);
+            dAsm.write(outp, null);
+        } catch(Exception e) {
+                if(BuildDalvikSuite.DEBUG)
+                e.printStackTrace();
+                System.err.println("Exception <" + e.getClass().getName() + ">" + e.getMessage() + 
+                        " while writing " + out_file.getPath());
+
+                close(outp);
+
+                out_file.delete();
+
+                return false;
+       }
+       finally {
+           close(outp);
+       }
+
+       return true;
+    }
+    
+    private static void close(Closeable c) {
+        if(c == null)
+            return;
+        try {
+            c.close();
+        } catch(IOException e) {
+            
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (super.equals(obj)) {
+            DasmBuildStep other = (DasmBuildStep) obj;
+
+            return inputFile.equals(other.inputFile)
+                    && generate_linenum == other.generate_linenum
+                    && outputFile.equals(other.outputFile);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return inputFile.hashCode() ^ outputFile.hashCode()
+                ^ (generate_linenum ? 31 : 37);
+    }
+}
diff --git a/tools/vm-tests-tf/src/util/build/DeviceUtil.java.template b/tools/vm-tests-tf/src/util/build/DeviceUtil.java.template
new file mode 100644
index 0000000..c64a138
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/DeviceUtil.java.template
@@ -0,0 +1,72 @@
+
+package dot.junit;
+
+
+import com.android.ddmlib.IDevice;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Scanner;
+
+public class DeviceUtil {
+
+    private static boolean DEBUG = System.getProperty("cts.vm-tests.debug") != null;
+
+    /**
+     * Executes the command and its arguments in a native process.
+     *
+     * @param commandAndArgs a string array to be passed containing the
+     *            executable and its arguments
+     * @param okIndicator if not null, this String must occur in the stdout of
+     *            the executable (since only checking for the return code is not
+     *            sufficient e.g. for adb shell cmd)
+     * @throws Exception thrown by the underlying command in case of an error.
+     */
+    public static void digestCommand(String[] commandAndArgs, String okIndicator) {
+        RuntimeException re = null;
+        try {
+            if (DEBUG) {
+                String c = "";
+                for (int i = 0; i < commandAndArgs.length; i++) {
+                    c += commandAndArgs[i] + " ";
+                }
+                System.out.print("com: " + c);
+            }
+            StringBuilder sb = new StringBuilder();
+            ProcessBuilder pb = new ProcessBuilder(commandAndArgs).redirectErrorStream(true);
+            Process p = pb.start();
+
+            InputStream is = p.getInputStream();
+            Scanner scanner = new Scanner(is);
+            int retCode = p.waitFor();
+            while (scanner.hasNextLine()) {
+                sb.append(scanner.nextLine());
+            }
+            scanner.close();
+            if (retCode != 0 || (okIndicator != null && !sb.toString().contains(okIndicator))) {
+                String msg = sb.toString() + "\nreturn code: " + retCode;
+                re = new RuntimeException(msg);
+                if (DEBUG) System.out.println("-> error! msg:"+msg);
+            } else {
+                if (DEBUG) System.out.println(" -> " + retCode);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Exception occurred: " + e.getClass().getName() + ", msg:"
+                    + e.getMessage());
+        } finally {
+            if (re != null) {
+                throw re;
+            }
+        }
+    }
+
+    public static void adbExec(IDevice device, String tmpdir, String classpath, String mainclass) {
+        DeviceUtil.digestCommand(new String[] {"adb", "-s", device.getSerialNumber(), "shell",
+               "ANDROID_DATA=" + tmpdir, "dalvikvm", "-Xint:portable", "-Xmx512M", "-Xss32K",
+               "-Djava.io.tmpdir=" + tmpdir, "-classpath", classpath, mainclass, "&&",
+               "echo", "mk_dalvikvmok" }, "mk_dalvikvmok");
+    }
+ }
diff --git a/tools/vm-tests-tf/src/util/build/DexBuildStep.java b/tools/vm-tests-tf/src/util/build/DexBuildStep.java
new file mode 100644
index 0000000..6aba51c
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/DexBuildStep.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import com.android.dx.command.dexer.Main;
+import java.io.IOException;
+
+public class DexBuildStep extends BuildStep {
+
+    private final boolean deleteInputFileAfterBuild;
+
+    DexBuildStep(BuildFile inputFile, BuildFile outputFile,
+            boolean deleteInputFileAfterBuild) {
+        super(inputFile, outputFile);
+        this.deleteInputFileAfterBuild = deleteInputFileAfterBuild;
+    }
+
+    @Override
+    boolean build() {
+
+        if (super.build()) {
+            Main.Arguments args = new Main.Arguments();
+
+            args.jarOutput = true;
+            args.fileNames = new String[] {inputFile.fileName.getAbsolutePath()};
+
+            args.outName = outputFile.fileName.getAbsolutePath();
+
+            int result = 0;
+            try {
+                result = Main.run(args);
+            } catch (IOException e) {
+                e.printStackTrace();
+                return false;
+            }
+
+            if (result == 0) {
+                if (deleteInputFileAfterBuild) {
+                    inputFile.fileName.delete();
+                }
+                return true;
+            } else {
+                System.err.println("exception while dexing "
+                        + inputFile.fileName.getAbsolutePath() + " to "
+                        + args.outName);
+                return false;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return inputFile.hashCode() ^ outputFile.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (super.equals(obj)) {
+            DexBuildStep other = (DexBuildStep) obj;
+
+            return inputFile.equals(other.inputFile)
+                    && outputFile.equals(other.outputFile);
+        }
+        return false;
+    }
+
+
+}
diff --git a/tools/vm-tests-tf/src/util/build/JarBuildStep.java b/tools/vm-tests-tf/src/util/build/JarBuildStep.java
new file mode 100644
index 0000000..776e905
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/JarBuildStep.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import sun.tools.jar.Main;
+
+import java.io.File;
+import java.io.IOException;
+
+
+public class JarBuildStep extends BuildStep {
+
+    String destFileName;
+    private final boolean deleteInputFileAfterBuild;
+
+    public JarBuildStep(BuildFile inputFile, String destFileName,
+            BuildFile outputFile, boolean deleteInputFileAfterBuild) {
+        super(inputFile, outputFile);
+        this.destFileName = destFileName;
+        this.deleteInputFileAfterBuild = deleteInputFileAfterBuild;
+    }
+
+    @Override
+    boolean build() {
+        if (super.build()) {
+            File tempFile = new File(inputFile.folder, destFileName);
+            try {
+                if (!inputFile.fileName.equals(tempFile)) {
+                    copyFile(inputFile.fileName, tempFile);
+                } else {
+                    tempFile = null;
+                }
+            } catch (IOException e) {
+                System.err.println("io exception:"+e.getMessage());
+                e.printStackTrace();
+                return false;
+            }
+
+            File outDir = outputFile.fileName.getParentFile();
+            if (!outDir.exists() && !outDir.mkdirs()) {
+                System.err.println("failed to create output dir: "
+                        + outDir.getAbsolutePath());
+                return false;
+            }
+            String[] arguments = new String[] {
+                    "-cMf", outputFile.fileName.getAbsolutePath(), "-C",
+                    inputFile.folder.getAbsolutePath(), destFileName};
+            Main main = new Main(System.out, System.err, "jar");
+            boolean success = main.run(arguments);
+
+            if (success) {
+                if (tempFile != null) {
+                    tempFile.delete();
+                }
+                if (deleteInputFileAfterBuild) {
+                    inputFile.fileName.delete();
+                }
+            } else {
+                System.err.println("exception in JarBuildStep while calling jar with args:" +
+                        " \"-cMf\", "+outputFile.fileName.getAbsolutePath()+", \"-C\"," + 
+                        inputFile.folder.getAbsolutePath()+", "+ destFileName);
+            }
+            return success;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return inputFile.hashCode() ^ outputFile.hashCode()
+                ^ destFileName.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (super.equals(obj)) {
+            JarBuildStep other = (JarBuildStep) obj;
+            return inputFile.equals(other.inputFile)
+                    && outputFile.equals(other.outputFile)
+                    && destFileName.equals(other.destFileName);
+
+        }
+        return false;
+    }
+
+}
diff --git a/tools/vm-tests-tf/src/util/build/JavacBuildStep.java b/tools/vm-tests-tf/src/util/build/JavacBuildStep.java
new file mode 100644
index 0000000..7d7033f
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/JavacBuildStep.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util.build;
+
+import com.sun.tools.javac.Main;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+public class JavacBuildStep extends BuildStep {
+
+    private final String destPath;
+    private final String classPath;
+    private final Set<String> sourceFiles = new HashSet<String>();
+    public JavacBuildStep(String destPath, String classPath) {
+        this.destPath = destPath;
+        this.classPath = classPath;
+    }
+    
+    public void addSourceFile(String sourceFile)
+    {
+        sourceFiles.add(sourceFile);
+    }
+    
+    @Override
+    boolean build() {
+        if (super.build())
+        {
+            if (sourceFiles.isEmpty())
+            {
+                return true;
+            }
+            
+            File destFile = new File(destPath);
+            if (!destFile.exists() && !destFile.mkdirs())
+            {
+                System.err.println("failed to create destination dir");
+                return false;
+            }
+            int args = 4;
+            String[] commandLine = new String[sourceFiles.size()+args];
+            commandLine[0] = "-classpath";
+            commandLine[1] = classPath;
+            commandLine[2] = "-d";
+            commandLine[3] = destPath;
+             
+            String[] files = new String[sourceFiles.size()];
+            sourceFiles.toArray(files);
+            
+            System.arraycopy(files, 0, commandLine, args, files.length);
+            
+            
+            return Main.compile(commandLine, new PrintWriter(System.err)) == 0;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // TODO Auto-generated method stub
+        if (super.equals(obj))
+        {
+            JavacBuildStep other = (JavacBuildStep) obj;
+            return destPath.equals(other.destPath) 
+                && classPath.equals(other.classPath)
+                && sourceFiles.equals(other.sourceFiles);
+        }
+        return false;
+    }
+    
+    @Override
+    public int hashCode() {
+        return destPath.hashCode() ^ classPath.hashCode() ^ sourceFiles.hashCode();
+    }
+}