Option.java revision 3294:9adfb22ff08f
1/*
2 * Copyright (c) 2006, 2016, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.javac.main;
27
28import java.io.FileWriter;
29import java.io.PrintWriter;
30import java.nio.file.Files;
31import java.nio.file.Path;
32import java.nio.file.Paths;
33import java.util.Collections;
34import java.util.EnumSet;
35import java.util.LinkedHashMap;
36import java.util.Map;
37import java.util.ServiceLoader;
38import java.util.Set;
39import java.util.TreeSet;
40import java.util.stream.Collectors;
41import java.util.stream.StreamSupport;
42
43import javax.lang.model.SourceVersion;
44
45import com.sun.tools.doclint.DocLint;
46import com.sun.tools.javac.code.Lint;
47import com.sun.tools.javac.code.Lint.LintCategory;
48import com.sun.tools.javac.code.Source;
49import com.sun.tools.javac.code.Type;
50import com.sun.tools.javac.jvm.Profile;
51import com.sun.tools.javac.jvm.Target;
52import com.sun.tools.javac.platform.PlatformProvider;
53import com.sun.tools.javac.processing.JavacProcessingEnvironment;
54import com.sun.tools.javac.util.Log;
55import com.sun.tools.javac.util.Log.PrefixKind;
56import com.sun.tools.javac.util.Log.WriterKind;
57import com.sun.tools.javac.util.Options;
58import com.sun.tools.javac.util.StringUtils;
59
60import static com.sun.tools.javac.main.Option.ChoiceKind.*;
61import static com.sun.tools.javac.main.Option.OptionGroup.*;
62import static com.sun.tools.javac.main.Option.OptionKind.*;
63
64/**
65 * Options for javac. The specific Option to handle a command-line option
66 * is identified by searching the members of this enum in order, looking for
67 * the first {@link #matches match}. The action for an Option is performed
68 * by calling {@link #process process}, and by providing a suitable
69 * {@link OptionHelper} to provide access the compiler state.
70 *
71 * <p><b>This is NOT part of any supported API.
72 * If you write code that depends on this, you do so at your own
73 * risk.  This code and its internal interfaces are subject to change
74 * or deletion without notice.</b></p>
75 */
76public enum Option {
77    G("-g", "opt.g", STANDARD, BASIC),
78
79    G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
80        @Override
81        public boolean process(OptionHelper helper, String option) {
82            helper.put("-g:", "none");
83            return false;
84        }
85    },
86
87    G_CUSTOM("-g:",  "opt.g.lines.vars.source",
88            STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
89
90    XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
91
92    XLINT_CUSTOM("-Xlint:", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
93        private static final String LINT_KEY_FORMAT = "         %-19s %s";
94        @Override
95        void help(Log log, OptionKind kind) {
96            if (this.kind != kind)
97                return;
98
99            log.printRawLines(WriterKind.NOTICE,
100                              String.format(HELP_LINE_FORMAT,
101                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.subopts"),
102                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.suboptlist")));
103            log.printRawLines(WriterKind.NOTICE,
104                              String.format(LINT_KEY_FORMAT,
105                                            "all",
106                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
107            for (LintCategory lc : LintCategory.values()) {
108                if (lc.hidden) continue;
109                log.printRawLines(WriterKind.NOTICE,
110                                  String.format(LINT_KEY_FORMAT,
111                                                lc.option,
112                                                log.localize(PrefixKind.JAVAC,
113                                                             "opt.Xlint.desc." + lc.option)));
114            }
115            log.printRawLines(WriterKind.NOTICE,
116                              String.format(LINT_KEY_FORMAT,
117                                            "none",
118                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.none")));
119        }
120    },
121
122    XDOCLINT("-Xdoclint", "opt.Xdoclint", EXTENDED, BASIC),
123
124    XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
125        @Override
126        public boolean matches(String option) {
127            return DocLint.isValidOption(
128                    option.replace(XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
129        }
130
131        @Override
132        public boolean process(OptionHelper helper, String option) {
133            String prev = helper.get(XDOCLINT_CUSTOM);
134            String next = (prev == null) ? option : (prev + " " + option);
135            helper.put(XDOCLINT_CUSTOM.text, next);
136            return false;
137        }
138    },
139
140    XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
141        @Override
142        public boolean matches(String option) {
143            return DocLint.isValidOption(
144                    option.replace(XDOCLINT_PACKAGE.text, DocLint.XCHECK_PACKAGE));
145        }
146
147        @Override
148        public boolean process(OptionHelper helper, String option) {
149            String prev = helper.get(XDOCLINT_PACKAGE);
150            String next = (prev == null) ? option : (prev + " " + option);
151            helper.put(XDOCLINT_PACKAGE.text, next);
152            return false;
153        }
154    },
155
156    // -nowarn is retained for command-line backward compatibility
157    NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
158        @Override
159        public boolean process(OptionHelper helper, String option) {
160            helper.put("-Xlint:none", option);
161            return false;
162        }
163    },
164
165    VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC),
166
167    // -deprecation is retained for command-line backward compatibility
168    DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
169        @Override
170        public boolean process(OptionHelper helper, String option) {
171            helper.put("-Xlint:deprecation", option);
172            return false;
173        }
174    },
175
176    CLASSPATH("-classpath", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
177
178    CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
179        @Override
180        public boolean process(OptionHelper helper, String option, String arg) {
181            return super.process(helper, "-classpath", arg);
182        }
183    },
184
185    SOURCEPATH("-sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
186
187    MODULESOURCEPATH("-modulesourcepath", "opt.arg.mspath", "opt.modulesourcepath", STANDARD, FILEMANAGER),
188
189    MODULEPATH("-modulepath", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER),
190
191    MP("-mp", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER) {
192        @Override
193        public boolean process(OptionHelper helper, String option, String arg) {
194            return super.process(helper, "-modulepath", arg);
195        }
196    },
197
198    UPGRADEMODULEPATH("-upgrademodulepath", "opt.arg.path", "opt.upgrademodulepath", STANDARD, FILEMANAGER),
199
200    SYSTEM("-system", "opt.arg.jdk", "opt.system", STANDARD, FILEMANAGER),
201
202    XPATCH("-Xpatch:", "opt.arg.path", "opt.Xpatch", EXTENDED, FILEMANAGER),
203
204    BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
205        @Override
206        public boolean process(OptionHelper helper, String option, String arg) {
207            helper.remove("-Xbootclasspath/p:");
208            helper.remove("-Xbootclasspath/a:");
209            return super.process(helper, option, arg);
210        }
211    },
212
213    XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
214
215    XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
216
217    XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
218        @Override
219        public boolean process(OptionHelper helper, String option, String arg) {
220            helper.remove("-Xbootclasspath/p:");
221            helper.remove("-Xbootclasspath/a:");
222            return super.process(helper, "-bootclasspath", arg);
223        }
224    },
225
226    EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
227
228    DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
229        @Override
230        public boolean process(OptionHelper helper, String option, String arg) {
231            return super.process(helper, "-extdirs", arg);
232        }
233    },
234
235    ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
236
237    DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
238        @Override
239        public boolean process(OptionHelper helper, String option, String arg) {
240            return super.process(helper, "-endorseddirs", arg);
241        }
242    },
243
244    PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
245
246    PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
247
248    PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
249
250    PROCESSORMODULEPATH("-processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
251
252    PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
253
254    D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
255
256    S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
257
258    H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
259
260    IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
261
262    ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
263
264    SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
265        @Override
266        public boolean process(OptionHelper helper, String option, String operand) {
267            Source source = Source.lookup(operand);
268            if (source == null) {
269                helper.error("err.invalid.source", operand);
270                return true;
271            }
272            return super.process(helper, option, operand);
273        }
274    },
275
276    TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
277        @Override
278        public boolean process(OptionHelper helper, String option, String operand) {
279            Target target = Target.lookup(operand);
280            if (target == null) {
281                helper.error("err.invalid.target", operand);
282                return true;
283            }
284            return super.process(helper, option, operand);
285        }
286    },
287
288    RELEASE("-release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
289        @Override
290        void help(Log log, OptionKind kind) {
291            if (this.kind != kind)
292                return;
293
294            Iterable<PlatformProvider> providers =
295                    ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
296            Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
297                                                 .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
298                                                                                                   .spliterator(),
299                                                                                           false))
300                                                 .collect(Collectors.toCollection(TreeSet :: new));
301
302            StringBuilder targets = new StringBuilder();
303            String delim = "";
304            for (String platform : platforms) {
305                targets.append(delim);
306                targets.append(platform);
307                delim = ", ";
308            }
309
310            log.printRawLines(WriterKind.NOTICE,
311                    String.format(HELP_LINE_FORMAT,
312                        super.helpSynopsis(log),
313                        log.localize(PrefixKind.JAVAC, descrKey, targets.toString())));
314        }
315    },
316
317    PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
318        @Override
319        public boolean process(OptionHelper helper, String option, String operand) {
320            Profile profile = Profile.lookup(operand);
321            if (profile == null) {
322                helper.error("err.invalid.profile", operand);
323                return true;
324            }
325            return super.process(helper, option, operand);
326        }
327    },
328
329    VERSION("-version", "opt.version", STANDARD, INFO) {
330        @Override
331        public boolean process(OptionHelper helper, String option) {
332            Log log = helper.getLog();
333            String ownName = helper.getOwnName();
334            log.printLines(PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
335            return super.process(helper, option);
336        }
337    },
338
339    FULLVERSION("-fullversion", null, HIDDEN, INFO) {
340        @Override
341        public boolean process(OptionHelper helper, String option) {
342            Log log = helper.getLog();
343            String ownName = helper.getOwnName();
344            log.printLines(PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
345            return super.process(helper, option);
346        }
347    },
348
349    DIAGS("-XDdiags=", null, HIDDEN, INFO) {
350        @Override
351        public boolean process(OptionHelper helper, String option) {
352            option = option.substring(option.indexOf('=') + 1);
353            String diagsOption = option.contains("%") ?
354                "-XDdiagsFormat=" :
355                "-XDdiags=";
356            diagsOption += option;
357            if (XD.matches(diagsOption))
358                return XD.process(helper, diagsOption);
359            else
360                return false;
361        }
362    },
363
364    HELP("-help", "opt.help", STANDARD, INFO) {
365        @Override
366        public boolean process(OptionHelper helper, String option) {
367            Log log = helper.getLog();
368            String ownName = helper.getOwnName();
369            log.printLines(PrefixKind.JAVAC, "msg.usage.header", ownName);
370            for (Option o: getJavaCompilerOptions()) {
371                o.help(log, OptionKind.STANDARD);
372            }
373            log.printNewline();
374            return super.process(helper, option);
375        }
376    },
377
378    A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
379        @Override
380        public boolean matches(String arg) {
381            return arg.startsWith("-A");
382        }
383
384        @Override
385        public boolean hasArg() {
386            return false;
387        }
388        // Mapping for processor options created in
389        // JavacProcessingEnvironment
390        @Override
391        public boolean process(OptionHelper helper, String option) {
392            int argLength = option.length();
393            if (argLength == 2) {
394                helper.error("err.empty.A.argument");
395                return true;
396            }
397            int sepIndex = option.indexOf('=');
398            String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
399            if (!JavacProcessingEnvironment.isValidOptionName(key)) {
400                helper.error("err.invalid.A.key", option);
401                return true;
402            }
403            return process(helper, option, option);
404        }
405    },
406
407    X("-X", "opt.X", STANDARD, INFO) {
408        @Override
409        public boolean process(OptionHelper helper, String option) {
410            Log log = helper.getLog();
411            for (Option o: getJavaCompilerOptions()) {
412                o.help(log, OptionKind.EXTENDED);
413            }
414            log.printNewline();
415            log.printLines(PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
416            return super.process(helper, option);
417        }
418    },
419
420    // This option exists only for the purpose of documenting itself.
421    // It's actually implemented by the launcher.
422    J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
423        @Override
424        public boolean process(OptionHelper helper, String option) {
425            throw new AssertionError
426                ("the -J flag should be caught by the launcher.");
427        }
428    },
429
430    MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
431        @Override
432        public boolean process(OptionHelper helper, String option) {
433            Type.moreInfo = true;
434            return super.process(helper, option);
435        }
436    },
437
438    // treat warnings as errors
439    WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
440
441    // prompt after each error
442    // new Option("-prompt",                                        "opt.prompt"),
443    PROMPT("-prompt", null, HIDDEN, BASIC),
444
445    // dump stack on error
446    DOE("-doe", null, HIDDEN, BASIC),
447
448    // output source after type erasure
449    PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
450
451    // display warnings for generic unchecked operations
452    WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
453        @Override
454        public boolean process(OptionHelper helper, String option) {
455            helper.put("-Xlint:unchecked", option);
456            return false;
457        }
458    },
459
460    XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
461
462    XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
463
464    XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
465        @Override
466        public boolean process(OptionHelper helper, String option, String arg) {
467            try {
468                Log log = helper.getLog();
469                log.setWriters(new PrintWriter(new FileWriter(arg), true));
470            } catch (java.io.IOException e) {
471                helper.error("err.error.writing.file", arg, e);
472                return true;
473            }
474            return super.process(helper, option, arg);
475        }
476    },
477
478    XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
479
480    XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),
481
482    XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
483
484    XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
485
486    XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
487
488    // see enum PkgInfo
489    XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
490
491    /* -O is a no-op, accepted for backward compatibility. */
492    O("-O", null, HIDDEN, BASIC),
493
494    /* -Xjcov produces tables to support the code coverage tool jcov. */
495    XJCOV("-Xjcov", null, HIDDEN, BASIC),
496
497    PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
498        @Override
499        public boolean process(OptionHelper helper, String option) {
500            String p = option.substring(option.indexOf(':') + 1).trim();
501            String prev = helper.get(PLUGIN);
502            helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p);
503            return false;
504        }
505    },
506
507    XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
508
509    /* This is a back door to the compiler's option table.
510     * -XDx=y sets the option x to the value y.
511     * -XDx sets the option x to the value x.
512     */
513    XD("-XD", null, HIDDEN, BASIC) {
514        @Override
515        public boolean matches(String s) {
516            return s.startsWith(text);
517        }
518        @Override
519        public boolean process(OptionHelper helper, String option) {
520            option = option.substring(text.length());
521            int eq = option.indexOf('=');
522            String key = (eq < 0) ? option : option.substring(0, eq);
523            String value = (eq < 0) ? option : option.substring(eq+1);
524            helper.put(key, value);
525            return false;
526        }
527    },
528
529    XADDEXPORTS("-XaddExports:", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
530        @Override
531        public boolean process(OptionHelper helper, String option) {
532            if (option.matches(".*,.*=.*")) { // temporary, for backwards compatibility
533                return processOldStyle(helper, option);
534            }
535            String p = option.substring(option.indexOf(':') + 1).trim();
536            String prev = helper.get(XADDEXPORTS);
537            helper.put(XADDEXPORTS.text, (prev == null) ? p : prev + '\0' + p);
538            return false;
539        }
540
541        // convert old style option into a series of new-style options
542        private boolean processOldStyle(OptionHelper helper, String option) {
543            String p = option.substring(option.indexOf(':') + 1).trim();
544            String[] entries = p.split("[ ,]+");
545            Map<String, String> map = new LinkedHashMap<>();
546            for (String e: entries) {
547                // Each entry is of the form   module/package=target
548                // we must group values for the same module/package together
549                int eq = e.indexOf('=');
550                if (eq == -1) {
551                    // don't bother with error message for backwards compatible support
552                    continue;
553                }
554                String modPkg = e.substring(0, eq);
555                String target = e.substring(eq + 1);
556                String targets = map.get(modPkg);
557                map.put(modPkg, (targets == null) ? target : targets + "," + target);
558            }
559            boolean ok = true;
560            for (Map.Entry<String, String> e: map.entrySet()) {
561                // process as new-style options
562                String key = e.getKey();
563                String value = e.getValue();
564                ok = ok & process(helper, XADDEXPORTS.text + key + "=" + value);
565            };
566            return ok;
567        }
568    },
569
570    XADDREADS("-XaddReads:", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
571        @Override
572        public boolean process(OptionHelper helper, String option) {
573            if (option.matches(".*,.*=.*")) { // temporary, for backwards compatibility
574                return processOldStyle(helper, option);
575            }
576            String p = option.substring(option.indexOf(':') + 1).trim();
577            String prev = helper.get(XADDREADS);
578            helper.put(XADDREADS.text, (prev == null) ? p : prev + '\0' + p);
579            return false;
580        }
581
582        // convert old style option into a series of new-style options
583        private boolean processOldStyle(OptionHelper helper, String option) {
584            String p = option.substring(option.indexOf(':') + 1).trim();
585            String[] entries = p.split("[ ,]+");
586            Map<String, String> map = new LinkedHashMap<>();
587            for (String e: entries) {
588                // Each entry is of the form   module=target
589                // we must group values for the same module together
590                int eq = e.indexOf('=');
591                if (eq == -1) {
592                    // don't bother with error message for backwards compatible support
593                    continue;
594                }
595                String modPkg = e.substring(0, eq);
596                String target = e.substring(eq + 1);
597                String targets = map.get(modPkg);
598                map.put(modPkg, (targets == null) ? target : targets + "," + target);
599            }
600            boolean ok = true;
601            for (Map.Entry<String, String> e: map.entrySet()) {
602                // process as new-style options
603                String key = e.getKey();
604                String value = e.getValue();
605                ok = ok & process(helper, XADDEXPORTS.text + key + "=" + value);
606            };
607            return ok;
608        }
609    },
610
611    XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
612        @Override
613        public boolean process(OptionHelper helper, String option) {
614            String prev = helper.get(XMODULE);
615            if (prev != null) {
616                helper.error("err.option.too.many", XMODULE.text);
617            }
618            String p = option.substring(option.indexOf(':') + 1);
619            helper.put(XMODULE.text, p);
620            return false;
621        }
622    },
623
624    M("-m", "opt.arg.m", "opt.m", STANDARD, BASIC),
625
626    ADDMODS("-addmods", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC),
627    LIMITMODS("-limitmods", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC),
628
629    // This option exists only for the purpose of documenting itself.
630    // It's actually implemented by the CommandLine class.
631    AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {
632        @Override
633        public boolean process(OptionHelper helper, String option) {
634            throw new AssertionError("the @ flag should be caught by CommandLine.");
635        }
636    },
637
638    // Standalone positional argument: source file or type name.
639    SOURCEFILE("sourcefile", null, HIDDEN, INFO) {
640        @Override
641        public boolean matches(String s) {
642            if (s.endsWith(".java"))  // Java source file
643                return true;
644            int sep = s.indexOf('/');
645            if (sep != -1) {
646                return SourceVersion.isName(s.substring(0, sep))
647                        && SourceVersion.isName(s.substring(sep + 1));
648            } else {
649                return SourceVersion.isName(s);   // Legal type name
650            }
651        }
652        @Override
653        public boolean process(OptionHelper helper, String option) {
654            if (option.endsWith(".java") ) {
655                Path p = Paths.get(option);
656                if (!Files.exists(p)) {
657                    helper.error("err.file.not.found", p);
658                    return true;
659                }
660                if (!Files.isRegularFile(p)) {
661                    helper.error("err.file.not.file", p);
662                    return true;
663                }
664                helper.addFile(p);
665            } else {
666                helper.addClassName(option);
667            }
668            return false;
669        }
670    };
671
672    /** The kind of an Option. This is used by the -help and -X options. */
673    public enum OptionKind {
674        /** A standard option, documented by -help. */
675        STANDARD,
676        /** An extended option, documented by -X. */
677        EXTENDED,
678        /** A hidden option, not documented. */
679        HIDDEN,
680    }
681
682    /** The group for an Option. This determines the situations in which the
683     *  option is applicable. */
684    enum OptionGroup {
685        /** A basic option, available for use on the command line or via the
686         *  Compiler API. */
687        BASIC,
688        /** An option for javac's standard JavaFileManager. Other file managers
689         *  may or may not support these options. */
690        FILEMANAGER,
691        /** A command-line option that requests information, such as -help. */
692        INFO,
693        /** A command-line "option" representing a file or class name. */
694        OPERAND
695    }
696
697    /** The kind of choice for "choice" options. */
698    enum ChoiceKind {
699        /** The expected value is exactly one of the set of choices. */
700        ONEOF,
701        /** The expected value is one of more of the set of choices. */
702        ANYOF
703    }
704
705    public final String text;
706
707    final OptionKind kind;
708
709    final OptionGroup group;
710
711    /** Documentation key for arguments.
712     */
713    final String argsNameKey;
714
715    /** Documentation key for description.
716     */
717    final String descrKey;
718
719    /** Suffix option (-foo=bar or -foo:bar)
720     */
721    final boolean hasSuffix;
722
723    /** The kind of choices for this option, if any.
724     */
725    final ChoiceKind choiceKind;
726
727    /** The choices for this option, if any, and whether or not the choices
728     *  are hidden
729     */
730    final Map<String,Boolean> choices;
731
732
733    Option(String text, String descrKey,
734            OptionKind kind, OptionGroup group) {
735        this(text, null, descrKey, kind, group, null, null, false);
736    }
737
738    Option(String text, String argsNameKey, String descrKey,
739            OptionKind kind, OptionGroup group) {
740        this(text, argsNameKey, descrKey, kind, group, null, null, false);
741    }
742
743    Option(String text, String argsNameKey, String descrKey,
744            OptionKind kind, OptionGroup group, boolean doHasSuffix) {
745        this(text, argsNameKey, descrKey, kind, group, null, null, doHasSuffix);
746    }
747
748    Option(String text, OptionKind kind, OptionGroup group,
749            ChoiceKind choiceKind, Map<String,Boolean> choices) {
750        this(text, null, null, kind, group, choiceKind, choices, false);
751    }
752
753    Option(String text, String descrKey,
754            OptionKind kind, OptionGroup group,
755            ChoiceKind choiceKind, String... choices) {
756        this(text, null, descrKey, kind, group, choiceKind,
757                createChoices(choices), false);
758    }
759    // where
760        private static Map<String,Boolean> createChoices(String... choices) {
761            Map<String,Boolean> map = new LinkedHashMap<>();
762            for (String c: choices)
763                map.put(c, false);
764            return map;
765        }
766
767    private Option(String text, String argsNameKey, String descrKey,
768            OptionKind kind, OptionGroup group,
769            ChoiceKind choiceKind, Map<String,Boolean> choices,
770            boolean doHasSuffix) {
771        this.text = text;
772        this.argsNameKey = argsNameKey;
773        this.descrKey = descrKey;
774        this.kind = kind;
775        this.group = group;
776        this.choiceKind = choiceKind;
777        this.choices = choices;
778        char lastChar = text.charAt(text.length()-1);
779        this.hasSuffix = doHasSuffix || lastChar == ':' || lastChar == '=';
780    }
781
782    public String getText() {
783        return text;
784    }
785
786    public OptionKind getKind() {
787        return kind;
788    }
789
790    public boolean hasArg() {
791        return argsNameKey != null && !hasSuffix;
792    }
793
794    public boolean matches(String option) {
795        if (!hasSuffix)
796            return option.equals(text);
797
798        if (!option.startsWith(text))
799            return false;
800
801        if (choices != null) {
802            String arg = option.substring(text.length());
803            if (choiceKind == ChoiceKind.ONEOF)
804                return choices.keySet().contains(arg);
805            else {
806                for (String a: arg.split(",+")) {
807                    if (!choices.keySet().contains(a))
808                        return false;
809                }
810            }
811        }
812
813        return true;
814    }
815
816    public boolean process(OptionHelper helper, String option, String arg) {
817        if (choices != null) {
818            if (choiceKind == ChoiceKind.ONEOF) {
819                // some clients like to see just one of option+choice set
820                for (String s: choices.keySet())
821                    helper.remove(option + s);
822                String opt = option + arg;
823                helper.put(opt, opt);
824                // some clients like to see option (without trailing ":")
825                // set to arg
826                String nm = option.substring(0, option.length() - 1);
827                helper.put(nm, arg);
828            } else {
829                // set option+word for each word in arg
830                for (String a: arg.split(",+")) {
831                    String opt = option + a;
832                    helper.put(opt, opt);
833                }
834            }
835        }
836        helper.put(option, arg);
837        if (group == OptionGroup.FILEMANAGER)
838            helper.handleFileManagerOption(this, arg);
839        return false;
840    }
841
842    public boolean process(OptionHelper helper, String option) {
843        if (hasSuffix)
844            return process(helper, text, option.substring(text.length()));
845        else
846            return process(helper, option, option);
847    }
848
849    private static final String HELP_LINE_FORMAT = "  %-26s %s";
850
851    void help(Log log, OptionKind kind) {
852        if (this.kind != kind)
853            return;
854
855        log.printRawLines(WriterKind.NOTICE,
856                String.format(HELP_LINE_FORMAT,
857                    helpSynopsis(log),
858                    log.localize(PrefixKind.JAVAC, descrKey)));
859
860    }
861
862    private String helpSynopsis(Log log) {
863        StringBuilder sb = new StringBuilder();
864        sb.append(text);
865        if (argsNameKey == null) {
866            if (choices != null) {
867                String sep = "{";
868                for (Map.Entry<String,Boolean> e: choices.entrySet()) {
869                    if (!e.getValue()) {
870                        sb.append(sep);
871                        sb.append(e.getKey());
872                        sep = ",";
873                    }
874                }
875                sb.append("}");
876            }
877        } else {
878            if (!hasSuffix)
879                sb.append(" ");
880            sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
881
882        }
883
884        return sb.toString();
885    }
886
887    // For -XpkgInfo:value
888    public enum PkgInfo {
889        /**
890         * Always generate package-info.class for every package-info.java file.
891         * The file may be empty if there annotations with a RetentionPolicy
892         * of CLASS or RUNTIME.  This option may be useful in conjunction with
893         * build systems (such as Ant) that expect javac to generate at least
894         * one .class file for every .java file.
895         */
896        ALWAYS,
897        /**
898         * Generate a package-info.class file if package-info.java contains
899         * annotations. The file may be empty if all the annotations have
900         * a RetentionPolicy of SOURCE.
901         * This value is just for backwards compatibility with earlier behavior.
902         * Either of the other two values are to be preferred to using this one.
903         */
904        LEGACY,
905        /**
906         * Generate a package-info.class file if and only if there are annotations
907         * in package-info.java to be written into it.
908         */
909        NONEMPTY;
910
911        public static PkgInfo get(Options options) {
912            String v = options.get(XPKGINFO);
913            return (v == null
914                    ? PkgInfo.LEGACY
915                    : PkgInfo.valueOf(StringUtils.toUpperCase(v)));
916        }
917    }
918
919    private static Map<String,Boolean> getXLintChoices() {
920        Map<String,Boolean> choices = new LinkedHashMap<>();
921        choices.put("all", false);
922        for (Lint.LintCategory c : Lint.LintCategory.values())
923            choices.put(c.option, c.hidden);
924        for (Lint.LintCategory c : Lint.LintCategory.values())
925            choices.put("-" + c.option, c.hidden);
926        choices.put("none", false);
927        return choices;
928    }
929
930    static Set<Option> getJavaCompilerOptions() {
931        return EnumSet.allOf(Option.class);
932    }
933
934    public static Set<Option> getJavacFileManagerOptions() {
935        return getOptions(EnumSet.of(FILEMANAGER));
936    }
937
938    public static Set<Option> getJavacToolOptions() {
939        return getOptions(EnumSet.of(BASIC));
940    }
941
942    static Set<Option> getOptions(Set<OptionGroup> desired) {
943        Set<Option> options = EnumSet.noneOf(Option.class);
944        for (Option option : Option.values())
945            if (desired.contains(option.group))
946                options.add(option);
947        return Collections.unmodifiableSet(options);
948    }
949
950}
951