1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.tools.jaotc;
  25 
  26 import java.text.MessageFormat;
  27 import java.util.ArrayList;
  28 import java.util.LinkedList;
  29 import java.util.List;
  30 
  31 import jdk.tools.jaotc.collect.ClassSearch;
  32 import jdk.tools.jaotc.collect.ClassSource;
  33 import jdk.tools.jaotc.collect.SearchFor;
  34 import jdk.tools.jaotc.collect.SearchPath;
  35 import jdk.tools.jaotc.collect.classname.ClassNameSourceProvider;
  36 import jdk.tools.jaotc.collect.directory.DirectorySourceProvider;
  37 import jdk.tools.jaotc.collect.jar.JarSourceProvider;
  38 import jdk.tools.jaotc.collect.module.ModuleSourceProvider;
  39 
  40 final class Options {
  41     List<SearchFor> files = new LinkedList<>();
  42     String osName;
  43     String outputName = defaultOutputName();
  44     String methodList;
  45     List<ClassSource> sources = new ArrayList<>();
  46     String linkerpath = null;
  47     SearchPath searchPath = new SearchPath();
  48 
  49     /**
  50      * We don't see scaling beyond 16 threads.
  51      */
  52     private static final int COMPILER_THREADS = 16;
  53 
  54     int threads = Integer.min(COMPILER_THREADS, Runtime.getRuntime().availableProcessors());
  55 
  56     boolean ignoreClassLoadingErrors;
  57     boolean exitOnError;
  58     boolean info;
  59     boolean verbose;
  60     boolean debug;
  61     boolean help;
  62     boolean version;
  63     boolean compileWithAssertions;
  64     boolean tiered;
  65 
  66     private String defaultOutputName() {
  67         osName = System.getProperty("os.name");
  68         String name = "unnamed.";
  69         String ext;
  70 
  71         switch (osName) {
  72             case "Linux":
  73             case "SunOS":
  74                 ext = "so";
  75                 break;
  76             case "Mac OS X":
  77                 ext = "dylib";
  78                 break;
  79             default:
  80                 if (osName.startsWith("Windows")) {
  81                     ext = "dll";
  82                 } else {
  83                     ext = "so";
  84                 }
  85         }
  86 
  87         return name + ext;
  88     }
  89 
  90     static class BadArgs extends Exception {
  91         private static final long serialVersionUID = 1L;
  92         final String key;
  93         final Object[] args;
  94         boolean showUsage;
  95 
  96         BadArgs(String key, Object... args) {
  97             super(MessageFormat.format(key, args));
  98             this.key = key;
  99             this.args = args;
 100         }
 101 
 102         BadArgs showUsage(boolean b) {
 103             showUsage = b;
 104             return this;
 105         }
 106     }
 107 
 108     abstract static class Option {
 109         final String help;
 110         final boolean hasArg;
 111         final String[] aliases;
 112 
 113         Option(String help, boolean hasArg, String... aliases) {
 114             this.help = help;
 115             this.hasArg = hasArg;
 116             this.aliases = aliases;
 117         }
 118 
 119         boolean isHidden() {
 120             return false;
 121         }
 122 
 123         boolean matches(String opt) {
 124             for (String a : aliases) {
 125                 if (a.equals(opt)) {
 126                     return true;
 127                 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
 128                     return true;
 129                 }
 130             }
 131             return false;
 132         }
 133 
 134         boolean ignoreRest() {
 135             return false;
 136         }
 137 
 138         abstract void process(Main task, String opt, String arg) throws BadArgs;
 139     }
 140 
 141     static Option[] recognizedOptions = {new Option("  --output <file>            Output file name", true, "--output") {
 142         @Override
 143         void process(Main task, String opt, String arg) {
 144             String name = arg;
 145             task.options.outputName = name;
 146         }
 147     }, new Option("  --class-name <class names> List of classes to compile", true, "--class-name", "--classname") {
 148         @Override
 149         void process(Main task, String opt, String arg) {
 150             task.options.files.addAll(ClassSearch.makeList(ClassNameSourceProvider.TYPE, arg));
 151         }
 152     }, new Option("  --jar <jarfiles>           List of jar files to compile", true, "--jar") {
 153         @Override
 154         void process(Main task, String opt, String arg) {
 155             task.options.files.addAll(ClassSearch.makeList(JarSourceProvider.TYPE, arg));
 156         }
 157     }, new Option("  --module <modules>         List of modules to compile", true, "--module") {
 158         @Override
 159         void process(Main task, String opt, String arg) {
 160             task.options.files.addAll(ClassSearch.makeList(ModuleSourceProvider.TYPE, arg));
 161         }
 162     }, new Option("  --directory <dirs>         List of directories where to search for files to compile", true, "--directory") {
 163         @Override
 164         void process(Main task, String opt, String arg) {
 165             task.options.files.addAll(ClassSearch.makeList(DirectorySourceProvider.TYPE, arg));
 166         }
 167     }, new Option("  --search-path <dirs>       List of directories where to search for specified files", true, "--search-path") {
 168         @Override
 169         void process(Main task, String opt, String arg) {
 170             String[] elements = arg.split(":");
 171             task.options.searchPath.add(elements);
 172         }
 173     }, new Option("  --compile-commands <file>  Name of file with compile commands", true, "--compile-commands") {
 174         @Override
 175         void process(Main task, String opt, String arg) {
 176             task.options.methodList = arg;
 177         }
 178     }, new Option("  --compile-for-tiered       Generate profiling code for tiered compilation", false, "--compile-for-tiered") {
 179         @Override
 180         void process(Main task, String opt, String arg) {
 181             task.options.tiered = true;
 182         }
 183     }, new Option("  --compile-with-assertions  Compile with java assertions", false, "--compile-with-assertions") {
 184         @Override
 185         void process(Main task, String opt, String arg) {
 186             task.options.compileWithAssertions = true;
 187         }
 188     }, new Option("  --compile-threads <number> Number of compilation threads to be used", true, "--compile-threads", "--threads") {
 189         @Override
 190         void process(Main task, String opt, String arg) {
 191             int threads = Integer.parseInt(arg);
 192             final int available = Runtime.getRuntime().availableProcessors();
 193             if (threads <= 0) {
 194                 task.warning("invalid number of threads specified: {0}, using: {1}", threads, available);
 195                 threads = available;
 196             }
 197             if (threads > available) {
 198                 task.warning("too many threads specified: {0}, limiting to: {1}", threads, available);
 199             }
 200             task.options.threads = Integer.min(threads, available);
 201         }
 202     }, new Option("  --ignore-errors            Ignores all exceptions thrown during class loading", false, "--ignore-errors") {
 203         @Override
 204         void process(Main task, String opt, String arg) {
 205             task.options.ignoreClassLoadingErrors = true;
 206         }
 207     }, new Option("  --exit-on-error            Exit on compilation errors", false, "--exit-on-error") {
 208         @Override
 209         void process(Main task, String opt, String arg) {
 210             task.options.exitOnError = true;
 211         }
 212     }, new Option("  --info                     Print information during compilation", false, "--info") {
 213         @Override
 214         void process(Main task, String opt, String arg) throws BadArgs {
 215             task.options.info = true;
 216         }
 217     }, new Option("  --verbose                  Print verbose information", false, "--verbose") {
 218         @Override
 219         void process(Main task, String opt, String arg) throws BadArgs {
 220             task.options.info = true;
 221             task.options.verbose = true;
 222         }
 223     }, new Option("  --debug                    Print debug information", false, "--debug") {
 224         @Override
 225         void process(Main task, String opt, String arg) throws BadArgs {
 226             task.options.info = true;
 227             task.options.verbose = true;
 228             task.options.debug = true;
 229         }
 230     }, new Option("  -? -h --help               Print this help message", false, "--help", "-h", "-?") {
 231         @Override
 232         void process(Main task, String opt, String arg) {
 233             task.options.help = true;
 234         }
 235     }, new Option("  --version                  Version information", false, "--version") {
 236         @Override
 237         void process(Main task, String opt, String arg) {
 238             task.options.version = true;
 239         }
 240     }, new Option("  --linker-path              Full path to linker executable", true, "--linker-path") {
 241         @Override
 242         void process(Main task, String opt, String arg) {
 243             task.options.linkerpath = arg;
 244         }
 245     }, new Option("  -J<flag>                   Pass <flag> directly to the runtime system", false, "-J") {
 246         @Override
 247         void process(Main task, String opt, String arg) {
 248         }
 249     }};
 250 
 251     static void handleOptions(Main task, String[] args) throws BadArgs {
 252         if (args.length == 0) {
 253             task.options.help = true;
 254             return;
 255         }
 256 
 257         // Make checkstyle happy.
 258         for (int i = 0; i < args.length; i++) {
 259             String arg = args[i];
 260 
 261             if (arg.charAt(0) == '-') {
 262                 Option option = getOption(arg);
 263                 String param = null;
 264 
 265                 if (option.hasArg) {
 266                     if (arg.startsWith("--") && arg.indexOf('=') > 0) {
 267                         param = arg.substring(arg.indexOf('=') + 1, arg.length());
 268                     } else if (i + 1 < args.length) {
 269                         param = args[++i];
 270                     }
 271 
 272                     if (param == null || param.isEmpty() || param.charAt(0) == '-') {
 273                         throw new BadArgs("missing argument for option: {0}", arg).showUsage(true);
 274                     }
 275                 }
 276 
 277                 option.process(task, arg, param);
 278 
 279                 if (option.ignoreRest()) {
 280                     break;
 281                 }
 282             } else {
 283                 task.options.files.add(new SearchFor(arg));
 284             }
 285         }
 286     }
 287 
 288     static Option getOption(String name) throws BadArgs {
 289         for (Option o : recognizedOptions) {
 290             if (o.matches(name)) {
 291                 return o;
 292             }
 293         }
 294         throw new BadArgs("unknown option: {0}", name).showUsage(true);
 295     }
 296 
 297 }