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