/* * 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 vogar; import com.google.common.base.Splitter; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import vogar.android.ActivityMode; import vogar.android.AdbTarget; import vogar.android.AndroidSdk; import vogar.android.DeviceFileCache; import vogar.android.DeviceRuntime; import vogar.android.HostRuntime; import vogar.commands.Mkdir; import vogar.commands.Rm; import vogar.tasks.TaskQueue; import vogar.util.Strings; public final class Run { /** A list of generic names that we avoid when naming generated files. */ private static final Set BANNED_NAMES = new HashSet(); static { BANNED_NAMES.add("classes"); BANNED_NAMES.add("javalib"); } public final File xmlReportsDirectory; public final File resultsDir; public final boolean recordResults; public final ExpectationStore expectationStore; public final Date date; public final String invokeWith; public final File keystore; public final Log log; public final Classpath classpath; public final Classpath buildClasspath; public final Classpath resourceClasspath; public final List sourcepath; public final Mkdir mkdir; public final Rm rm; public final int firstMonitorPort; public final int timeoutSeconds; public final boolean profile; public final boolean profileBinary; public final int profileDepth; public final int profileInterval; public final boolean profileThreadGroup; public final File profileFile; public final File javaHome; public final Integer debugPort; public final List javacArgs; public final boolean benchmark; public final File runnerDir; public final boolean cleanBefore; public final boolean cleanAfter; public final File localTemp; public final int maxConcurrentActions; public final File deviceUserHome; public final Console console; public final int smallTimeoutSeconds; public final String vmCommand; public final String dalvikCache; public final List additionalVmArgs; public final List targetArgs; public final boolean useBootClasspath; public final int largeTimeoutSeconds; public final RetrievedFilesFilter retrievedFiles; public final Driver driver; public final Mode mode; public final Target target; public final AndroidSdk androidSdk; public final XmlReportPrinter reportPrinter; public final JarSuggestions jarSuggestions; public final ClassFileIndex classFileIndex; public final OutcomeStore outcomeStore; public final TaskQueue taskQueue; public final boolean testOnly; public Run(Vogar vogar) throws IOException { this.console = vogar.stream ? new Console.StreamingConsole() : new Console.MultiplexingConsole(); console.setUseColor( vogar.color, vogar.passColor, vogar.skipColor, vogar.failColor, vogar.warnColor); console.setAnsi(vogar.ansi); console.setIndent(vogar.indent); console.setVerbose(vogar.verbose); this.localTemp = new File("/tmp/vogar/" + UUID.randomUUID()); this.log = console; if (vogar.sshHost != null) { this.target = new SshTarget(vogar.sshHost, log); } else if (vogar.modeId.isLocal()) { this.target = new LocalTarget(this); } else { this.target = new AdbTarget(this); } this.vmCommand = vogar.vmCommand; this.dalvikCache = vogar.dalvikCache; this.additionalVmArgs = vogar.vmArgs; this.benchmark = vogar.benchmark; this.cleanBefore = vogar.cleanBefore; this.cleanAfter = vogar.cleanAfter; this.date = new Date(); this.debugPort = vogar.debugPort; this.runnerDir = vogar.deviceDir != null ? new File(vogar.deviceDir, "run") : new File(target.defaultDeviceDir(), "run"); this.deviceUserHome = new File(runnerDir, "user.home"); this.mkdir = new Mkdir(console); this.rm = new Rm(console); this.firstMonitorPort = vogar.firstMonitorPort; this.invokeWith = vogar.invokeWith; this.javacArgs = vogar.javacArgs; this.javaHome = vogar.javaHome; this.largeTimeoutSeconds = vogar.timeoutSeconds * Vogar.LARGE_TIMEOUT_MULTIPLIER; this.maxConcurrentActions = (vogar.stream || vogar.modeId == ModeId.ACTIVITY) ? 1 : Vogar.NUM_PROCESSORS; this.timeoutSeconds = vogar.timeoutSeconds; this.smallTimeoutSeconds = vogar.timeoutSeconds; this.sourcepath = vogar.sourcepath; this.resourceClasspath = Classpath.of(vogar.resourceClasspath); this.useBootClasspath = vogar.useBootClasspath; this.targetArgs = vogar.targetArgs; this.xmlReportsDirectory = vogar.xmlReportsDirectory; this.profile = vogar.profile; this.profileBinary = vogar.profileBinary; this.profileFile = vogar.profileFile; this.profileDepth = vogar.profileDepth; this.profileInterval = vogar.profileInterval; this.profileThreadGroup = vogar.profileThreadGroup; this.recordResults = vogar.recordResults; this.resultsDir = vogar.resultsDir == null ? new File(vogar.vogarDir, "results") : vogar.resultsDir; this.keystore = localFile("activity", "vogar.keystore"); this.classpath = Classpath.of(vogar.classpath); this.classpath.addAll(vogarJar()); this.testOnly = vogar.testOnly; if (vogar.modeId.requiresAndroidSdk()) { androidSdk = new AndroidSdk(log, mkdir, vogar.modeId); androidSdk.setCaches(new HostFileCache(log, mkdir), new DeviceFileCache(log, runnerDir, androidSdk)); } else { androidSdk = null; } expectationStore = ExpectationStore.parse( console, vogar.expectationFiles, vogar.modeId, vogar.variant); if (vogar.openBugsCommand != null) { expectationStore.loadBugStatuses(new CommandBugDatabase(log, vogar.openBugsCommand)); } this.mode = createMode(vogar.modeId, vogar.variant); this.buildClasspath = Classpath.of(vogar.buildClasspath); if (vogar.modeId.requiresAndroidSdk()) { buildClasspath.addAll(androidSdk.getCompilationClasspath()); } this.classFileIndex = new ClassFileIndex(log, mkdir, vogar.jarSearchDirs); if (vogar.suggestClasspaths) { classFileIndex.createIndex(); } this.retrievedFiles = new RetrievedFilesFilter(profile, profileFile); this.reportPrinter = new XmlReportPrinter(xmlReportsDirectory, expectationStore, date); this.jarSuggestions = new JarSuggestions(); this.outcomeStore = new OutcomeStore(log, mkdir, rm, resultsDir, recordResults, expectationStore, date); this.driver = new Driver(this); this.taskQueue = new TaskQueue(console, maxConcurrentActions); } private Mode createMode(ModeId modeId, Variant variant) { switch (modeId) { case JVM: return new JavaVm(this); case HOST: case HOST_DALVIK: case HOST_ART_KITKAT: return new HostRuntime(this, modeId, variant); case DEVICE: case DEVICE_DALVIK: case DEVICE_ART_KITKAT: case APP_PROCESS: return new DeviceRuntime(this, modeId, variant); case ACTIVITY: return new ActivityMode(this); default: throw new IllegalArgumentException("Unsupported mode: " + modeId); } } public final File localFile(Object... path) { return new File(localTemp + "/" + Strings.join("/", path)); } private File vogarJar() { URL jarUrl = Vogar.class.getResource("/vogar/Vogar.class"); if (jarUrl == null) { // should we add an option for IDE users, to use a user-specified vogar.jar? throw new IllegalStateException("Vogar cannot find its own .jar"); } /* * Parse a URI like jar:file:/Users/jessewilson/vogar/vogar.jar!/vogar/Vogar.class * to yield a .jar file like /Users/jessewilson/vogar/vogar.jar. */ String url = jarUrl.toString(); int bang = url.indexOf("!"); String JAR_URI_PREFIX = "jar:file:"; if (url.startsWith(JAR_URI_PREFIX) && bang != -1) { return new File(url.substring(JAR_URI_PREFIX.length(), bang)); } else { throw new IllegalStateException("Vogar cannot find the .jar file in " + jarUrl); } } public final File hostJar(Object nameOrAction) { return localFile(nameOrAction, nameOrAction + ".jar"); } /** * Returns a path for a Java tool such as java, javac, jar where * the Java home is used if present, otherwise assumes it will * come from the path. */ public String javaPath(String tool) { return (javaHome == null) ? tool : new File(new File(javaHome, "bin"), tool).getPath(); } public File targetDexFile(String name) { return new File(runnerDir, name + ".dex.jar"); } public File localDexFile(String name) { return localFile(name, name + ".dex.jar"); } /** * Returns a recognizable readable name for the given generated .jar file, * appropriate for use in naming derived files. * * @param file a product of the android build system, such as * "out/core-libart_intermediates/javalib.jar". * @return a recognizable base name like "core-libart_intermediates". */ public String basenameOfJar(File file) { String name = file.getName().replaceAll("\\.jar$", ""); while (BANNED_NAMES.contains(name)) { file = file.getParentFile(); name = file.getName(); } return name; } public File vogarTemp() { return new File(runnerDir, "tmp"); } public File dalvikCache() { return new File(runnerDir.getParentFile(), dalvikCache); } /** * Returns an environment variable assignment to configure where the VM will * store its dexopt files. This must be set on production devices and is * optional for development devices. */ public String getAndroidData() { // The VM wants the parent directory of a directory named "dalvik-cache" return "ANDROID_DATA=" + dalvikCache().getParentFile(); } /** * Returns a parsed list of the --invoke-with command and its * arguments, or an empty list if no --invoke-with was provided. */ public Iterable invokeWith() { if (invokeWith == null) { return Collections.emptyList(); } return Splitter.onPattern("\\s+").omitEmptyStrings().split(invokeWith); } }