diff options
author | Jesse Wilson <jessewilson@google.com> | 2010-04-16 19:33:37 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2010-04-16 19:33:37 -0700 |
commit | 94bab211fe0c871aa4c0b9f3ee6a021f3804174a (patch) | |
tree | f4126f8257a87f2f92fedc0389446a74d36f96df | |
parent | d4ade6970a3dfd471cd4f8b233c7f0e7528cd3c4 (diff) | |
download | libcore-94bab211fe0c871aa4c0b9f3ee6a021f3804174a.zip libcore-94bab211fe0c871aa4c0b9f3ee6a021f3804174a.tar.gz libcore-94bab211fe0c871aa4c0b9f3ee6a021f3804174a.tar.bz2 |
Teaching vogar to run prebuilt .jar files.
Also trying to simplify some internals. We always build to
intermediate .jar files now.
I've renamed TestFinder to RunnerSpec and fixed its users.
22 files changed, 335 insertions, 388 deletions
diff --git a/tools/runner/java/vogar/Action.java b/tools/runner/java/vogar/Action.java index 190c457..5a562c7 100644 --- a/tools/runner/java/vogar/Action.java +++ b/tools/runner/java/vogar/Action.java @@ -17,7 +17,6 @@ package vogar; import java.io.File; -import vogar.target.Runner; /** * A named job such as a test or benchmark run. This class tracks the resource @@ -27,36 +26,34 @@ public final class Action { private final String name; private final String actionClass; - private final File actionDirectory; - private final File actionJava; - private final String description; - private final Class<? extends Runner> runnerClass; - private final File runnerJava; - private final Classpath runnerClasspath; + private final File resourcesDirectory; + private final File javaFile; + private final RunnerSpec runnerSpec; private File userDir = new File(System.getProperty("user.dir")); - public Action(String name, String actionClass, File actionDirectory, - File actionJava, String description, Class<? extends Runner> runnerClass, - File runnerJava, Classpath runnerClasspath) { + public Action(String name, String actionClass, File resourcesDirectory, + File javaFile, RunnerSpec runnerSpec) { this.name = name; this.actionClass = actionClass; - this.actionDirectory = actionDirectory; - this.actionJava = actionJava; - this.description = description; - this.runnerClass = runnerClass; - this.runnerJava = runnerJava; - this.runnerClasspath = runnerClasspath; + this.resourcesDirectory = resourcesDirectory; + this.javaFile = javaFile; + this.runnerSpec = runnerSpec; } /** - * Returns the local directory containing this action's java file. + * Returns the local directory containing this action's required resource + * files, or {@code null} if this action is standalone. */ - public File getJavaDirectory() { - return actionDirectory; + public File getResourcesDirectory() { + return resourcesDirectory; } + /** + * Returns this action's java file, or {@code null} if this file wasn't + * built from source. + */ public File getJavaFile() { - return actionJava; + return javaFile; } /** @@ -74,12 +71,8 @@ public final class Action { return name; } - /** - * Returns an English description of this action, or null if no such - * description is known. - */ - public String getDescription() { - return description; + public RunnerSpec getRunnerSpec() { + return runnerSpec; } /** @@ -94,18 +87,6 @@ public final class Action { return userDir; } - public Class<? extends Runner> getRunnerClass() { - return runnerClass; - } - - public File getRunnerJava() { - return runnerJava; - } - - public Classpath getRunnerClasspath() { - return runnerClasspath; - } - @Override public String toString() { return name; } diff --git a/tools/runner/java/vogar/ActivityMode.java b/tools/runner/java/vogar/ActivityMode.java index 795b6a2..da49c89 100644 --- a/tools/runner/java/vogar/ActivityMode.java +++ b/tools/runner/java/vogar/ActivityMode.java @@ -19,17 +19,13 @@ package vogar; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.logging.Logger; import vogar.commands.Aapt; import vogar.commands.Command; import vogar.commands.Dx; -import vogar.commands.Mkdir; import vogar.commands.Rm; /** @@ -43,114 +39,47 @@ final class ActivityMode extends Mode { ActivityMode(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, boolean cleanBefore, boolean cleanAfter, - File deviceRunnerDir) { + File deviceRunnerDir, Classpath classpath) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, monitorPort, localTemp, deviceRunnerDir), - sdkJar, javacArgs, monitorPort); + sdkJar, javacArgs, monitorPort, classpath); } private EnvironmentDevice getEnvironmentDevice() { return (EnvironmentDevice) environment; } - @Override protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) { - testRunnerJava.add(new File("dalvik/libcore/tools/runner/lib/TestActivity.java")); - super.prepare(testRunnerJava, testRunnerClasspath); + @Override protected void prepare(Set<RunnerSpec> runners) { + runnerJava.add(new File("dalvik/libcore/tools/runner/lib/TestActivity.java")); + super.prepare(runners); } - @Override protected void postCompileRunner() { - } - - @Override protected void postCompile(Action action) { + @Override protected void postCompile(Action action, File jar) { logger.fine("aapt and push " + action.getName()); - // Some things of note: - // 1. we can't put multiple dex files in one apk - // 2. we can't just give dex multiple jars with conflicting class names - // 3. dex is slow if we give it too much to chew on - // 4. dex can run out of memory if given too much to chew on + // We can't put multiple dex files in one apk. + // We can't just give dex multiple jars with conflicting class names // With that in mind, the APK packaging strategy is as follows: - // 1. make an empty classes temporary directory - // 2. add test runner classes - // 3. find original jar test came from, add contents to classes - // 4. add supported runner classes specified by finder - // 5. add latest test classes to output - // 6. dx to create a dex - // 7. aapt the dex to create apk - // 8. sign the apk - // 9. install the apk - File packagingDir = makePackagingDirectory(action); - addRunnerClasses(packagingDir); - List<File> found = new ArrayList<File>(); - File originalJar = findOriginalJar(action); - if (originalJar != null) { - found.add(originalJar); - } - found.addAll(action.getRunnerClasspath().getElements()); - extractJars(packagingDir, found); - addActionClasses(action, packagingDir); - File dex = createDex(action, packagingDir); + // 1. dx to create a dex + // 2. aapt the dex to create apk + // 3. sign the apk + // 4. install the apk + File dex = createDex(action, jar); File apkUnsigned = createApk(action, dex); File apkSigned = signApk(action, apkUnsigned); installApk(action, apkSigned); } - private File makePackagingDirectory(Action action) { - File packagingDir = new File(environment.actionCompilationDir(action), "packaging"); - new Rm().directoryTree(packagingDir); - new Mkdir().mkdirs(packagingDir); - return packagingDir; - } - - private void addRunnerClasses(File packagingDir) { - new Command("rsync", "-a", - environment.runnerClassesDir() + "/", - packagingDir + "/").execute(); - } - - private File findOriginalJar(Action action) { - String targetClass = action.getTargetClass(); - String targetClassFile = targetClass.replace('.', '/') + ".class"; - for (File element : classpath.getElements()) { - try { - JarFile jar = new JarFile(element); - JarEntry jarEntry = jar.getJarEntry(targetClassFile); - if (jarEntry != null) { - return element; - } - } catch (IOException e) { - throw new RuntimeException( - "Could not find element " + element + - " of class path " + classpath, e); - } - } - return null; - } - - private static void extractJars(File packagingDir, List<File> jars) { - for (File jar : jars) { - new Command.Builder() - .args("unzip") - .args("-q") - .args("-o") - .args(jar) - .args("-d") - .args(packagingDir).execute(); - } - new Rm().directoryTree(new File(packagingDir, "META-INF")); - } - - private void addActionClasses(Action action, File packagingDir) { - File classesDir = environment.classesDir(action); - new Command("rsync", "-a", - classesDir + "/", - packagingDir + "/").execute(); - } - private File createDex(Action action, File packagingDir) { - File classesDir = environment.classesDir(action); - File dex = new File(classesDir + ".dex"); - new Dx().dex(dex, Classpath.of(packagingDir)); + /** + * Returns a single dexfile containing {@code action}'s classes and all + * dependencies. + */ + private File createDex(Action action, File actionJar) { + File dex = environment.file(action, "classes.dex"); + Classpath classesToDex = Classpath.of(actionJar); + classesToDex.addAll(this.classpath); + new Dx().dex(dex, classesToDex); return dex; } @@ -179,9 +108,7 @@ final class ActivityMode extends Mode { " </activity>\n" + " </application>\n" + "</manifest>\n"; - File androidManifestFile = - new File(environment.actionCompilationDir(action), - "AndroidManifest.xml"); + File androidManifestFile = environment.file(action, "classes", "AndroidManifest.xml"); try { FileOutputStream androidManifestOut = new FileOutputStream(androidManifestFile); @@ -191,17 +118,15 @@ final class ActivityMode extends Mode { throw new RuntimeException("Problem writing " + androidManifestFile, e); } - File classesDir = environment.classesDir(action); - File apkUnsigned = new File(classesDir + ".apk.unsigned"); + File apkUnsigned = environment.file(action, action + ".apk.unsigned"); new Aapt().apk(apkUnsigned, androidManifestFile); new Aapt().add(apkUnsigned, dex); - new Aapt().add(apkUnsigned, new File(classesDir , TestProperties.FILE)); + new Aapt().add(apkUnsigned, environment.file(action, "classes", TestProperties.FILE)); return apkUnsigned; } private File signApk(Action action, File apkUnsigned) { - File classesDir = environment.classesDir(action); - File apkSigned = new File(classesDir, action.getName() + ".apk"); + File apkSigned = environment.file(action, action + ".apk"); // TODO: we should be able to work with a shipping SDK, not depend on out/... // TODO: we should be able to work without hardwired keys, not depend on build/... new Command.Builder() diff --git a/tools/runner/java/vogar/CaliperFinder.java b/tools/runner/java/vogar/CaliperSpec.java index 3e2101d..70095aa 100644 --- a/tools/runner/java/vogar/CaliperFinder.java +++ b/tools/runner/java/vogar/CaliperSpec.java @@ -24,21 +24,28 @@ import vogar.target.Runner; * Create {@link Action}s for {@code .java} files with Caliper benchmarks in * them. */ -class CaliperFinder extends NamingPatternCodeFinder { +class CaliperSpec extends NamingPatternRunnerSpec { @Override protected boolean matches(File file) { return super.matches(file) && file.getName().endsWith("Benchmark.java"); } + public boolean supports(String clazz) { + return clazz.endsWith("Benchmark"); + } + public Class<? extends Runner> getRunnerClass() { return CaliperRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/CaliperRunner.java"); } - public Classpath getRunnerClasspath() { - return new Classpath(); + public Classpath getClasspath() { + return Classpath.of( + new File("dalvik/libcore/tools/runner/lib/jsr305.jar"), + new File("dalvik/libcore/tools/runner/lib/guava.jar"), + new File("dalvik/libcore/tools/runner/lib/caliper.jar")); } } diff --git a/tools/runner/java/vogar/DeviceDalvikVm.java b/tools/runner/java/vogar/DeviceDalvikVm.java index 4b44f02..7243b1e 100644 --- a/tools/runner/java/vogar/DeviceDalvikVm.java +++ b/tools/runner/java/vogar/DeviceDalvikVm.java @@ -17,7 +17,9 @@ package vogar; import java.io.File; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Logger; import vogar.commands.Dx; @@ -27,53 +29,54 @@ import vogar.commands.Dx; final class DeviceDalvikVm extends Vm { private static final Logger logger = Logger.getLogger(DeviceDalvikVm.class.getName()); + /** A list of generic names that we avoid when naming generated files. */ + private static final Set<String> BANNED_NAMES = new HashSet<String>(); + { + BANNED_NAMES.add("classes"); + BANNED_NAMES.add("javalib"); + } + DeviceDalvikVm(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, List<String> additionalVmArgs, List<String> targetArgs, boolean cleanBefore, boolean cleanAfter, - File runnerDir) { + File runnerDir, Classpath classpath) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, monitorPort, localTemp, - runnerDir), sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort); + runnerDir), sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort, classpath); } private EnvironmentDevice getEnvironmentDevice() { return (EnvironmentDevice) environment; } - @Override protected void postCompileRunner() { - // TODO: does this really need to be a special case? - postCompile("testrunner", environment.runnerClassesDir()); - + @Override protected void installRunner() { // dex everything on the classpath and push it to the device. for (File classpathElement : classpath.getElements()) { - String name = basenameOfJar(classpathElement); - logger.fine("dex and push " + name); - // make the local dex (inside a jar) - // TODO: this is *really* expensive. we need a cache! - File outputFile = getEnvironmentDevice().actionDir(name + ".jar"); - new Dx().dex(outputFile, Classpath.of(classpathElement)); - // push the local dex to the device - getEnvironmentDevice().adb.push(outputFile, deviceDexFile(name)); + dexAndPush(basenameOfJar(classpathElement), classpathElement); } } - private String basenameOfJar(File jarFile) { - return jarFile.getName().replaceAll("\\.jar$", ""); + private String basenameOfJar(File file) { + String name = file.getName().replaceAll("\\.jar$", ""); + while (BANNED_NAMES.contains(name)) { + file = file.getParentFile(); + name = file.getName(); + } + return name; } - @Override protected void postCompile(Action action) { - postCompile(action.getName(), environment.classesDir(action)); + @Override protected void postCompile(Action action, File jar) { + dexAndPush(action.getName(), jar); } - private void postCompile(String name, File dir) { + private void dexAndPush(String name, File jar) { logger.fine("dex and push " + name); // make the local dex (inside a jar) - File localDex = new File(dir.getPath() + ".jar"); - new Dx().dex(localDex, Classpath.of(dir)); + File localDex = environment.file(name, name + ".dx.jar"); + new Dx().dex(localDex, Classpath.of(jar)); // post the local dex to the device - File deviceDex = deviceDexFile(name); - getEnvironmentDevice().adb.push(localDex, deviceDex); + getEnvironmentDevice().adb.push(localDex, deviceDexFile(name)); } private File deviceDexFile(String name) { @@ -97,13 +100,12 @@ final class DeviceDalvikVm extends Vm { .temp(getEnvironmentDevice().vogarTemp); } - @Override protected Classpath getRuntimeSupportClasspath(Action action) { - Classpath classpath = new Classpath(); - classpath.addAll(deviceDexFile(action.getName())); - classpath.addAll(deviceDexFile("testrunner")); - for (File classpathElement : this.classpath.getElements()) { - classpath.addAll(deviceDexFile(basenameOfJar(classpathElement))); + @Override protected Classpath getRuntimeClasspath(Action action) { + Classpath result = new Classpath(); + result.addAll(deviceDexFile(action.getName())); + for (File classpathElement : classpath.getElements()) { + result.addAll(deviceDexFile(basenameOfJar(classpathElement))); } - return classpath; + return result; } } diff --git a/tools/runner/java/vogar/Driver.java b/tools/runner/java/vogar/Driver.java index 40dcf58..00c46cf 100644 --- a/tools/runner/java/vogar/Driver.java +++ b/tools/runner/java/vogar/Driver.java @@ -46,7 +46,7 @@ final class Driver implements HostMonitor.Handler { private final File localTemp; private final ExpectationStore expectationStore; - private final List<CodeFinder> codeFinders; + private final List<RunnerSpec> runnerSpecs; private final Mode mode; private final XmlReportPrinter reportPrinter; private final Console console; @@ -59,6 +59,7 @@ final class Driver implements HostMonitor.Handler { private Timer actionTimeoutTimer = new Timer("action timeout", true); + private final Set<RunnerSpec> runnerSpecsBearingActions = new HashSet<RunnerSpec>(); private final Map<String, Action> actions = Collections.synchronizedMap( new LinkedHashMap<String, Action>()); private final Map<String, Outcome> outcomes = Collections.synchronizedMap( @@ -71,13 +72,13 @@ final class Driver implements HostMonitor.Handler { private int unsupportedActions = 0; public Driver(File localTemp, Mode mode, ExpectationStore expectationStore, - List<CodeFinder> codeFinders, XmlReportPrinter reportPrinter, + List<RunnerSpec> runnerSpecs, XmlReportPrinter reportPrinter, Console console, HostMonitor monitor, int monitorPort, long timeoutSeconds) { this.localTemp = localTemp; this.expectationStore = expectationStore; this.mode = mode; this.console = console; - this.codeFinders = codeFinders; + this.runnerSpecs = runnerSpecs; this.reportPrinter = reportPrinter; this.monitor = monitor; this.monitorPort = monitorPort; @@ -87,44 +88,27 @@ final class Driver implements HostMonitor.Handler { /** * Builds and executes the actions in the given files. */ - public void buildAndRunAllActions(Collection<File> files) { + public void buildAndRun(Collection<File> files, Collection<String> classes) { if (!actions.isEmpty()) { throw new IllegalStateException("Drivers are not reusable"); } new Mkdir().mkdirs(localTemp); - for (File file : files) { - Set<Action> actionsForFile = Collections.emptySet(); - for (CodeFinder codeFinder : codeFinders) { - actionsForFile = codeFinder.findActions(file); + filesToActions(files); + classesToActions(classes); - // break as soon as we find any match. We don't need multiple - // matches for the same file, since that would run it twice. - if (!actionsForFile.isEmpty()) { - break; - } - } - - for (Action action : actionsForFile) { - actions.put(action.getName(), action); - } + if (actions.isEmpty()) { + logger.info("Nothing to do."); + return; } - // compute TestRunner java and classpath to pass to mode.prepare - Set<File> runnerJava = new HashSet<File>(); - Classpath runnerClasspath = new Classpath(); - for (final Action action : actions.values()) { - runnerJava.add(action.getRunnerJava()); - runnerClasspath.addAll(action.getRunnerClasspath()); - } + logger.info("Actions: " + actions.size()); // mode.prepare before mode.buildAndInstall to ensure the runner is // built. packaging of activity APK files needs the runner along with // the action-specific files. - mode.prepare(runnerJava, runnerClasspath); - - logger.info("Actions: " + actions.size()); + mode.prepare(runnerSpecsBearingActions); // build and install actions in a background thread. Using lots of // threads helps for packages that contain many unsupported actions @@ -189,6 +173,8 @@ final class Driver implements HostMonitor.Handler { logger.info(numFiles + " XML files written."); } + mode.shutdown(); + if (failures > 0 || unsupportedActions > 0) { Collections.sort(failureNames); console.summarizeFailures(failureNames); @@ -200,6 +186,40 @@ final class Driver implements HostMonitor.Handler { } } + private void classesToActions(Collection<String> classes) { + for (String clazz : classes) { + for (RunnerSpec runnerSpec : runnerSpecs) { + if (runnerSpec.supports(clazz)) { + runnerSpecsBearingActions.add(runnerSpec); + Action action = new Action(clazz, clazz, null, null, runnerSpec); + actions.put(action.getName(), action); + break; + } + } + } + } + + private void filesToActions(Collection<File> files) { + for (File file : files) { + Set<Action> actionsForFile = Collections.emptySet(); + + for (RunnerSpec runnerSpec : runnerSpecs) { + actionsForFile = runnerSpec.findActions(file); + + // break as soon as we find any match. We don't need multiple + // matches for the same file, since that would run it twice. + if (!actionsForFile.isEmpty()) { + runnerSpecsBearingActions.add(runnerSpec); + break; + } + } + + for (Action action : actionsForFile) { + actions.put(action.getName(), action); + } + } + } + /** * Executes a single action and then prints the result. */ diff --git a/tools/runner/java/vogar/Environment.java b/tools/runner/java/vogar/Environment.java index e149e0a..fc5c3ea 100644 --- a/tools/runner/java/vogar/Environment.java +++ b/tools/runner/java/vogar/Environment.java @@ -58,29 +58,16 @@ abstract class Environment { void cleanup(Action action) { if (cleanAfter) { logger.fine("clean " + action.getName()); - new Rm().directoryTree(actionCompilationDir(action)); - new Rm().directoryTree(actionUserDir(action)); + new Rm().directoryTree(file(action)); } } - final File actionDir(String name) { - return new File(localTemp, name); + final File file(Object... path) { + return new File(localTemp + "/" + Strings.join(path, "/")); } - final File runnerDir(String name) { - return new File(actionDir("testrunner"), name); - } - - final File runnerClassesDir() { - return runnerDir("classes"); - } - - final File actionCompilationDir(Action action) { - return new File(localTemp, action.getName()); - } - - final File classesDir(Action action) { - return new File(actionCompilationDir(action), "classes"); + final File hostJar(Object nameOrAction) { + return file(nameOrAction, nameOrAction + ".jar"); } final File actionUserDir(Action action) { @@ -88,5 +75,9 @@ abstract class Environment { return new File(testTemp, action.getName()); } - abstract void shutdown(); + void shutdown() { + if (cleanAfter) { + new Rm().directoryTree(localTemp); + } + } } diff --git a/tools/runner/java/vogar/EnvironmentDevice.java b/tools/runner/java/vogar/EnvironmentDevice.java index b315ea1..eddde8d 100644 --- a/tools/runner/java/vogar/EnvironmentDevice.java +++ b/tools/runner/java/vogar/EnvironmentDevice.java @@ -51,7 +51,10 @@ class EnvironmentDevice extends Environment { @Override protected void prepareUserDir(Action action) { File actionClassesDirOnDevice = actionClassesDirOnDevice(action); adb.mkdir(actionClassesDirOnDevice); - adb.push(action.getJavaDirectory(), actionClassesDirOnDevice); + File resourcesDirectory = action.getResourcesDirectory(); + if (resourcesDirectory != null) { + adb.push(resourcesDirectory, actionClassesDirOnDevice); + } action.setUserDir(actionClassesDirOnDevice); } @@ -67,6 +70,7 @@ class EnvironmentDevice extends Environment { } @Override void shutdown() { + super.shutdown(); if (cleanAfter) { adb.rm(runnerDir); } diff --git a/tools/runner/java/vogar/EnvironmentHost.java b/tools/runner/java/vogar/EnvironmentHost.java index 33a59e1..7942332 100644 --- a/tools/runner/java/vogar/EnvironmentHost.java +++ b/tools/runner/java/vogar/EnvironmentHost.java @@ -37,11 +37,15 @@ class EnvironmentHost extends Environment { throw new IllegalStateException(); } - new Mkdir().mkdirs(actionUserDir.getParentFile()); - new Command("cp", "-r", action.getJavaDirectory().toString(), - actionUserDir.toString()).execute(); + File resourcesDirectory = action.getResourcesDirectory(); + if (resourcesDirectory != null) { + new Mkdir().mkdirs(actionUserDir.getParentFile()); + new Command("cp", "-r", resourcesDirectory.toString(), + actionUserDir.toString()).execute(); + } else { + new Mkdir().mkdirs(actionUserDir); + } + action.setUserDir(actionUserDir); } - - @Override void shutdown() {} } diff --git a/tools/runner/java/vogar/JUnitFinder.java b/tools/runner/java/vogar/JUnitSpec.java index 0dcfbcd..d8169ba 100644 --- a/tools/runner/java/vogar/JUnitFinder.java +++ b/tools/runner/java/vogar/JUnitSpec.java @@ -23,24 +23,33 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with JUnit tests in them. */ -class JUnitFinder extends NamingPatternCodeFinder { +class JUnitSpec extends NamingPatternRunnerSpec { @Override protected boolean matches(File file) { String filename = file.getName(); - return super.matches(file) - && (filename.endsWith("Test.java") || filename.endsWith("TestSuite.java")); + return super.matches(file) && (filename.endsWith("Test.java") + || filename.endsWith("TestSuite.java") + || filename.contains("Tests")); + } + + public boolean supports(String clazz) { + return clazz.endsWith("Test") + || clazz.endsWith("TestSuite") + || clazz.contains("Tests"); } public Class<? extends Runner> getRunnerClass() { return JUnitRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/JUnitRunner.java"); } - public Classpath getRunnerClasspath() { - // TODO: we should be able to work with a shipping SDK, not depend on out/... - return Classpath.of(new File("out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar").getAbsoluteFile()); + public Classpath getClasspath() { + // TODO: jar up just the junit classes and drop the jar in our lib/ directory. + return Classpath.of( + new File("out/target/common/obj/JAVA_LIBRARIES/core-tests-luni_intermediates/classes.jar").getAbsoluteFile(), + new File("out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar").getAbsoluteFile()); } } diff --git a/tools/runner/java/vogar/JavaVm.java b/tools/runner/java/vogar/JavaVm.java index f728744..f8bdd56 100644 --- a/tools/runner/java/vogar/JavaVm.java +++ b/tools/runner/java/vogar/JavaVm.java @@ -28,31 +28,25 @@ final class JavaVm extends Vm { JavaVm(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, File javaHome, List<String> additionalVmArgs, - List<String> targetArgs, boolean cleanBefore, boolean cleanAfter) { + List<String> targetArgs, boolean cleanBefore, boolean cleanAfter, + Classpath classpath) { super(new EnvironmentHost(cleanBefore, cleanAfter, debugPort, localTemp), - sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort); + sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort, classpath); this.javaHome = javaHome; } - @Override protected void postCompileRunner() { - } - - @Override protected void postCompile(Action action) { - } - - @Override protected VmCommandBuilder newVmCommandBuilder( - File workingDirectory) { + @Override protected VmCommandBuilder newVmCommandBuilder(File workingDirectory) { String java = javaHome == null ? "java" : new File(javaHome, "bin/java").getPath(); return new VmCommandBuilder() .vmCommand(java) .workingDir(workingDirectory); } - @Override protected Classpath getRuntimeSupportClasspath(Action action) { - Classpath classpath = new Classpath(); - classpath.addAll(environment.classesDir(action)); - classpath.addAll(this.classpath); - classpath.addAll(environment.runnerClassesDir()); - classpath.addAll(runnerClasspath); + + @Override protected Classpath getRuntimeClasspath(Action action) { + Classpath result = new Classpath(); + result.addAll(classpath); + result.addAll(environment.hostJar(action)); + /* * For javax.net.ssl tests dependency on Bouncy Castle for * creating a self-signed X509 certificate. Needs to be run @@ -61,7 +55,7 @@ final class JavaVm extends Vm { * * --java-home /usr/lib/jvm/java-6-openjdk */ - classpath.addAll(new File("/usr/share/java/bcprov.jar")); - return classpath; + result.addAll(new File("/usr/share/java/bcprov.jar")); + return result; } } diff --git a/tools/runner/java/vogar/JtregFinder.java b/tools/runner/java/vogar/JtregSpec.java index b413166..1c43949 100644 --- a/tools/runner/java/vogar/JtregFinder.java +++ b/tools/runner/java/vogar/JtregSpec.java @@ -35,12 +35,12 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with jtreg tests in them. */ -class JtregFinder implements CodeFinder { +class JtregSpec implements RunnerSpec { // TODO: add support for the @library directive, as seen in // test/com/sun/crypto/provider/Cipher/AES/TestKATForECB_VT.java - private static final Logger logger = Logger.getLogger(JtregFinder.class.getName()); + private static final Logger logger = Logger.getLogger(JtregSpec.class.getName()); /** * The subpath of a platform implementation under which tests live. Used to @@ -51,7 +51,7 @@ class JtregFinder implements CodeFinder { private final File localTemp; - JtregFinder(File localTemp) { + JtregSpec(File localTemp) { this.localTemp = localTemp; } @@ -85,9 +85,7 @@ class JtregFinder implements CodeFinder { TestDescription description = testResult.getDescription(); String qualifiedName = qualifiedName(description); String testClass = description.getName(); - result.add(new Action(qualifiedName, testClass, description.getDir(), description.getFile(), - description.getTitle(), - getRunnerClass(), getRunnerJava(), getRunnerClasspath())); + result.add(new Action(qualifiedName, testClass, description.getDir(), description.getFile(), this)); } return result; } catch (Exception jtregFailure) { @@ -96,6 +94,11 @@ class JtregFinder implements CodeFinder { } } + public boolean supports(String clazz) { + // the jtreg runner cannot run prebuilt classes + return false; + } + /** * Returns a fully qualified name of the form {@code * java.lang.Math.PowTests} from the given test description. The returned @@ -128,11 +131,11 @@ class JtregFinder implements CodeFinder { return JtregRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/JtregRunner.java"); } - public Classpath getRunnerClasspath() { + public Classpath getClasspath() { return new Classpath(); } } diff --git a/tools/runner/java/vogar/MainFinder.java b/tools/runner/java/vogar/MainSpec.java index 4c1f1db..d2bebfb 100644 --- a/tools/runner/java/vogar/MainFinder.java +++ b/tools/runner/java/vogar/MainSpec.java @@ -23,17 +23,21 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with main methods in them. */ -class MainFinder extends NamingPatternCodeFinder { +class MainSpec extends NamingPatternRunnerSpec { + + public boolean supports(String clazz) { + return true; + } public Class<? extends Runner> getRunnerClass() { return MainRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/MainRunner.java"); } - public Classpath getRunnerClasspath() { + public Classpath getClasspath() { return new Classpath(); } } diff --git a/tools/runner/java/vogar/Mode.java b/tools/runner/java/vogar/Mode.java index eaabb94..7009adb 100644 --- a/tools/runner/java/vogar/Mode.java +++ b/tools/runner/java/vogar/Mode.java @@ -56,42 +56,34 @@ abstract class Mode { protected final Set<File> runnerJava = new HashSet<File>(); /** - * Classpath of runner on the host side including any supporting libraries - * for runnerJava. Useful for compiling runnerJava as well as executing it - * on the host. Execution on the device requires further packaging typically - * done by postCompile. + * User classes that need to be included in the classpath for both + * compilation and execution. Also includes dependencies of all active + * runners. */ - protected final Classpath runnerClasspath = new Classpath(); - - // TODO: this should be an immutable collection. - protected final Classpath classpath = Classpath.of( - new File("dalvik/libcore/tools/runner/lib/jsr305.jar"), - new File("dalvik/libcore/tools/runner/lib/guava.jar"), - new File("dalvik/libcore/tools/runner/lib/caliper.jar"), - // TODO: we should be able to work with a shipping SDK, not depend on out/... - // dalvik/libcore/**/test/ for junit - // TODO: jar up just the junit classes and drop the jar in our lib/ directory. - new File("out/host/common/obj/JAVA_LIBRARIES/kxml2-2.3.0_intermediates/javalib.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/core-tests-luni_intermediates/classes.jar").getAbsoluteFile()); - - Mode(Environment environment, File sdkJar, List<String> javacArgs, int monitorPort) { + protected final Classpath classpath = new Classpath(); + + Mode(Environment environment, File sdkJar, List<String> javacArgs, + int monitorPort, Classpath classpath) { this.environment = environment; this.sdkJar = sdkJar; this.javacArgs = javacArgs; this.monitorPort = monitorPort; + this.classpath.addAll(classpath); } /** * Initializes the temporary directories and harness necessary to run * actions. */ - protected void prepare(Set<File> runnerJava, Classpath runnerClasspath) { - this.runnerJava.add(new File(Vogar.HOME_JAVA, "vogar/target/TestRunner.java")); - this.runnerJava.addAll(dalvikAnnotationSourceFiles()); - this.runnerJava.addAll(runnerJava); - this.runnerClasspath.addAll(runnerClasspath); + protected void prepare(Set<RunnerSpec> runners) { + for (RunnerSpec runnerSpec : runners) { + runnerJava.add(runnerSpec.getSource()); + classpath.addAll(runnerSpec.getClasspath()); + } + runnerJava.add(new File(Vogar.HOME_JAVA, "vogar/target/TestRunner.java")); environment.prepare(); - compileRunner(); + classpath.addAll(compileRunner()); + installRunner(); } private List<File> dalvikAnnotationSourceFiles() { @@ -106,31 +98,27 @@ abstract class Mode { return Arrays.asList(javaSourceFiles); } - private void compileRunner() { + /** + * Returns a .jar file containing the compiled runner .java files. + */ + private File compileRunner() { logger.fine("build runner"); - - Classpath classpath = new Classpath(); - classpath.addAll(this.classpath); - classpath.addAll(runnerClasspath); - - File base = environment.runnerClassesDir(); - new Mkdir().mkdirs(base); + File classes = environment.file("runner", "classes"); + File jar = environment.hostJar("runner"); + new Mkdir().mkdirs(classes); new Javac() .bootClasspath(sdkJar) .classpath(classpath) .sourcepath(Vogar.HOME_JAVA) - .destination(base) + .destination(classes) .extra(javacArgs) .compile(runnerJava); - postCompileRunner(); + new Command("jar", "cvfM", jar.getPath(), + "-C", classes.getPath(), "./").execute(); + return jar; } /** - * Hook method called after runner compilation. - */ - abstract protected void postCompileRunner(); - - /** * Compiles classes for the given action and makes them ready for execution. * * @return null if the compilation succeeded, or an outcome describing the @@ -140,7 +128,8 @@ abstract class Mode { logger.fine("build " + action.getName()); try { - compile(action); + File jar = compile(action); + postCompile(action, jar); } catch (CommandFailedException e) { return new Outcome(action.getName(), action.getName(), Result.COMPILE_FAILED, e.getOutputLines()); @@ -152,17 +141,12 @@ abstract class Mode { } /** - * Compiles the classes for the described action. + * Returns the .jar file containing the action's compiled classes. * * @throws CommandFailedException if javac fails */ - private void compile(Action action) throws IOException { - if (!JAVA_SOURCE_PATTERN.matcher(action.getJavaFile().toString()).find()) { - throw new CommandFailedException(Collections.<String>emptyList(), - Collections.singletonList("Cannot compile: " + action.getJavaFile())); - } - - File classesDir = environment.classesDir(action); + private File compile(Action action) throws IOException { + File classesDir = environment.file(action, "classes"); new Mkdir().mkdirs(classesDir); FileOutputStream propertiesOut = new FileOutputStream( new File(classesDir, TestProperties.FILE)); @@ -171,30 +155,32 @@ abstract class Mode { properties.store(propertiesOut, "generated by " + Mode.class.getName()); propertiesOut.close(); - Classpath classpath = new Classpath(); - classpath.addAll(this.classpath); - classpath.addAll(action.getRunnerClasspath()); + Javac javac = new Javac(); Set<File> sourceFiles = new HashSet<File>(); - sourceFiles.add(action.getJavaFile()); sourceFiles.addAll(dalvikAnnotationSourceFiles()); - // compile the action case - new Javac() - .bootClasspath(sdkJar) + File javaFile = action.getJavaFile(); + if (javaFile != null) { + if (!JAVA_SOURCE_PATTERN.matcher(javaFile.toString()).find()) { + throw new CommandFailedException(Collections.<String>emptyList(), + Collections.singletonList("Cannot compile: " + javaFile)); + } + sourceFiles.add(javaFile); + javac.sourcepath(javaFile.getParentFile()); + } + + javac.bootClasspath(sdkJar) .classpath(classpath) - .sourcepath(action.getJavaDirectory()) .destination(classesDir) .extra(javacArgs) .compile(sourceFiles); - postCompile(action); - } - - /** - * Hook method called after action compilation. - */ - abstract protected void postCompile(Action action); + File jar = environment.hostJar(action); + new Command("jar", "cvfM", jar.getPath(), + "-C", classesDir.getPath(), "./").execute(); + return jar; + } /** * Fill in properties for running in this mode @@ -202,11 +188,21 @@ abstract class Mode { protected void fillInProperties(Properties properties, Action action) { properties.setProperty(TestProperties.TEST_CLASS, action.getTargetClass()); properties.setProperty(TestProperties.QUALIFIED_NAME, action.getName()); - properties.setProperty(TestProperties.RUNNER_CLASS, action.getRunnerClass().getName()); + properties.setProperty(TestProperties.RUNNER_CLASS, action.getRunnerSpec().getRunnerClass().getName()); properties.setProperty(TestProperties.MONITOR_PORT, String.valueOf(monitorPort)); } /** + * Hook method called after runner compilation. + */ + protected void installRunner() {} + + /** + * Hook method called after action compilation. + */ + protected void postCompile(Action action, File jar) {} + + /** * Create the command that executes the action. */ protected abstract Command createActionCommand(Action action); diff --git a/tools/runner/java/vogar/NamingPatternCodeFinder.java b/tools/runner/java/vogar/NamingPatternRunnerSpec.java index d87a35f..429bab9 100644 --- a/tools/runner/java/vogar/NamingPatternCodeFinder.java +++ b/tools/runner/java/vogar/NamingPatternRunnerSpec.java @@ -27,7 +27,7 @@ import java.util.regex.Pattern; * A code finder that traverses through the directory tree looking for matching * naming patterns. */ -abstract class NamingPatternCodeFinder implements CodeFinder { +abstract class NamingPatternRunnerSpec implements RunnerSpec { private final String PACKAGE_PATTERN = "(?m)^\\s*package\\s+(\\S+)\\s*;"; @@ -61,10 +61,7 @@ abstract class NamingPatternCodeFinder implements CodeFinder { } String className = fileToClass(file); - File directory = file.getParentFile(); - String description = null; - sink.add(new Action(className, className, directory, file, description, - getRunnerClass(), getRunnerJava(), getRunnerClasspath())); + sink.add(new Action(className, className, null, file, this)); } /** diff --git a/tools/runner/java/vogar/CodeFinder.java b/tools/runner/java/vogar/RunnerSpec.java index 4437848..73a4ce1 100644 --- a/tools/runner/java/vogar/CodeFinder.java +++ b/tools/runner/java/vogar/RunnerSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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. @@ -21,28 +21,36 @@ import java.util.Set; import vogar.target.Runner; /** - * A strategy for finding runnable things in a directory. + * Defines a runner for a type of Java code, such as a JUnit test, benchmark, + * or class with main method. */ -public interface CodeFinder { +public interface RunnerSpec { /** * Returns all actions in the given file or directory. If the returned set * is empty, no executable code of this kind were found. */ - public Set<Action> findActions(File file); + Set<Action> findActions(File file); + + /** + * Returns true if this runner can exercise {@code clazz}. + * + * @param clazz a fully qualified classname. + */ + boolean supports(String clazz); /** * Return the class for the TestRunner */ - public Class<? extends Runner> getRunnerClass(); + Class<? extends Runner> getRunnerClass(); /** * Return the Java file for the TestRunner */ - public File getRunnerJava(); + File getSource(); /** * Return the compile classpath for the TestRunner */ - public Classpath getRunnerClasspath(); + Classpath getClasspath(); } diff --git a/tools/runner/java/vogar/TestProperties.java b/tools/runner/java/vogar/TestProperties.java index d35f349..d9a5b7b 100644 --- a/tools/runner/java/vogar/TestProperties.java +++ b/tools/runner/java/vogar/TestProperties.java @@ -57,22 +57,5 @@ final public class TestProperties { */ public static final String MONITOR_PORT = "monitorPort"; - /** - * Result value for successful test - */ - public static final String RESULT_SUCCESS = "SUCCESS"; - - /** - * Result value for failed test - */ - public static final String RESULT_FAILURE = "FAILURE"; - - public static String result(boolean success) { - return success ? RESULT_SUCCESS : RESULT_FAILURE; - } - - /** - * This class should not be instantiated - */ private TestProperties() {} } diff --git a/tools/runner/java/vogar/Vm.java b/tools/runner/java/vogar/Vm.java index 4edb0bc..350b861 100644 --- a/tools/runner/java/vogar/Vm.java +++ b/tools/runner/java/vogar/Vm.java @@ -35,8 +35,9 @@ public abstract class Vm extends Mode { protected final List<String> targetArgs; Vm(Environment environment, File sdkJar, List<String> javacArgs, - List<String> additionalVmArgs, List<String> targetArgs, int monitorPort) { - super(environment, sdkJar, javacArgs, monitorPort); + List<String> additionalVmArgs, List<String> targetArgs, int monitorPort, + Classpath classpath) { + super(environment, sdkJar, javacArgs, monitorPort, classpath); this.additionalVmArgs = additionalVmArgs; this.targetArgs = targetArgs; } @@ -46,7 +47,7 @@ public abstract class Vm extends Mode { */ @Override protected Command createActionCommand(Action action) { return newVmCommandBuilder(action.getUserDir()) - .classpath(getRuntimeSupportClasspath(action)) + .classpath(getRuntimeClasspath(action)) .userDir(action.getUserDir()) .debugPort(environment.debugPort) .vmArgs(additionalVmArgs) @@ -64,7 +65,7 @@ public abstract class Vm extends Mode { * Returns the classpath containing JUnit and the dalvik annotations * required for action execution. */ - protected abstract Classpath getRuntimeSupportClasspath(Action action); + protected abstract Classpath getRuntimeClasspath(Action action); /** * Builds a virtual machine command. diff --git a/tools/runner/java/vogar/Vogar.java b/tools/runner/java/vogar/Vogar.java index 2e9e3a9..6286d87 100644 --- a/tools/runner/java/vogar/Vogar.java +++ b/tools/runner/java/vogar/Vogar.java @@ -24,6 +24,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.regex.Pattern; /** * Command line interface for running benchmarks and tests on dalvik. @@ -33,9 +34,17 @@ public final class Vogar { static final File HOME = new File("dalvik/libcore/tools/runner"); static final File HOME_JAVA = new File(HOME, "java"); + static final Pattern CLASS_NAME_PATTERN + = Pattern.compile("(\\w+)(\\.\\w+)*\\.[A-Z]\\w*"); + private static class Options { + private final Classpath classpath = Classpath.of( + // TODO: we should be able to work with a shipping SDK, not depend on out/... + new File("out/host/common/obj/JAVA_LIBRARIES/kxml2-2.3.0_intermediates/javalib.jar").getAbsoluteFile()); + private final List<File> actionFiles = new ArrayList<File>(); + private final List<String> actionClasses = new ArrayList<String>(); private final List<String> targetArgs = new ArrayList<String>(); @Option(names = { "--expectations" }) @@ -101,8 +110,9 @@ public final class Vogar { private void printUsage() { System.out.println("Usage: Vogar [options]... <actions>... [target args]..."); System.out.println(); - System.out.println(" <actions>: .java files or directories containing jtreg tests, JUnit"); - System.out.println(" tests, Caliper benchmarks, or executable Java classes."); + System.out.println(" <actions>: .java files, .jar files, directories, or class names."); + System.out.println(" These should be JUnit tests, jtreg tests, Caliper benchmarks"); + System.out.println(" or executable Java classes."); System.out.println(); System.out.println(" [args]: arguments passed to the target process. This is only useful when"); System.out.println(" the target process is a Caliper benchmark or main method."); @@ -258,23 +268,31 @@ public final class Vogar { } // separate the actions and the target args - boolean action = true; - for (String arg : actionsAndTargetArgs) { + int index = 0; + for (; index < actionsAndTargetArgs.size(); index++) { + String arg = actionsAndTargetArgs.get(index); if (arg.equals("--")) { - action = false; - continue; + index++; + break; } - File actionFile = new File(arg); - if (action && actionFile.exists()) { - actionFiles.add(actionFile); + File file = new File(arg); + if (file.exists()) { + if (arg.endsWith(".jar")) { + classpath.addAll(file); + } else { + actionFiles.add(file); + } + } else if (CLASS_NAME_PATTERN.matcher(arg).matches()) { + actionClasses.add(arg); } else { - targetArgs.add(arg); - action = false; + break; } } - if (actionFiles.isEmpty()) { + targetArgs.addAll(actionsAndTargetArgs.subList(index, actionsAndTargetArgs.size())); + + if (actionFiles.isEmpty() && actionClasses.isEmpty()) { System.out.println("No actions provided."); return false; } @@ -311,7 +329,8 @@ public final class Vogar { options.targetArgs, options.cleanBefore, options.cleanAfter, - options.deviceRunnerDir); + options.deviceRunnerDir, + options.classpath); } else if (options.mode.equals(Options.MODE_HOST)) { monitorPort = 8788; mode = new JavaVm( @@ -324,8 +343,8 @@ public final class Vogar { options.vmArgs, options.targetArgs, options.cleanBefore, - options.cleanAfter - ); + options.cleanAfter, + options.classpath); } else if (options.mode.equals(Options.MODE_ACTIVITY)) { monitorPort = 8787; mode = new ActivityMode( @@ -336,7 +355,8 @@ public final class Vogar { localTemp, options.cleanBefore, options.cleanAfter, - options.deviceRunnerDir); + options.deviceRunnerDir, + options.classpath); } else { System.out.println("Unknown mode mode " + options.mode + "."); return; @@ -344,11 +364,11 @@ public final class Vogar { HostMonitor monitor = new HostMonitor(options.monitorTimeout); - List<CodeFinder> codeFinders = Arrays.asList( - new JtregFinder(localTemp), - new JUnitFinder(), - new CaliperFinder(), - new MainFinder()); + List<RunnerSpec> runnerSpecs = Arrays.asList( + new JtregSpec(localTemp), + new JUnitSpec(), + new CaliperSpec(), + new MainSpec()); ExpectationStore expectationStore; try { @@ -366,16 +386,14 @@ public final class Vogar { localTemp, mode, expectationStore, - codeFinders, + runnerSpecs, xmlReportPrinter, console, monitor, monitorPort, options.timeoutSeconds); - driver.buildAndRunAllActions(options.actionFiles); - - mode.shutdown(); + driver.buildAndRun(options.actionFiles, options.actionClasses); } public static void main(String[] args) { diff --git a/tools/runner/java/vogar/commands/Dx.java b/tools/runner/java/vogar/commands/Dx.java index 0fcf280..d2e3d00 100644 --- a/tools/runner/java/vogar/commands/Dx.java +++ b/tools/runner/java/vogar/commands/Dx.java @@ -33,6 +33,7 @@ public final class Dx { * Converts all the .class files on 'classpath' into a dex file written to 'output'. */ public void dex(File output, Classpath classpath) { + output.getParentFile().mkdirs(); File key = DEX_CACHE.makeKey(classpath); if (key != null && key.exists()) { logger.fine("dex cache hit for " + classpath); diff --git a/tools/runner/java/vogar/target/TestRunner.java b/tools/runner/java/vogar/target/TestRunner.java index 739cbc5..9419e6f 100644 --- a/tools/runner/java/vogar/target/TestRunner.java +++ b/tools/runner/java/vogar/target/TestRunner.java @@ -75,7 +75,7 @@ public class TestRunner { } } - public void run(String[] args) { + public void run(String... args) { final TargetMonitor monitor = new TargetMonitor(); monitor.await(monitorPort); diff --git a/tools/runner/lib/TestActivity.java b/tools/runner/lib/TestActivity.java index 39a6575..6e6d09f 100644 --- a/tools/runner/lib/TestActivity.java +++ b/tools/runner/lib/TestActivity.java @@ -48,7 +48,7 @@ public class TestActivity extends Activity { 1, Threads.daemonThreadFactory()); executor.submit(new Runnable() { public void run() { - new TestRunner().run(args); + new TestRunner().run(); } }); executor.shutdown(); diff --git a/tools/runner/vogar b/tools/runner/vogar index 4e20ac3..470b029 100755 --- a/tools/runner/vogar +++ b/tools/runner/vogar @@ -21,6 +21,5 @@ android_root=`dirname $0`/../../../.. cd ${android_root} classpath=${android_root}/out/host/linux-x86/framework/dalvik_runner.jar -core_jar=${android_root}/out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar -exec java -cp $classpath vogar.Vogar --sdk ${core_jar} "$@" +exec java -cp $classpath vogar.Vogar "$@" |