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
24package jdk.tools.jaotc;
25
26import java.text.MessageFormat;
27import java.util.ArrayList;
28import java.util.LinkedList;
29import java.util.List;
30
31import jdk.tools.jaotc.collect.ClassSearch;
32import jdk.tools.jaotc.collect.ClassSource;
33import jdk.tools.jaotc.collect.SearchFor;
34import jdk.tools.jaotc.collect.SearchPath;
35import jdk.tools.jaotc.collect.classname.ClassNameSourceProvider;
36import jdk.tools.jaotc.collect.directory.DirectorySourceProvider;
37import jdk.tools.jaotc.collect.jar.JarSourceProvider;
38import jdk.tools.jaotc.collect.module.ModuleSourceProvider;
39
40final 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("  --help                     Print this usage message", false, "--help") {
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}
298