1 /* 2 * Copyright (C) 2009 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.annotations.VisibleForTesting; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.Lists; 22 23 import java.io.File; 24 import java.io.IOException; 25 import java.util.ArrayList; 26 import java.util.List; 27 import java.util.Set; 28 import java.util.LinkedHashSet; 29 30 import vogar.android.AdbChrootTarget; 31 import vogar.android.AdbTarget; 32 import vogar.android.AndroidSdk; 33 import vogar.android.DeviceFileCache; 34 import vogar.android.DeviceFilesystem; 35 import vogar.commands.Mkdir; 36 import vogar.commands.Rm; 37 import vogar.util.Strings; 38 39 /** 40 * Command line interface for running benchmarks and tests on dalvik. 41 */ 42 public final class Vogar { 43 static final int LARGE_TIMEOUT_MULTIPLIER = 20; 44 public static final int NUM_PROCESSORS = Runtime.getRuntime().availableProcessors(); 45 46 private final List<File> actionFiles = new ArrayList<File>(); 47 private final List<String> actionClassesAndPackages = new ArrayList<String>(); 48 final List<String> targetArgs = new ArrayList<String>(); 49 private final OptionParser optionParser = new OptionParser(this); 50 private File configFile = Vogar.dotFile(".vogarconfig"); 51 private String[] configArgs; 52 public final static Console console = new Console.StreamingConsole(); 53 dotFile(String name)54 public static File dotFile (String name) { 55 return new File(System.getProperty("user.home", "."), name); 56 } 57 maybeAnsiTerminal()58 private static boolean maybeAnsiTerminal() { 59 if (System.console() == null) { 60 return false; 61 } 62 String terminal = System.getenv("TERM"); 63 return terminal == null || !terminal.contains("dumb"); 64 } 65 maybeColorTerminal()66 private static boolean maybeColorTerminal() { 67 if (!maybeAnsiTerminal()) { 68 return false; 69 } 70 String terminal = System.getenv("TERM"); 71 return terminal != null && terminal.contains("color"); 72 } 73 74 @Option(names = { "--expectations" }) 75 Set<File> expectationFiles = new LinkedHashSet<File>(); 76 { AndroidSdk.defaultExpectations()77 expectationFiles.addAll(AndroidSdk.defaultExpectations()); 78 } 79 80 @Option(names = { "--mode" }) 81 ModeId modeId = ModeId.DEVICE; 82 83 @Option(names = { "--variant" }) 84 Variant variant = Variant.DEFAULT; 85 86 @Option(names = { "--ssh" }) 87 private String sshHost; 88 89 @Option(names = { "--chroot" }) 90 private String chrootDir; 91 92 @Option(names = { "--timeout" }) 93 int timeoutSeconds = 60; // default is one minute; 94 95 @Option(names = { "--first-monitor-port" }) 96 int firstMonitorPort = -1; 97 98 @Option(names = { "--clean-before" }) 99 boolean cleanBefore = true; 100 101 @Option(names = { "--clean-after" }) 102 boolean cleanAfter = true; 103 104 @Option(names = { "--clean" }) 105 private boolean clean = true; 106 107 @Option(names = { "--xml-reports-directory" }) 108 File xmlReportsDirectory; 109 110 @Option(names = { "--indent" }) 111 private String indent = " "; 112 113 @Option(names = { "--verbose" }) 114 private boolean verbose; 115 116 @Option(names = { "--stream" }) 117 boolean stream = true; 118 119 @Option(names = { "--color" }) 120 private boolean color = maybeColorTerminal(); 121 122 @Option(names = { "--pass-color" }) 123 private int passColor = 32; // green 124 125 @Option(names = { "--skip-color" }) 126 private int skipColor = 33; // yellow 127 128 @Option(names = { "--fail-color" }) 129 private int failColor = 31; // red 130 131 @Option(names = { "--warn-color" }) 132 private int warnColor = 35; // purple 133 134 @Option(names = { "--ansi" }) 135 private boolean ansi = maybeAnsiTerminal(); 136 137 @Option(names = { "--debug" }) 138 Integer debugPort; 139 140 @Option(names = { "--debug-app" }) 141 boolean debugApp; 142 143 @Option(names = { "--device-dir" }) 144 private File deviceDir; 145 146 @Option(names = { "--vm-arg" }) 147 List<String> vmArgs = new ArrayList<String>(); 148 149 @Option(names = { "--vm-command" }) 150 String vmCommand; 151 152 @Option(names = { "--dalvik-cache" }) 153 String dalvikCache = "dalvik-cache"; 154 155 @Option(names = { "--java-home" }) 156 File javaHome; 157 158 @Option(names = { "--exclude-filter" }) 159 List<String> excludeFilters = new ArrayList<>(); 160 161 @Option(names = { "--javac-arg" }) 162 List<String> javacArgs = new ArrayList<String>(); 163 164 @Option(names = { "--multidex" }) 165 boolean multidex = true; 166 167 @Option(names = { "--use-bootclasspath" }) 168 boolean useBootClasspath = false; 169 170 @Option(names = { "--build-classpath" }) 171 List<File> buildClasspath = new ArrayList<File>(); 172 173 @Option(names = { "--classpath", "-cp" }) 174 List<File> classpath = new ArrayList<File>(); 175 176 @Option(names = { "--resource-classpath" }) 177 List<File> resourceClasspath = new ArrayList<File>(); 178 179 @Option(names = { "--sourcepath" }) 180 List<File> sourcepath = new ArrayList<File>(); 181 { AndroidSdk.defaultSourcePath()182 sourcepath.addAll(AndroidSdk.defaultSourcePath()); 183 } 184 185 @Option(names = { "--jar-search-dir" }) 186 List<File> jarSearchDirs = Lists.newArrayList(); 187 188 @Option(names = { "--vogar-dir" }) 189 File vogarDir = Vogar.dotFile(".vogar"); 190 191 @Option(names = { "--record-results" }) 192 boolean recordResults = false; 193 194 @Option(names = { "--results-dir" }) 195 File resultsDir = null; 196 197 @Option(names = { "--suggest-classpaths" }) 198 boolean suggestClasspaths = false; 199 200 @Option(names = { "--invoke-with" }) 201 String invokeWith = null; 202 203 @Option(names = { "--benchmark" }) 204 boolean benchmark = false; 205 206 @Option(names = { "--open-bugs-command" }) 207 String openBugsCommand; 208 209 @Option(names = { "--test-only" }) 210 boolean testOnly = false; 211 212 @Option(names = { "--toolchain" }) 213 Toolchain toolchain = null; 214 215 @Option(names = { "--language" }) 216 Language language = Language.CUR; 217 218 @Option(names = { "--check-jni" }) 219 boolean checkJni = true; 220 221 @Option(names = {"--runner-type"}) 222 RunnerType runnerType; 223 224 @Option(names = {"--sdk-version"}) 225 Integer sdkVersion = 28; 226 227 @Option(names = {"--serial-dexing"}) 228 boolean serialDexing = false; 229 230 @Option(names = {"--verbose-dex-stats"}) 231 boolean verboseDexStats = false; 232 Vogar()233 @VisibleForTesting public Vogar() {} 234 printUsage()235 private void printUsage() { 236 // have to reset fields so that "Default is: FOO" lines are accurate 237 optionParser.reset(); 238 239 System.out.println("Usage: Vogar [options]... <actions>... [-- target args]..."); 240 System.out.println(); 241 System.out.println(" <actions>: .java files, directories, or class names."); 242 System.out.println(" These should be JUnit tests, TestNG tests, jtreg tests, Caliper benchmarks"); 243 System.out.println(" or executable Java classes."); 244 System.out.println(); 245 System.out.println(" When passing in a JUnit or TestNG test class, it may have \"#method_name\""); 246 System.out.println(" appended to it, to specify a single test method."); 247 System.out.println(); 248 System.out.println(" [target args]: arguments passed to the target process. This is only useful when"); 249 System.out.println(" the target process is a Caliper benchmark or main method."); 250 System.out.println(); 251 System.out.println("GENERAL OPTIONS"); 252 System.out.println(); 253 System.out.println(" --mode <ACTIVITY|APP_PROCESS|DEVICE|HOST|JVM>: specify which environment to run in."); 254 System.out.println(" ACTIVITY: runs in an Android application on a device or emulator"); 255 System.out.println(" APP_PROCESS: runs in an app_process runtime on a device or emulator"); 256 System.out.println(" DEVICE: runs in an ART runtime on a device or emulator"); 257 System.out.println(" HOST: runs in an ART runtime on the local desktop built with any lunch combo."); 258 System.out.println(" JVM: runs in a Java VM on the local desktop"); 259 System.out.println(" Default is: " + modeId); 260 System.out.println(); 261 System.out.println(" --variant <DEFAULT|X32|X64>: specify which dalvikvm (or other) variant to execute with"); 262 System.out.println(" Used with --mode <HOST|DEVICE> only, not applicable for all devices."); 263 System.out.println(" DEFAULT: default (or N/A), X32: 32-bit, X64: 64-bit"); 264 System.out.println(" Default is: " + variant); 265 System.out.println(); 266 System.out.println(" --toolchain <D8|JAVAC>: Which toolchain to use."); 267 System.out.println(" Default depends on --mode value (currently " 268 + modeId.defaultToolchain() + " for --mode=" + modeId + ")"); 269 System.out.println(); 270 System.out.println(" --language <J17|JN|JO|CUR>: Which language level to use."); 271 System.out.println(" Default is: " + language); 272 System.out.println(); 273 System.out.println(" --ssh <host:port>: target a remote machine via SSH."); 274 System.out.println(); 275 System.out.println(" --chroot <dir>: target a chroot dir on device"); 276 System.out.println(" Only works with --mode device."); 277 System.out.println(); 278 System.out.println(" --clean: synonym for --clean-before and --clean-after (default)."); 279 System.out.println(" Disable with --no-clean if you want no files removed."); 280 System.out.println(); 281 System.out.println(" --stream: stream output as it is emitted."); 282 System.out.println(); 283 System.out.println(" --benchmark: for use with dalvikvm, this dexes all files together,"); 284 System.out.println(" and is mandatory for running Caliper benchmarks, and a good idea"); 285 System.out.println(" for other performance sensitive code."); 286 System.out.println(" If you specify this without specifying --runner-type then it"); 287 System.out.println(" assumes --runner-type=" 288 + RunnerType.CALIPER.name().toLowerCase()); 289 System.out.println(); 290 System.out.println(" --invoke-with: provide a command to invoke the VM with. Examples:"); 291 System.out.println(" --mode host --invoke-with \"valgrind --leak-check=full\""); 292 System.out.println(" --mode device --invoke-with \"strace -f -o/sdcard/strace.txt\""); 293 System.out.println(); 294 System.out.println(" --timeout <seconds>: maximum execution time of each action before the"); 295 System.out.println(" runner aborts it. Specifying zero seconds or using --debug will"); 296 System.out.println(" disable the execution timeout. Tests tagged with 'large' will time"); 297 System.out.println(" out in " + LARGE_TIMEOUT_MULTIPLIER + "x this timeout."); 298 System.out.println(" Default is: " + timeoutSeconds); 299 System.out.println(); 300 System.out.println(" --xml-reports-directory <path>: directory to emit JUnit-style"); 301 System.out.println(" XML test results."); 302 System.out.println(); 303 System.out.println(" --classpath <jar file>: add the .jar to both build and execute classpaths."); 304 System.out.println(); 305 System.out.println(" --use-bootclasspath: use the classpath as search path for bootstrap classes."); 306 System.out.println(); 307 System.out.println(" --build-classpath <element>: add the directory or .jar to the build"); 308 System.out.println(" classpath. Such classes are available as build dependencies, but"); 309 System.out.println(" not at runtime."); 310 System.out.println(); 311 System.out.println(" --sourcepath <directory>: add the directory to the build sourcepath."); 312 System.out.println(); 313 System.out.println(" --vogar-dir <directory>: directory in which to find Vogar"); 314 System.out.println(" configuration information, caches, saved and results"); 315 System.out.println(" unless they've been put explicitly elsewhere."); 316 System.out.println(" Default is: " + vogarDir); 317 System.out.println(); 318 System.out.println(" --record-results: record test results for future comparison."); 319 System.out.println(); 320 System.out.println(" --results-dir <directory>: read and write (if --record-results used)"); 321 System.out.println(" results from and to this directory."); 322 System.out.println(); 323 System.out.println(" --runner-type <default|caliper|main|junit|testng>: specify which runner to use."); 324 System.out.println(" default: runs JUnit tests, TestNG tests and main() classes"); 325 System.out.println(" caliper: runs Caliper benchmarks only"); 326 System.out.println(" main: runs main() classes only"); 327 System.out.println(" junit: runs JUnit tests only"); 328 System.out.println(" testng: runs TestNG tests only"); 329 System.out.println(" Default is determined by --benchmark and --testonly, if they are"); 330 System.out.println(" not specified then defaults to: default"); 331 System.out.println(); 332 System.out.println(" --test-only: only run JUnit tests."); 333 System.out.println(" Default is: " + testOnly); 334 System.out.println(" DEPRECATED: Use --runner-type=" 335 + RunnerType.JUNIT.name().toLowerCase()); 336 System.out.println(); 337 System.out.println(" --verbose: turn on persistent verbose output."); 338 System.out.println(); 339 System.out.println(" --serial-dexing: disallow Vogar spawn multiple simultaneous dex tasks"); 340 System.out.println(" Enabling this is useful when there is a memory constraint;"); 341 System.out.println(" and each dex task could easily consume around 1.5G of memory."); 342 System.out.println(" Default is: " + serialDexing); 343 System.out.println(); 344 System.out.println(" --verbose-dex-stats: print verbose stats of used resources by dex tasks"); 345 System.out.println(" Enabling this wraps each dex task in '/usr/bin/time -v' call"); 346 System.out.println(" and adds its output to the stdout log. Useful to get a sense of"); 347 System.out.println(" resource usage such as RSS memory, CPU usage and wall-clock time."); 348 System.out.println(" Default is: " + verboseDexStats); 349 System.out.println(); 350 System.out.println(" --check-jni: enable CheckJNI mode."); 351 System.out.println(" See http://developer.android.com/training/articles/perf-jni.html."); 352 System.out.println(" Default is: " + checkJni + ", but disabled for --benchmark."); 353 System.out.println(""); 354 System.out.println("TARGET OPTIONS"); 355 System.out.println(); 356 System.out.println(" --debug <port>: enable Java debugging on the specified port."); 357 System.out.println(" This port must be free both on the device and on the local"); 358 System.out.println(" system. Disables the timeout specified by --timeout-seconds."); 359 System.out.println(); 360 System.out.println(" --debug-app: enable debugging while running in an activity."); 361 System.out.println(" This will require the use of DDMS to connect to the activity"); 362 System.out.println(" on the device, and expose the debugger on an appropriate port."); 363 System.out.println(); 364 System.out.println(" --device-dir <directory>: use the specified directory for"); 365 System.out.println(" on-device temporary files and code."); 366 System.out.println(); 367 System.out.println(" --vm-arg <argument>: include the specified argument when spawning a"); 368 System.out.println(" virtual machine. Examples: -Xint:fast, -ea, -Xmx16M"); 369 System.out.println(); 370 System.out.println(" --vm-command <argument>: override default vm executable name."); 371 System.out.println(" Default is 'java' for the JVM and a version of dalvikvm for the host and target."); 372 System.out.println(); 373 System.out.println(" --java-home <java_home>: execute the actions on the local workstation"); 374 System.out.println(" using the specified java home directory. This does not impact"); 375 System.out.println(" which javac gets used. When unset, java is used from the PATH."); 376 System.out.println(); 377 System.out.println(" --exclude-filter <java_annotation>: exclude the annotated tests"); 378 System.out.println(); 379 System.out.println("EXOTIC OPTIONS"); 380 System.out.println(); 381 System.out.println(" --suggest-classpaths: build an index of jar files under the"); 382 System.out.println(" directories given by --jar-search-dir arguments. If Vogar then "); 383 System.out.println(" fails due to missing classes or packages, it will use the index to"); 384 System.out.println(" diagnose the problem and suggest a fix."); 385 System.out.println(); 386 System.out.println(" Currently only looks for jars called exactly \"classes.jar\"."); 387 System.out.println(); 388 System.out.println(" --jar-search-dir <directory>: a directory that should be searched for"); 389 System.out.println(" jar files to add to the class file index for use with"); 390 System.out.println(" --suggest-classpaths."); 391 System.out.println(); 392 System.out.println(" --clean-before: remove working directories before building and"); 393 System.out.println(" running (default). Disable with --no-clean-before if you are"); 394 System.out.println(" using interactively with your own temporary input files."); 395 System.out.println(); 396 System.out.println(" --clean-after: remove temporary files after running (default)."); 397 System.out.println(" Disable with --no-clean-after and use with --verbose if"); 398 System.out.println(" you'd like to manually re-run commands afterwards."); 399 System.out.println(); 400 System.out.println(" --color: format output in technicolor."); 401 System.out.println(); 402 System.out.println(" --pass-color: ANSI color code to use for passes."); 403 System.out.println(" Default: 32 (green)"); 404 System.out.println(); 405 System.out.println(" --skip-color: ANSI color code to use for skips."); 406 System.out.println(" Default: 33 (yellow)"); 407 System.out.println(); 408 System.out.println(" --warn-color: ANSI color code to use for warnings."); 409 System.out.println(" Default: 35 (purple)"); 410 System.out.println(); 411 System.out.println(" --fail-color: ANSI color code to use for failures."); 412 System.out.println(" Default: 31 (red)"); 413 System.out.println(); 414 System.out.println(" --ansi: use ANSI escape sequences to remove intermediate output."); 415 System.out.println(); 416 System.out.println(" --expectations <file>: include the specified file when looking for"); 417 System.out.println(" action expectations. The file should include qualified action names"); 418 System.out.println(" and the corresponding expected output."); 419 System.out.println(" Default is: " + expectationFiles); 420 System.out.println(); 421 System.out.println(" --indent: amount to indent action result output. Can be set to ''"); 422 System.out.println(" (aka empty string) to simplify output parsing."); 423 System.out.println(" Default is: '" + indent + "'"); 424 System.out.println(); 425 System.out.println(" --javac-arg <argument>: include the specified argument when invoking"); 426 System.out.println(" javac. Examples: --javac-arg -Xmaxerrs --javac-arg 1"); 427 System.out.println(); 428 System.out.println(" --multidex: whether to use native multidex support"); 429 System.out.println(" Disable with --no-multidex."); 430 System.out.println(" Default is: " + multidex); 431 System.out.println(); 432 System.out.println(" --sdk-version <argument>: min and target sdk version."); 433 System.out.println(" Used in the app manifest for ACTIVITY mode"); 434 System.out.println(" to prevent warning popups about old applications"); 435 System.out.println(" Default is: " + sdkVersion); 436 System.out.println(); 437 System.out.println(" --dalvik-cache <argument>: override default dalvik-cache location."); 438 System.out.println(" Default is: " + dalvikCache); 439 System.out.println(); 440 System.out.println(" --first-monitor-port <port>: the port on the host (and possibly target)"); 441 System.out.println(" used to traffic control messages between vogar and forked processes."); 442 System.out.println(" Use this to avoid port conflicts when running multiple vogar instances"); 443 System.out.println(" concurrently. Vogar will use up to N ports starting with this one,"); 444 System.out.println(" where N is the number of processors on the host (" + NUM_PROCESSORS + "). "); 445 System.out.println(); 446 System.out.println(" --open-bugs-command <command>: a command that will take bug IDs as parameters"); 447 System.out.println(" and return those bugs that are still open. For example, if bugs 123 and"); 448 System.out.println(" 789 are both open, the command should echo those values:"); 449 System.out.println(" $ ~/bin/bug-command 123 456 789"); 450 System.out.println(" 123"); 451 System.out.println(" 789"); 452 System.out.println(); 453 System.out.println("CONFIG FILE"); 454 System.out.println(); 455 System.out.println(" User-defined default arguments can be specified in ~/.vogarconfig. See"); 456 System.out.println(" .vogarconfig.example for an example."); 457 System.out.println(); 458 } 459 460 @VisibleForTesting parseArgs(String[] args)461 public boolean parseArgs(String[] args) { 462 // extract arguments from config file 463 configArgs = OptionParser.readFile(configFile); 464 465 // config file args are added first so that in a conflict, the currently supplied 466 // arguments win. 467 List<String> actionsAndTargetArgs = optionParser.parse(configArgs); 468 if (!actionsAndTargetArgs.isEmpty()) { 469 throw new RuntimeException( 470 "actions or targets given in .vogarconfig: " + actionsAndTargetArgs); 471 } 472 473 try { 474 actionsAndTargetArgs.addAll(optionParser.parse(args)); 475 } catch (RuntimeException e) { 476 System.out.println(e.getMessage()); 477 return false; 478 } 479 480 // 481 // Semantic error validation 482 // 483 484 if (javaHome != null && !new File(javaHome, "/bin/java").exists()) { 485 System.out.println("Invalid java home: " + javaHome); 486 return false; 487 } 488 489 // check vm option consistency 490 if (!modeId.acceptsVmArgs() && !vmArgs.isEmpty()) { 491 System.out.println("VM args " + vmArgs + " should not be specified for mode " + modeId); 492 return false; 493 } 494 495 // Check variant / mode compatibility. 496 if (!modeId.supportsVariant(variant)) { 497 System.out.println("Variant " + variant + " not supported for mode " + modeId); 498 return false; 499 } 500 501 if (toolchain == null) { 502 toolchain = modeId.defaultToolchain(); 503 System.out.println("Defaulting --toolchain to " + toolchain); 504 } else if (!modeId.supportsToolchain(toolchain)) { 505 System.out.println("Toolchain " + toolchain + " not supported for mode " + modeId); 506 return false; 507 } 508 509 if (chrootDir != null && !modeId.supportsChroot()) { 510 System.out.println("Chroot-based execution not supported for mode " + modeId); 511 return false; 512 } 513 514 if (xmlReportsDirectory != null && !xmlReportsDirectory.isDirectory()) { 515 System.out.println("Invalid XML reports directory: " + xmlReportsDirectory); 516 return false; 517 } 518 519 if (!clean) { 520 cleanBefore = false; 521 cleanAfter = false; 522 } 523 524 // 525 // Post-processing arguments 526 // 527 528 if (vmCommand == null) { 529 vmCommand = modeId.defaultVmCommand(variant); 530 } 531 532 // disable timeout when benchmarking or debugging 533 if (benchmark || debugPort != null) { 534 timeoutSeconds = 0; 535 } 536 537 if (firstMonitorPort == -1) { 538 firstMonitorPort = modeId.isLocal() ? 8788 : 8787; 539 } 540 541 // separate the actions and the target args 542 int index = 0; 543 for (; index < actionsAndTargetArgs.size(); index++) { 544 String arg = actionsAndTargetArgs.get(index); 545 if (arg.equals("--")) { 546 index++; 547 break; 548 } 549 550 File file = new File(arg); 551 if (file.exists()) { 552 if (arg.endsWith(".java") || file.isDirectory()) { 553 actionFiles.add(file.getAbsoluteFile()); 554 } else { 555 System.out.println("Expected a .jar file, .java file, directory, " 556 + "package name or classname, but was: " + arg); 557 return false; 558 } 559 } else { 560 actionClassesAndPackages.add(arg); 561 } 562 } 563 564 targetArgs.addAll(actionsAndTargetArgs.subList(index, actionsAndTargetArgs.size())); 565 566 if (actionFiles.isEmpty() && actionClassesAndPackages.isEmpty()) { 567 System.out.println("No actions provided."); 568 return false; 569 } 570 571 if (!modeId.acceptsVmArgs() && !targetArgs.isEmpty()) { 572 System.out.println("Target args " + targetArgs + " should not be specified for mode " + modeId); 573 return false; 574 } 575 576 if (modeId == ModeId.ACTIVITY && debugPort != null) { 577 System.out.println("Activity debugging requires the use of --debug-app and DDMS."); 578 return false; 579 } 580 581 if (debugApp && modeId != ModeId.ACTIVITY) { 582 System.out.println("--debug-app can only be used in combination with --mode activity."); 583 return false; 584 } 585 586 // When using --benchmark, 587 // caliper will spawn each benchmark as a new process (default dalvikvm). 588 // 589 // When using also --mode app_process, we want that new process to be app_process. 590 // 591 // Pass --vm app_process to it so that it knows not to use dalvikvm. 592 if ("app_process".equals(vmCommand) && benchmark) { 593 targetArgs.add("--vm"); 594 targetArgs.add("app_process"); 595 } 596 597 return true; 598 } 599 600 /** 601 * The type of the target. 602 */ 603 private enum TargetType { 604 ADB(AdbTarget.defaultDeviceDir()), 605 ADB_CHROOT(AdbChrootTarget.defaultDeviceDir()), 606 LOCAL(LocalTarget.defaultDeviceDir()), 607 SSH(SshTarget.defaultDeviceDir()); 608 609 /** 610 * The default device dir. 611 */ 612 private final File defaultDeviceDir; 613 TargetType(File defaultDeviceDir)614 TargetType(File defaultDeviceDir) { 615 this.defaultDeviceDir = defaultDeviceDir; 616 } 617 defaultDeviceDir()618 public File defaultDeviceDir() { 619 return defaultDeviceDir; 620 } 621 } 622 run()623 private boolean run() throws IOException { 624 // Create a new Console for use by Run. 625 Console console = this.stream 626 ? new Console.StreamingConsole() 627 : new Console.MultiplexingConsole(); 628 console.setUseColor(color, passColor, skipColor, failColor, warnColor); 629 console.setAnsi(ansi); 630 console.setIndent(indent); 631 console.setVerbose(verbose); 632 633 Mkdir mkdir = new Mkdir(console); 634 Rm rm = new Rm(console); 635 636 // Select the target type, this is needed in order to calculate the runnerDir, which is in 637 // turn needed for creating the AdbTarget below. 638 TargetType targetType; 639 if (sshHost != null) { 640 targetType = TargetType.SSH; 641 } else if (modeId.isLocal()) { 642 targetType = TargetType.LOCAL; 643 } else if (chrootDir != null) { 644 targetType = TargetType.ADB_CHROOT; 645 } else { 646 targetType = TargetType.ADB; 647 } 648 649 File runnerDir = deviceDir != null 650 ? new File(deviceDir, "run") 651 : new File(targetType.defaultDeviceDir(), "run"); 652 653 // Create the target. 654 Target target; 655 switch (targetType) { 656 case ADB: { 657 ImmutableList<String> targetProcessPrefix = ImmutableList.of("adb", "shell"); 658 DeviceFilesystem deviceFilesystem = 659 new DeviceFilesystem(console, targetProcessPrefix); 660 DeviceFileCache deviceFileCache = 661 new DeviceFileCache(console, runnerDir, deviceFilesystem); 662 target = new AdbTarget(console, deviceFilesystem, deviceFileCache); 663 } 664 break; 665 case ADB_CHROOT: { 666 ImmutableList<String> targetProcessPrefix = ImmutableList.of("adb", "shell"); 667 DeviceFilesystem deviceFilesystem = 668 new DeviceFilesystem(console, targetProcessPrefix); 669 // Directory `runnerDir` is relative to the chroot; `runnerDirInRoot` is its 670 // counterpart relative to the device's filesystem "absolute" root. 671 File runnerDirInRoot = new File(chrootDir + "/" + runnerDir.getPath()); 672 DeviceFileCache deviceFileCache = 673 new DeviceFileCache(console, runnerDirInRoot, deviceFilesystem); 674 target = 675 new AdbChrootTarget(console, deviceFilesystem, deviceFileCache, chrootDir); 676 } 677 break; 678 case SSH: 679 if (chrootDir != null) { 680 target = new SshChrootTarget(console, sshHost, chrootDir); 681 } else { 682 target = new SshTarget(console, sshHost); 683 } 684 break; 685 case LOCAL: 686 target = new LocalTarget(console, mkdir, rm); 687 break; 688 default: 689 throw new IllegalStateException("Unknown target type: " + targetType); 690 } 691 692 AndroidSdk androidSdk = null; 693 if (modeId.requiresAndroidSdk()) { 694 androidSdk = AndroidSdk.createAndroidSdk(console, mkdir, modeId, language, 695 !actionFiles.isEmpty(), serialDexing, verboseDexStats); 696 } 697 698 if (runnerType == null) { 699 if (benchmark) { 700 if (testOnly) { 701 throw new IllegalStateException( 702 "--benchmark and --testOnly are mutually exclusive and deprecated," 703 + " use --runner-type"); 704 } 705 if (modeId == ModeId.ACTIVITY) { 706 throw new IllegalStateException( 707 "--benchmark and --mode activity are mutually exclusive"); 708 } 709 runnerType = RunnerType.CALIPER; 710 } else if (testOnly) { 711 runnerType = RunnerType.JUNIT; 712 } else { 713 runnerType = RunnerType.DEFAULT; 714 } 715 } else { 716 if (testOnly) { 717 throw new IllegalStateException( 718 "--runnerType and --testOnly are mutually exclusive"); 719 } 720 721 if (runnerType.supportsCaliper()) { 722 if (modeId == ModeId.ACTIVITY) { 723 throw new IllegalStateException( 724 "--runnerType caliper and --mode activity are mutually exclusive"); 725 } 726 727 // Assume --benchmark 728 benchmark = true; 729 } 730 } 731 732 Run run = new Run(this, toolchain, console, mkdir, androidSdk, rm, 733 target, runnerDir); 734 if (configArgs.length > 0) { 735 run.console.verbose("loaded arguments from .vogarconfig: " + 736 Strings.join(" ", (Object)configArgs)); 737 } 738 return run.driver.buildAndRun(actionFiles, actionClassesAndPackages); 739 } 740 main(String[] args)741 public static void main(String[] args) throws IOException { 742 Vogar vogar = new Vogar(); 743 if (!vogar.parseArgs(args)) { 744 vogar.printUsage(); 745 System.exit(1); 746 } 747 boolean allSuccess = vogar.run(); 748 System.exit(allSuccess ? 0 : 1); 749 } 750 } 751