1 /*
   2  * Copyright (c) 2017, 2018, 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 
  25 
  26 package jdk.tools.jaotc;
  27 
  28 import java.text.MessageFormat;
  29 import java.util.ArrayList;
  30 import java.util.LinkedList;
  31 import java.util.List;
  32 
  33 import jdk.tools.jaotc.collect.ClassSearch;
  34 import jdk.tools.jaotc.collect.ClassSource;
  35 import jdk.tools.jaotc.collect.SearchFor;
  36 import jdk.tools.jaotc.collect.SearchPath;
  37 import jdk.tools.jaotc.collect.classname.ClassNameSourceProvider;
  38 import jdk.tools.jaotc.collect.directory.DirectorySourceProvider;
  39 import jdk.tools.jaotc.collect.jar.JarSourceProvider;
  40 import jdk.tools.jaotc.collect.module.ModuleSourceProvider;
  41 
  42 final class Options {
  43     List<SearchFor> files = new LinkedList<>();
  44     String osName;
  45     String outputName = defaultOutputName();
  46     String methodList;
  47     List<ClassSource> sources = new ArrayList<>();
  48     String linkerpath = null;
  49     SearchPath searchPath = new SearchPath();
  50 
  51     /**
  52      * We don't see scaling beyond 16 threads.
  53      */
  54     private static final int COMPILER_THREADS = 16;
  55 
  56     int threads = Integer.min(COMPILER_THREADS, Runtime.getRuntime().availableProcessors());
  57 
  58     boolean ignoreClassLoadingErrors;
  59     boolean exitOnError;
  60     boolean info;
  61     boolean verbose;
  62     boolean debug;
  63     boolean help;
  64     boolean version;
  65     boolean compileWithAssertions;
  66     boolean tiered;
  67 
  68     private String defaultOutputName() {
  69         osName = System.getProperty("os.name");
  70         String name = "unnamed.";
  71         String ext;
  72 
  73         switch (osName) {
  74             case "Linux":
  75             case "SunOS":
  76                 ext = "so";
  77                 break;
  78             case "Mac OS X":
  79                 ext = "dylib";
  80                 break;
  81             default:
  82                 if (osName.startsWith("Windows")) {
  83                     ext = "dll";
  84                 } else {
  85                     ext = "so";
  86                 }
  87         }
  88 
  89         return name + ext;
  90     }
  91 
  92     static class BadArgs extends Exception {
  93         private static final long serialVersionUID = 1L;
  94         final String key;
  95         final Object[] args;
  96         boolean showUsage;
  97 
  98         BadArgs(String key, Object... args) {
  99             super(MessageFormat.format(key, args));
 100             this.key = key;
 101             this.args = args;
 102         }
 103 
 104         BadArgs showUsage(boolean b) {
 105             showUsage = b;
 106             return this;
 107         }
 108     }
 109 
 110     abstract static class Option {
 111         final String help;
 112         final boolean hasArg;
 113         final String[] aliases;
 114 
 115         Option(String help, boolean hasArg, String... aliases) {
 116             this.help = help;
 117             this.hasArg = hasArg;
 118             this.aliases = aliases;
 119         }
 120 
 121         boolean isHidden() {
 122             return false;
 123         }
 124 
 125         boolean matches(String opt) {
 126             for (String a : aliases) {
 127                 if (a.equals(opt)) {
 128                     return true;
 129                 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
 130                     return true;
 131                 }
 132             }
 133             return false;
 134         }
 135 
 136         boolean ignoreRest() {
 137             return false;
 138         }
 139 
 140         abstract void process(Main task, String opt, String arg) throws BadArgs;
 141     }
 142 
 143     static Option[] recognizedOptions = {new Option("  --output <file>            Output file name", true, "--output") {
 144         @Override
 145         void process(Main task, String opt, String arg) {
 146             String name = arg;
 147             task.options.outputName = name;
 148         }
 149     }, new Option("  --class-name <class names> List of classes to compile", true, "--class-name", "--classname") {
 150         @Override
 151         void process(Main task, String opt, String arg) {
 152             task.options.files.addAll(ClassSearch.makeList(ClassNameSourceProvider.TYPE, arg));
 153         }
 154     }, new Option("  --jar <jarfiles>           List of jar files to compile", true, "--jar") {
 155         @Override
 156         void process(Main task, String opt, String arg) {
 157             task.options.files.addAll(ClassSearch.makeList(JarSourceProvider.TYPE, arg));
 158         }
 159     }, new Option("  --module <modules>         List of modules to compile", true, "--module") {
 160         @Override
 161         void process(Main task, String opt, String arg) {
 162             task.options.files.addAll(ClassSearch.makeList(ModuleSourceProvider.TYPE, arg));
 163         }
 164     }, new Option("  --directory <dirs>         List of directories where to search for files to compile", true, "--directory") {
 165         @Override
 166         void process(Main task, String opt, String arg) {
 167             task.options.files.addAll(ClassSearch.makeList(DirectorySourceProvider.TYPE, arg));
 168         }
 169     }, new Option("  --search-path <dirs>       List of directories where to search for specified files", true, "--search-path") {
 170         @Override
 171         void process(Main task, String opt, String arg) {
 172             String[] elements = arg.split(":");
 173             task.options.searchPath.add(elements);
 174         }
 175     }, new Option("  --compile-commands <file>  Name of file with compile commands", true, "--compile-commands") {
 176         @Override
 177         void process(Main task, String opt, String arg) {
 178             task.options.methodList = arg;
 179         }
 180     }, new Option("  --compile-for-tiered       Generate profiling code for tiered compilation", false, "--compile-for-tiered") {
 181         @Override
 182         void process(Main task, String opt, String arg) {
 183             task.options.tiered = true;
 184         }
 185     }, new Option("  --compile-with-assertions  Compile with java assertions", false, "--compile-with-assertions") {
 186         @Override
 187         void process(Main task, String opt, String arg) {
 188             task.options.compileWithAssertions = true;
 189         }
 190     }, new Option("  --compile-threads <number> Number of compilation threads to be used", true, "--compile-threads", "--threads") {
 191         @Override
 192         void process(Main task, String opt, String arg) {
 193             int threads = Integer.parseInt(arg);
 194             final int available = Runtime.getRuntime().availableProcessors();
 195             if (threads <= 0) {
 196                 task.warning("invalid number of threads specified: {0}, using: {1}", threads, available);
 197                 threads = available;
 198             }
 199             if (threads > available) {
 200                 task.warning("too many threads specified: {0}, limiting to: {1}", threads, available);
 201             }
 202             task.options.threads = Integer.min(threads, available);
 203         }
 204     }, new Option("  --ignore-errors            Ignores all exceptions thrown during class loading", false, "--ignore-errors") {
 205         @Override
 206         void process(Main task, String opt, String arg) {
 207             task.options.ignoreClassLoadingErrors = true;
 208         }
 209     }, new Option("  --exit-on-error            Exit on compilation errors", false, "--exit-on-error") {
 210         @Override
 211         void process(Main task, String opt, String arg) {
 212             task.options.exitOnError = true;
 213         }
 214     }, new Option("  --info                     Print information during compilation", false, "--info") {
 215         @Override
 216         void process(Main task, String opt, String arg) throws BadArgs {
 217             task.options.info = true;
 218         }
 219     }, new Option("  --verbose                  Print verbose information", false, "--verbose") {
 220         @Override
 221         void process(Main task, String opt, String arg) throws BadArgs {
 222             task.options.info = true;
 223             task.options.verbose = true;
 224         }
 225     }, new Option("  --debug                    Print debug information", false, "--debug") {
 226         @Override
 227         void process(Main task, String opt, String arg) throws BadArgs {
 228             task.options.info = true;
 229             task.options.verbose = true;
 230             task.options.debug = true;
 231         }
 232     }, new Option("  -? -h --help               Print this help message", false, "--help", "-h", "-?") {
 233         @Override
 234         void process(Main task, String opt, String arg) {
 235             task.options.help = true;
 236         }
 237     }, new Option("  --version                  Version information", false, "--version") {
 238         @Override
 239         void process(Main task, String opt, String arg) {
 240             task.options.version = true;
 241         }
 242     }, new Option("  --linker-path              Full path to linker executable", true, "--linker-path") {
 243         @Override
 244         void process(Main task, String opt, String arg) {
 245             task.options.linkerpath = arg;
 246         }
 247     }, new Option("  -J<flag>                   Pass <flag> directly to the runtime system", false, "-J") {
 248         @Override
 249         void process(Main task, String opt, String arg) {
 250         }
 251     }};
 252 
 253     static void handleOptions(Main task, String[] args) throws BadArgs {
 254         if (args.length == 0) {
 255             task.options.help = true;
 256             return;
 257         }
 258 
 259         // Make checkstyle happy.
 260         int i = 0;
 261         while (i < args.length) {
 262             String arg = args[i];
 263 
 264             if (arg.charAt(0) == '-') {
 265                 Option option = getOption(arg);
 266                 String param = null;
 267 
 268                 if (option.hasArg) {
 269                     if (arg.startsWith("--") && arg.indexOf('=') > 0) {
 270                         param = arg.substring(arg.indexOf('=') + 1, arg.length());
 271                     } else if (i + 1 < args.length) {
 272                         param = args[++i];
 273                     }
 274 
 275                     if (param == null || param.isEmpty() || param.charAt(0) == '-') {
 276                         throw new BadArgs("missing argument for option: {0}", arg).showUsage(true);
 277                     }
 278                 }
 279 
 280                 option.process(task, arg, param);
 281 
 282                 if (option.ignoreRest()) {
 283                     break;
 284                 }
 285             } else {
 286                 task.options.files.add(new SearchFor(arg));
 287             }
 288             i++;
 289         }
 290     }
 291 
 292     static Option getOption(String name) throws BadArgs {
 293         for (Option o : recognizedOptions) {
 294             if (o.matches(name)) {
 295                 return o;
 296             }
 297         }
 298         throw new BadArgs("unknown option: {0}", name).showUsage(true);
 299     }
 300 
 301 }