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 17 package vogar; 18 19 import com.google.common.base.Splitter; 20 21 import com.google.common.base.Supplier; 22 import java.io.File; 23 import java.io.IOException; 24 import java.net.URL; 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.Date; 28 import java.util.HashSet; 29 import java.util.List; 30 import java.util.Set; 31 import java.util.UUID; 32 33 import vogar.android.ActivityMode; 34 import vogar.android.AndroidSdk; 35 import vogar.android.DeviceRuntime; 36 import vogar.android.HostRuntime; 37 import vogar.commands.Mkdir; 38 import vogar.commands.Rm; 39 import vogar.tasks.TaskQueue; 40 import vogar.util.Strings; 41 42 public final class Run { 43 /** 44 * A list of generic names that we avoid when naming generated files. 45 */ 46 private static final Set<String> BANNED_NAMES = new HashSet<String>(); 47 48 private static final String JAR_URI_PREFIX = "jar:file:"; 49 50 private static final String FILE_URL_PREFIX = "file:"; 51 52 private static final String VOGAR_CLASS_RESOURCE_PATH = "/vogar/Vogar.class"; 53 54 static { 55 BANNED_NAMES.add("classes"); 56 BANNED_NAMES.add("javalib"); 57 } 58 59 public final File xmlReportsDirectory; 60 public final File resultsDir; 61 public final boolean recordResults; 62 public final ExpectationStore expectationStore; 63 public final Date date; 64 public final String invokeWith; 65 public final File keystore; 66 public final Log log; 67 public final Classpath classpath; 68 public final Classpath buildClasspath; 69 public final Classpath resourceClasspath; 70 public final List<File> sourcepath; 71 public final Mkdir mkdir; 72 public final Rm rm; 73 public final int firstMonitorPort; 74 public final int timeoutSeconds; 75 public final File javaHome; 76 public final List<String> excludeFilters; 77 public final Integer debugPort; 78 public final Language language; 79 public final List<String> javacArgs; 80 public final boolean multidex; 81 public final boolean benchmark; 82 public final File runnerDir; 83 public final boolean cleanBefore; 84 public final boolean cleanAfter; 85 public final File localTemp; 86 public final int maxConcurrentActions; 87 public final File deviceUserHome; 88 public final Console console; 89 public final int smallTimeoutSeconds; 90 public final String vmCommand; 91 public final String dalvikCache; 92 public final List<String> additionalVmArgs; 93 public final List<String> targetArgs; 94 public final boolean useBootClasspath; 95 public final int largeTimeoutSeconds; 96 public final RetrievedFilesFilter retrievedFiles; 97 public final Driver driver; 98 public final Mode mode; 99 public final Target target; 100 public final AndroidSdk androidSdk; 101 public final XmlReportPrinter reportPrinter; 102 public final JarSuggestions jarSuggestions; 103 public final ClassFileIndex classFileIndex; 104 public final OutcomeStore outcomeStore; 105 public final TaskQueue taskQueue; 106 public final RunnerType runnerType; 107 public final Toolchain toolchain; 108 public final boolean checkJni; 109 public final boolean debugging; 110 public final Integer sdkVersion; 111 public final boolean serialDexing; 112 public final boolean verboseDexStats; 113 Run(Vogar vogar, Toolchain toolchain, Console console, Mkdir mkdir, AndroidSdk androidSdk, Rm rm, Target target, File runnerDir)114 public Run(Vogar vogar, Toolchain toolchain, Console console, Mkdir mkdir, 115 AndroidSdk androidSdk, Rm rm, Target target, File runnerDir) 116 throws IOException { 117 this.console = console; 118 119 this.localTemp = new File("/tmp/vogar/" + UUID.randomUUID()); 120 this.log = console; 121 122 this.target = target; 123 124 this.toolchain = toolchain; 125 this.vmCommand = vogar.vmCommand; 126 this.dalvikCache = vogar.dalvikCache; 127 this.additionalVmArgs = vogar.vmArgs; 128 this.benchmark = vogar.benchmark; 129 this.cleanBefore = vogar.cleanBefore; 130 this.cleanAfter = vogar.cleanAfter; 131 this.date = new Date(); 132 this.debugPort = vogar.debugPort; 133 this.runnerDir = runnerDir; 134 this.deviceUserHome = new File(runnerDir, "user.home"); 135 this.mkdir = mkdir; 136 this.rm = rm; 137 this.firstMonitorPort = vogar.firstMonitorPort; 138 this.invokeWith = vogar.invokeWith; 139 this.language = vogar.language; 140 this.javacArgs = vogar.javacArgs; 141 this.multidex = vogar.multidex; 142 this.javaHome = vogar.javaHome; 143 this.excludeFilters = new ArrayList<>(); 144 this.excludeFilters.addAll(vogar.excludeFilters); 145 this.largeTimeoutSeconds = vogar.timeoutSeconds * Vogar.LARGE_TIMEOUT_MULTIPLIER; 146 this.maxConcurrentActions = (vogar.stream || vogar.modeId == ModeId.ACTIVITY) 147 ? 1 148 : Vogar.NUM_PROCESSORS; 149 this.timeoutSeconds = vogar.timeoutSeconds; 150 this.smallTimeoutSeconds = vogar.timeoutSeconds; 151 this.sourcepath = vogar.sourcepath; 152 this.resourceClasspath = Classpath.of(vogar.resourceClasspath); 153 this.useBootClasspath = vogar.useBootClasspath; 154 this.targetArgs = vogar.targetArgs; 155 this.xmlReportsDirectory = vogar.xmlReportsDirectory; 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.runnerType = vogar.runnerType; 164 165 this.androidSdk = androidSdk; 166 167 expectationStore = ExpectationStore.parse( 168 console, vogar.expectationFiles, vogar.modeId, vogar.variant); 169 if (vogar.openBugsCommand != null) { 170 expectationStore.loadBugStatuses(new CommandBugDatabase(log, vogar.openBugsCommand)); 171 } 172 173 this.mode = createMode(vogar.modeId, vogar.variant); 174 175 this.buildClasspath = Classpath.of(vogar.buildClasspath); 176 if (androidSdk != null) { 177 buildClasspath.addAll(androidSdk.getCompilationClasspath()); 178 } 179 180 this.classFileIndex = new ClassFileIndex(log, mkdir, vogar.jarSearchDirs); 181 if (vogar.suggestClasspaths) { 182 classFileIndex.createIndex(); 183 } 184 185 this.retrievedFiles = new RetrievedFilesFilter(); 186 this.reportPrinter = new XmlReportPrinter(xmlReportsDirectory, expectationStore, date); 187 this.jarSuggestions = new JarSuggestions(); 188 this.outcomeStore = new OutcomeStore(log, mkdir, rm, resultsDir, recordResults, 189 expectationStore, date); 190 this.driver = new Driver(this); 191 this.taskQueue = new TaskQueue(console, maxConcurrentActions); 192 this.checkJni = vogar.checkJni; 193 this.debugging = (vogar.debugPort != null) || vogar.debugApp; 194 this.sdkVersion = vogar.sdkVersion; 195 this.serialDexing = vogar.serialDexing; 196 this.verboseDexStats = vogar.verboseDexStats; 197 } 198 createMode(ModeId modeId, Variant variant)199 private Mode createMode(ModeId modeId, Variant variant) { 200 switch (modeId) { 201 case JVM: 202 return new JavaVm(this); 203 case HOST: 204 return new HostRuntime(this, modeId, variant); 205 case DEVICE: 206 case APP_PROCESS: 207 return new DeviceRuntime(this, modeId, variant, new Supplier<String>() { 208 @Override 209 public String get() { 210 return target.getDeviceUserName(); 211 } 212 }); 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 public final File localDir(Object... path) { 225 String joinedPath = localTemp + "/" + Strings.join("/", path); 226 File f = new File(joinedPath); 227 f.mkdirs(); 228 if (!f.exists()) { 229 throw new AssertionError("Failed to mkdirs: " + joinedPath); 230 } 231 return f; 232 } 233 234 private File vogarJar() { 235 URL jarUrl = Vogar.class.getResource(VOGAR_CLASS_RESOURCE_PATH); 236 if (jarUrl == null) { 237 // should we add an option for IDE users, to use a user-specified vogar.jar? 238 throw new IllegalStateException("Vogar cannot find its own .jar"); 239 } 240 241 /* 242 * Parse a URI like jar:file:/Users/jessewilson/vogar/vogar.jar!/vogar/Vogar.class 243 * to yield a .jar file like /Users/jessewilson/vogar/vogar.jar. 244 */ 245 String url = jarUrl.toString(); 246 int bang = url.indexOf("!"); 247 if (url.startsWith(JAR_URI_PREFIX) && bang != -1) { 248 return new File(url.substring(JAR_URI_PREFIX.length(), bang)); 249 } else if (url.startsWith(FILE_URL_PREFIX) && url.endsWith(VOGAR_CLASS_RESOURCE_PATH)) { 250 // Vogar is being run from a classes directory. 251 return new File(url.substring(FILE_URL_PREFIX.length(), 252 url.length() - VOGAR_CLASS_RESOURCE_PATH.length())); 253 } else { 254 throw new IllegalStateException("Vogar cannot find the .jar file in " + jarUrl); 255 } 256 } 257 258 public final File hostJar(Object nameOrAction) { 259 return localFile(nameOrAction, nameOrAction + ".jar"); 260 } 261 262 /** 263 * Returns a path for a Java tool such as java, javac, jar where 264 * the Java home is used if present, otherwise assumes it will 265 * come from the path. 266 */ 267 public String javaPath(String tool) { 268 return (javaHome == null) 269 ? tool 270 : new File(new File(javaHome, "bin"), tool).getPath(); 271 } 272 273 public File targetDexFile(String name) { 274 return new File(runnerDir, name + ".dex.jar"); 275 } 276 277 public File localDexFile(String name) { 278 return localFile(name, name + ".dex.jar"); 279 } 280 281 /** 282 * Returns a recognizable readable name for the given generated .jar file, 283 * appropriate for use in naming derived files. 284 * 285 * @param file a product of the android build system, such as 286 * "out/core-libart_intermediates/javalib.jar". 287 * @return a recognizable base name like "core-libart_intermediates". 288 */ 289 public String basenameOfJar(File file) { 290 String name = file.getName().replaceAll("(\\.jar)$", ""); 291 while (BANNED_NAMES.contains(name)) { 292 file = file.getParentFile(); 293 name = file.getName(); 294 } 295 return name; 296 } 297 298 public File vogarTemp() { 299 return new File(runnerDir, "tmp"); 300 } 301 302 public File dalvikCache() { 303 return new File(runnerDir.getParentFile(), dalvikCache); 304 } 305 306 /** 307 * Returns the directory where the VM stores its dexopt files. 308 */ 309 public String getAndroidDataPath() { 310 // The VM wants the parent directory of a directory named "dalvik-cache" 311 return dalvikCache().getParentFile().getPath(); 312 } 313 314 /** 315 * Returns a parsed list of the --invoke-with command and its 316 * arguments, or an empty list if no --invoke-with was provided. 317 */ 318 public Iterable<String> invokeWith() { 319 if (invokeWith == null) { 320 return Collections.emptyList(); 321 } 322 return Splitter.onPattern("\\s+").omitEmptyStrings().split(invokeWith); 323 } 324 } 325