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