1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package vogar; 18 19import com.google.common.base.Splitter; 20import java.io.File; 21import java.io.IOException; 22import java.net.URL; 23import java.util.Collections; 24import java.util.Date; 25import java.util.HashSet; 26import java.util.List; 27import java.util.Set; 28import java.util.UUID; 29import vogar.android.ActivityMode; 30import vogar.android.AdbTarget; 31import vogar.android.AndroidSdk; 32import vogar.android.DeviceFileCache; 33import vogar.android.DeviceRuntime; 34import vogar.android.HostRuntime; 35import vogar.commands.Mkdir; 36import vogar.commands.Rm; 37import vogar.tasks.TaskQueue; 38import vogar.util.Strings; 39 40public final class Run { 41 /** A list of generic names that we avoid when naming generated files. */ 42 private static final Set<String> BANNED_NAMES = new HashSet<String>(); 43 static { 44 BANNED_NAMES.add("classes"); 45 BANNED_NAMES.add("javalib"); 46 } 47 48 public final File xmlReportsDirectory; 49 public final File resultsDir; 50 public final boolean recordResults; 51 public final ExpectationStore expectationStore; 52 public final Date date; 53 public final String invokeWith; 54 public final File keystore; 55 public final Log log; 56 public final Classpath classpath; 57 public final Classpath buildClasspath; 58 public final Classpath resourceClasspath; 59 public final List<File> sourcepath; 60 public final Mkdir mkdir; 61 public final Rm rm; 62 public final int firstMonitorPort; 63 public final int timeoutSeconds; 64 public final boolean profile; 65 public final boolean profileBinary; 66 public final int profileDepth; 67 public final int profileInterval; 68 public final boolean profileThreadGroup; 69 public final File profileFile; 70 public final File javaHome; 71 public final Integer debugPort; 72 public final List<String> javacArgs; 73 public final boolean benchmark; 74 public final File runnerDir; 75 public final boolean cleanBefore; 76 public final boolean cleanAfter; 77 public final File localTemp; 78 public final int maxConcurrentActions; 79 public final File deviceUserHome; 80 public final Console console; 81 public final int smallTimeoutSeconds; 82 public final String vmCommand; 83 public final String dalvikCache; 84 public final List<String> additionalVmArgs; 85 public final List<String> targetArgs; 86 public final boolean useBootClasspath; 87 public final int largeTimeoutSeconds; 88 public final RetrievedFilesFilter retrievedFiles; 89 public final Driver driver; 90 public final Mode mode; 91 public final Target target; 92 public final AndroidSdk androidSdk; 93 public final XmlReportPrinter reportPrinter; 94 public final JarSuggestions jarSuggestions; 95 public final ClassFileIndex classFileIndex; 96 public final OutcomeStore outcomeStore; 97 public final TaskQueue taskQueue; 98 public final boolean testOnly; 99 100 public Run(Vogar vogar) throws IOException { 101 this.console = vogar.stream 102 ? new Console.StreamingConsole() 103 : new Console.MultiplexingConsole(); 104 console.setUseColor( 105 vogar.color, vogar.passColor, vogar.skipColor, vogar.failColor, vogar.warnColor); 106 console.setAnsi(vogar.ansi); 107 console.setIndent(vogar.indent); 108 console.setVerbose(vogar.verbose); 109 110 this.localTemp = new File("/tmp/vogar/" + UUID.randomUUID()); 111 this.log = console; 112 113 if (vogar.sshHost != null) { 114 this.target = new SshTarget(vogar.sshHost, log); 115 } else if (vogar.modeId.isLocal()) { 116 this.target = new LocalTarget(this); 117 } else { 118 this.target = new AdbTarget(this); 119 } 120 121 this.vmCommand = vogar.vmCommand; 122 this.dalvikCache = vogar.dalvikCache; 123 this.additionalVmArgs = vogar.vmArgs; 124 this.benchmark = vogar.benchmark; 125 this.cleanBefore = vogar.cleanBefore; 126 this.cleanAfter = vogar.cleanAfter; 127 this.date = new Date(); 128 this.debugPort = vogar.debugPort; 129 this.runnerDir = vogar.deviceDir != null 130 ? new File(vogar.deviceDir, "run") 131 : new File(target.defaultDeviceDir(), "run"); 132 this.deviceUserHome = new File(runnerDir, "user.home"); 133 this.mkdir = new Mkdir(console); 134 this.rm = new Rm(console); 135 this.firstMonitorPort = vogar.firstMonitorPort; 136 this.invokeWith = vogar.invokeWith; 137 this.javacArgs = vogar.javacArgs; 138 this.javaHome = vogar.javaHome; 139 this.largeTimeoutSeconds = vogar.timeoutSeconds * Vogar.LARGE_TIMEOUT_MULTIPLIER; 140 this.maxConcurrentActions = (vogar.stream || vogar.modeId == ModeId.ACTIVITY) 141 ? 1 142 : Vogar.NUM_PROCESSORS; 143 this.timeoutSeconds = vogar.timeoutSeconds; 144 this.smallTimeoutSeconds = vogar.timeoutSeconds; 145 this.sourcepath = vogar.sourcepath; 146 this.resourceClasspath = Classpath.of(vogar.resourceClasspath); 147 this.useBootClasspath = vogar.useBootClasspath; 148 this.targetArgs = vogar.targetArgs; 149 this.xmlReportsDirectory = vogar.xmlReportsDirectory; 150 this.profile = vogar.profile; 151 this.profileBinary = vogar.profileBinary; 152 this.profileFile = vogar.profileFile; 153 this.profileDepth = vogar.profileDepth; 154 this.profileInterval = vogar.profileInterval; 155 this.profileThreadGroup = vogar.profileThreadGroup; 156 this.recordResults = vogar.recordResults; 157 this.resultsDir = vogar.resultsDir == null 158 ? new File(vogar.vogarDir, "results") 159 : vogar.resultsDir; 160 this.keystore = localFile("activity", "vogar.keystore"); 161 this.classpath = Classpath.of(vogar.classpath); 162 this.classpath.addAll(vogarJar()); 163 this.testOnly = vogar.testOnly; 164 165 if (vogar.modeId.requiresAndroidSdk()) { 166 androidSdk = new AndroidSdk(log, mkdir, vogar.modeId); 167 androidSdk.setCaches(new HostFileCache(log, mkdir), 168 new DeviceFileCache(log, runnerDir, androidSdk)); 169 } else { 170 androidSdk = null; 171 } 172 173 expectationStore = ExpectationStore.parse( 174 console, vogar.expectationFiles, vogar.modeId, vogar.variant); 175 if (vogar.openBugsCommand != null) { 176 expectationStore.loadBugStatuses(new CommandBugDatabase(log, vogar.openBugsCommand)); 177 } 178 179 this.mode = createMode(vogar.modeId, vogar.variant); 180 181 this.buildClasspath = Classpath.of(vogar.buildClasspath); 182 if (vogar.modeId.requiresAndroidSdk()) { 183 buildClasspath.addAll(androidSdk.getCompilationClasspath()); 184 } 185 186 this.classFileIndex = new ClassFileIndex(log, mkdir, vogar.jarSearchDirs); 187 if (vogar.suggestClasspaths) { 188 classFileIndex.createIndex(); 189 } 190 191 this.retrievedFiles = new RetrievedFilesFilter(profile, profileFile); 192 this.reportPrinter = new XmlReportPrinter(xmlReportsDirectory, expectationStore, date); 193 this.jarSuggestions = new JarSuggestions(); 194 this.outcomeStore = new OutcomeStore(log, mkdir, rm, resultsDir, recordResults, 195 expectationStore, date); 196 this.driver = new Driver(this); 197 this.taskQueue = new TaskQueue(console, maxConcurrentActions); 198 } 199 200 private Mode createMode(ModeId modeId, Variant variant) { 201 switch (modeId) { 202 case JVM: 203 return new JavaVm(this); 204 case HOST: 205 case HOST_DALVIK: 206 case HOST_ART_KITKAT: 207 return new HostRuntime(this, modeId, variant); 208 case DEVICE: 209 case DEVICE_DALVIK: 210 case DEVICE_ART_KITKAT: 211 case APP_PROCESS: 212 return new DeviceRuntime(this, modeId, variant); 213 case ACTIVITY: 214 return new ActivityMode(this); 215 default: 216 throw new IllegalArgumentException("Unsupported mode: " + modeId); 217 } 218 } 219 220 public final File localFile(Object... path) { 221 return new File(localTemp + "/" + Strings.join("/", path)); 222 } 223 224 private File vogarJar() { 225 URL jarUrl = Vogar.class.getResource("/vogar/Vogar.class"); 226 if (jarUrl == null) { 227 // should we add an option for IDE users, to use a user-specified vogar.jar? 228 throw new IllegalStateException("Vogar cannot find its own .jar"); 229 } 230 231 /* 232 * Parse a URI like jar:file:/Users/jessewilson/vogar/vogar.jar!/vogar/Vogar.class 233 * to yield a .jar file like /Users/jessewilson/vogar/vogar.jar. 234 */ 235 String url = jarUrl.toString(); 236 int bang = url.indexOf("!"); 237 String JAR_URI_PREFIX = "jar:file:"; 238 if (url.startsWith(JAR_URI_PREFIX) && bang != -1) { 239 return new File(url.substring(JAR_URI_PREFIX.length(), bang)); 240 } else { 241 throw new IllegalStateException("Vogar cannot find the .jar file in " + jarUrl); 242 } 243 } 244 245 public final File hostJar(Object nameOrAction) { 246 return localFile(nameOrAction, nameOrAction + ".jar"); 247 } 248 249 /** 250 * Returns a path for a Java tool such as java, javac, jar where 251 * the Java home is used if present, otherwise assumes it will 252 * come from the path. 253 */ 254 public String javaPath(String tool) { 255 return (javaHome == null) 256 ? tool 257 : new File(new File(javaHome, "bin"), tool).getPath(); 258 } 259 260 public File targetDexFile(String name) { 261 return new File(runnerDir, name + ".dex.jar"); 262 } 263 264 public File localDexFile(String name) { 265 return localFile(name, name + ".dex.jar"); 266 } 267 268 /** 269 * Returns a recognizable readable name for the given generated .jar file, 270 * appropriate for use in naming derived files. 271 * 272 * @param file a product of the android build system, such as 273 * "out/core-libart_intermediates/javalib.jar". 274 * @return a recognizable base name like "core-libart_intermediates". 275 */ 276 public String basenameOfJar(File file) { 277 String name = file.getName().replaceAll("\\.jar$", ""); 278 while (BANNED_NAMES.contains(name)) { 279 file = file.getParentFile(); 280 name = file.getName(); 281 } 282 return name; 283 } 284 285 public File vogarTemp() { 286 return new File(runnerDir, "tmp"); 287 } 288 289 public File dalvikCache() { 290 return new File(runnerDir.getParentFile(), dalvikCache); 291 } 292 293 /** 294 * Returns an environment variable assignment to configure where the VM will 295 * store its dexopt files. This must be set on production devices and is 296 * optional for development devices. 297 */ 298 public String getAndroidData() { 299 // The VM wants the parent directory of a directory named "dalvik-cache" 300 return "ANDROID_DATA=" + dalvikCache().getParentFile(); 301 } 302 303 /** 304 * Returns a parsed list of the --invoke-with command and its 305 * arguments, or an empty list if no --invoke-with was provided. 306 */ 307 public Iterable<String> invokeWith() { 308 if (invokeWith == null) { 309 return Collections.emptyList(); 310 } 311 return Splitter.onPattern("\\s+").omitEmptyStrings().split(invokeWith); 312 } 313} 314