• 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 = { "--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