• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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