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 
  26 package com.sun.tools.javac.main;
  27 
  28 import java.io.FileWriter;
  29 import java.io.PrintWriter;
  30 import java.nio.file.Files;
  31 import java.nio.file.Path;
  32 import java.nio.file.Paths;
  33 import java.text.Collator;
  34 import java.util.Arrays;
  35 import java.util.Collections;
  36 import java.util.Comparator;
  37 import java.util.EnumSet;
  38 import java.util.HashSet;
  39 import java.util.Iterator;
  40 import java.util.LinkedHashMap;
  41 import java.util.LinkedHashSet;
  42 import java.util.Locale;
  43 import java.util.Map;
  44 import java.util.ServiceLoader;
  45 import java.util.Set;
  46 import java.util.TreeSet;
  47 import java.util.regex.Matcher;
  48 import java.util.regex.Pattern;
  49 import java.util.stream.Collectors;
  50 import java.util.stream.StreamSupport;
  51 
  52 import javax.lang.model.SourceVersion;
  53 
  54 import com.sun.tools.doclint.DocLint;
  55 import com.sun.tools.javac.code.Lint;
  56 import com.sun.tools.javac.code.Lint.LintCategory;
  57 import com.sun.tools.javac.code.Source;
  58 import com.sun.tools.javac.code.Type;
  59 import com.sun.tools.javac.jvm.Profile;
  60 import com.sun.tools.javac.jvm.Target;
  61 import com.sun.tools.javac.platform.PlatformProvider;
  62 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  63 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  64 import com.sun.tools.javac.util.Assert;
  65 import com.sun.tools.javac.util.JDK9Wrappers;
  66 import com.sun.tools.javac.util.Log;
  67 import com.sun.tools.javac.util.Log.PrefixKind;
  68 import com.sun.tools.javac.util.Log.WriterKind;
  69 import com.sun.tools.javac.util.Options;
  70 import com.sun.tools.javac.util.StringUtils;
  71 
  72 import static com.sun.tools.javac.main.Option.ChoiceKind.*;
  73 import static com.sun.tools.javac.main.Option.OptionGroup.*;
  74 import static com.sun.tools.javac.main.Option.OptionKind.*;
  75 
  76 /**
  77  * Options for javac.
  78  * The specific Option to handle a command-line option can be found by calling
  79  * {@link #lookup}, which search some or all of the members of this enum in order,
  80  * looking for the first {@link #matches match}.
  81  * The action for an Option is performed {@link #handleOption}, which determines
  82  * whether an argument is needed and where to find it;
  83  * {@code handleOption} then calls {@link #process process} providing a suitable
  84  * {@link OptionHelper} to provide access the compiler state.
  85  *
  86  * <p><b>This is NOT part of any supported API.
  87  * If you write code that depends on this, you do so at your own
  88  * risk.  This code and its internal interfaces are subject to change
  89  * or deletion without notice.</b></p>
  90  */
  91 public enum Option {
  92     G("-g", "opt.g", STANDARD, BASIC),
  93 
  94     G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
  95         @Override
  96         public void process(OptionHelper helper, String option) {
  97             helper.put("-g:", "none");
  98         }
  99     },
 100 
 101     G_CUSTOM("-g:",  "opt.g.lines.vars.source",
 102             STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
 103 
 104     XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
 105 
 106     XLINT_CUSTOM("-Xlint:", "opt.arg.Xlint", "opt.Xlint.custom", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
 107         private final String LINT_KEY_FORMAT = LARGE_INDENT + "  %-" +
 108                 (DEFAULT_SYNOPSIS_WIDTH + SMALL_INDENT.length() - LARGE_INDENT.length() - 2) + "s %s";
 109         @Override
 110         protected void help(Log log) {
 111             super.help(log);
 112             log.printRawLines(WriterKind.STDOUT,
 113                               String.format(LINT_KEY_FORMAT,
 114                                             "all",
 115                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
 116             for (LintCategory lc : LintCategory.values()) {
 117                 log.printRawLines(WriterKind.STDOUT,
 118                                   String.format(LINT_KEY_FORMAT,
 119                                                 lc.option,
 120                                                 log.localize(PrefixKind.JAVAC,
 121                                                              "opt.Xlint.desc." + lc.option)));
 122             }
 123             log.printRawLines(WriterKind.STDOUT,
 124                               String.format(LINT_KEY_FORMAT,
 125                                             "none",
 126                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.none")));
 127         }
 128     },
 129 
 130     XDOCLINT("-Xdoclint", "opt.Xdoclint", EXTENDED, BASIC),
 131 
 132     XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
 133         @Override
 134         public boolean matches(String option) {
 135             return DocLint.isValidOption(
 136                     option.replace(XDOCLINT_CUSTOM.primaryName, DocLint.XMSGS_CUSTOM_PREFIX));
 137         }
 138 
 139         @Override
 140         public void process(OptionHelper helper, String option) {
 141             String prev = helper.get(XDOCLINT_CUSTOM);
 142             String next = (prev == null) ? option : (prev + " " + option);
 143             helper.put(XDOCLINT_CUSTOM.primaryName, next);
 144         }
 145     },
 146 
 147     XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
 148         @Override
 149         public boolean matches(String option) {
 150             return DocLint.isValidOption(
 151                     option.replace(XDOCLINT_PACKAGE.primaryName, DocLint.XCHECK_PACKAGE));
 152         }
 153 
 154         @Override
 155         public void process(OptionHelper helper, String option) {
 156             String prev = helper.get(XDOCLINT_PACKAGE);
 157             String next = (prev == null) ? option : (prev + " " + option);
 158             helper.put(XDOCLINT_PACKAGE.primaryName, next);
 159         }
 160     },
 161 
 162     // -nowarn is retained for command-line backward compatibility
 163     NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
 164         @Override
 165         public void process(OptionHelper helper, String option) {
 166             helper.put("-Xlint:none", option);
 167         }
 168     },
 169 
 170     VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC),
 171 
 172     // -deprecation is retained for command-line backward compatibility
 173     DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
 174         @Override
 175         public void process(OptionHelper helper, String option) {
 176             helper.put("-Xlint:deprecation", option);
 177         }
 178     },
 179 
 180     CLASS_PATH("--class-path -classpath -cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
 181 
 182     SOURCE_PATH("--source-path -sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
 183 
 184     MODULE_SOURCE_PATH("--module-source-path", "opt.arg.mspath", "opt.modulesourcepath", STANDARD, FILEMANAGER),
 185 
 186     MODULE_PATH("--module-path -p", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER),
 187 
 188     UPGRADE_MODULE_PATH("--upgrade-module-path", "opt.arg.path", "opt.upgrademodulepath", STANDARD, FILEMANAGER),
 189 
 190     SYSTEM("--system", "opt.arg.jdk", "opt.system", STANDARD, FILEMANAGER),
 191 
 192     PATCH_MODULE("--patch-module", "opt.arg.patch", "opt.patch", EXTENDED, FILEMANAGER) {
 193         // The deferred filemanager diagnostics mechanism assumes a single value per option,
 194         // but --patch-module can be used multiple times, once per module. Therefore we compose
 195         // a value for the option containing the last value specified for each module, and separate
 196         // the the module=path pairs by an invalid path character, NULL.
 197         // The standard file manager code knows to split apart the NULL-separated components.
 198         @Override
 199         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 200             if (arg.isEmpty()) {
 201                 throw helper.newInvalidValueException("err.no.value.for.option", option);
 202             } else if (getPattern().matcher(arg).matches()) {
 203                 String prev = helper.get(PATCH_MODULE);
 204                 if (prev == null) {
 205                     super.process(helper, option, arg);
 206                 } else {
 207                     String argModulePackage = arg.substring(0, arg.indexOf('='));
 208                     boolean isRepeated = Arrays.stream(prev.split("\0"))
 209                             .map(s -> s.substring(0, s.indexOf('=')))
 210                             .collect(Collectors.toSet())
 211                             .contains(argModulePackage);
 212                     if (isRepeated) {
 213                         throw helper.newInvalidValueException("err.repeated.value.for.patch.module", argModulePackage);
 214                     } else {
 215                         super.process(helper, option, prev + '\0' + arg);
 216                     }
 217                 }
 218             } else {
 219                 throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
 220             }
 221         }
 222 
 223         @Override
 224         public Pattern getPattern() {
 225             return Pattern.compile("([^/]+)=(,*[^,].*)");
 226         }
 227     },
 228 
 229     BOOT_CLASS_PATH("--boot-class-path -bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
 230         @Override
 231         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 232             helper.remove("-Xbootclasspath/p:");
 233             helper.remove("-Xbootclasspath/a:");
 234             super.process(helper, option, arg);
 235         }
 236     },
 237 
 238     XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
 239 
 240     XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
 241 
 242     XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
 243         @Override
 244         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 245             helper.remove("-Xbootclasspath/p:");
 246             helper.remove("-Xbootclasspath/a:");
 247             super.process(helper, "-bootclasspath", arg);
 248         }
 249     },
 250 
 251     EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
 252 
 253     DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
 254         @Override
 255         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 256             EXTDIRS.process(helper, "-extdirs", arg);
 257         }
 258     },
 259 
 260     ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
 261 
 262     DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
 263         @Override
 264         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 265             ENDORSEDDIRS.process(helper, "-endorseddirs", arg);
 266         }
 267     },
 268 
 269     PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
 270 
 271     PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
 272 
 273     PROCESSOR_PATH("--processor-path -processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
 274 
 275     PROCESSOR_MODULE_PATH("--processor-module-path", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
 276 
 277     PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
 278 
 279     D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
 280 
 281     S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
 282 
 283     H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
 284 
 285     IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
 286 
 287     ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
 288 
 289     SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
 290         @Override
 291         public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
 292             Source source = Source.lookup(operand);
 293             if (source == null) {
 294                 throw helper.newInvalidValueException("err.invalid.source", operand);
 295             }
 296             super.process(helper, option, operand);
 297         }
 298     },
 299 
 300     TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
 301         @Override
 302         public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
 303             Target target = Target.lookup(operand);
 304             if (target == null) {
 305                 throw helper.newInvalidValueException("err.invalid.target", operand);
 306             }
 307             super.process(helper, option, operand);
 308         }
 309     },
 310 
 311     RELEASE("--release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
 312         @Override
 313         protected void help(Log log) {
 314             Iterable<PlatformProvider> providers =
 315                     ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
 316             Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
 317                                                  .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
 318                                                                                                    .spliterator(),
 319                                                                                            false))
 320                                                  .collect(Collectors.toCollection(TreeSet :: new));
 321 
 322             StringBuilder targets = new StringBuilder();
 323             String delim = "";
 324             for (String platform : platforms) {
 325                 targets.append(delim);
 326                 targets.append(platform);
 327                 delim = ", ";
 328             }
 329 
 330             super.help(log, log.localize(PrefixKind.JAVAC, descrKey, targets.toString()));
 331         }
 332     },
 333 
 334     PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
 335         @Override
 336         public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
 337             Profile profile = Profile.lookup(operand);
 338             if (profile == null) {
 339                 throw helper.newInvalidValueException("err.invalid.profile", operand);
 340             }
 341             super.process(helper, option, operand);
 342         }
 343     },
 344 
 345     VERSION("-version", "opt.version", STANDARD, INFO) {
 346         @Override
 347         public void process(OptionHelper helper, String option) throws InvalidValueException {
 348             Log log = helper.getLog();
 349             String ownName = helper.getOwnName();
 350             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
 351             super.process(helper, option);
 352         }
 353     },
 354 
 355     FULLVERSION("-fullversion", null, HIDDEN, INFO) {
 356         @Override
 357         public void process(OptionHelper helper, String option) throws InvalidValueException {
 358             Log log = helper.getLog();
 359             String ownName = helper.getOwnName();
 360             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
 361             super.process(helper, option);
 362         }
 363     },
 364 
 365     // Note: -h is already taken for "native header output directory".
 366     HELP("--help -help", "opt.help", STANDARD, INFO) {
 367         @Override
 368         public void process(OptionHelper helper, String option) throws InvalidValueException {
 369             Log log = helper.getLog();
 370             String ownName = helper.getOwnName();
 371             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
 372             showHelp(log, OptionKind.STANDARD);
 373             log.printNewline(WriterKind.STDOUT);
 374             super.process(helper, option);
 375         }
 376     },
 377 
 378     A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, ArgKind.ADJACENT) {
 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 void process(OptionHelper helper, String option) throws InvalidValueException {
 392             int argLength = option.length();
 393             if (argLength == 2) {
 394                 throw helper.newInvalidValueException("err.empty.A.argument");
 395             }
 396             int sepIndex = option.indexOf('=');
 397             String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
 398             if (!JavacProcessingEnvironment.isValidOptionName(key)) {
 399                 throw helper.newInvalidValueException("err.invalid.A.key", option);
 400             }
 401             helper.put(option, option);
 402         }
 403     },
 404 
 405     X("-X", "opt.X", STANDARD, INFO) {
 406         @Override
 407         public void process(OptionHelper helper, String option) throws InvalidValueException {
 408             Log log = helper.getLog();
 409             showHelp(log, OptionKind.EXTENDED);
 410             log.printNewline(WriterKind.STDOUT);
 411             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
 412             super.process(helper, option);
 413         }
 414     },
 415 
 416     // This option exists only for the purpose of documenting itself.
 417     // It's actually implemented by the launcher.
 418     J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, ArgKind.ADJACENT) {
 419         @Override
 420         public void process(OptionHelper helper, String option) {
 421             throw new AssertionError("the -J flag should be caught by the launcher.");
 422         }
 423     },
 424 
 425     MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
 426         @Override
 427         public void process(OptionHelper helper, String option) throws InvalidValueException {
 428             Type.moreInfo = true;
 429             super.process(helper, option);
 430         }
 431     },
 432 
 433     // treat warnings as errors
 434     WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
 435 
 436     // prompt after each error
 437     // new Option("-prompt",                                        "opt.prompt"),
 438     PROMPT("-prompt", null, HIDDEN, BASIC),
 439 
 440     // dump stack on error
 441     DOE("-doe", null, HIDDEN, BASIC),
 442 
 443     // output source after type erasure
 444     PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
 445 
 446     // display warnings for generic unchecked operations
 447     WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
 448         @Override
 449         public void process(OptionHelper helper, String option) {
 450             helper.put("-Xlint:unchecked", option);
 451         }
 452     },
 453 
 454     XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
 455 
 456     XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
 457 
 458     XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
 459         @Override
 460         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 461             try {
 462                 Log log = helper.getLog();
 463                 log.setWriters(new PrintWriter(new FileWriter(arg), true));
 464             } catch (java.io.IOException e) {
 465                 throw helper.newInvalidValueException("err.error.writing.file", arg, e);
 466             }
 467             super.process(helper, option, arg);
 468         }
 469     },
 470 
 471     XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
 472 
 473     XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),
 474 
 475     XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
 476 
 477     XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
 478 
 479     XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
 480 
 481     // see enum PkgInfo
 482     XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
 483 
 484     /* -O is a no-op, accepted for backward compatibility. */
 485     O("-O", null, HIDDEN, BASIC),
 486 
 487     /* -Xjcov produces tables to support the code coverage tool jcov. */
 488     XJCOV("-Xjcov", null, HIDDEN, BASIC),
 489 
 490     PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
 491         @Override
 492         public void process(OptionHelper helper, String option) {
 493             String p = option.substring(option.indexOf(':') + 1).trim();
 494             String prev = helper.get(PLUGIN);
 495             helper.put(PLUGIN.primaryName, (prev == null) ? p : prev + '\0' + p);
 496         }
 497     },
 498 
 499     XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
 500 
 501     DEBUG("--debug:", null, HIDDEN, BASIC) {
 502         @Override
 503         public void process(OptionHelper helper, String option) throws InvalidValueException {
 504             HiddenGroup.DEBUG.process(helper, option);
 505         }
 506     },
 507 
 508     SHOULDSTOP("--should-stop:", null, HIDDEN, BASIC) {
 509         @Override
 510         public void process(OptionHelper helper, String option) throws InvalidValueException {
 511             HiddenGroup.SHOULDSTOP.process(helper, option);
 512         }
 513     },
 514 
 515     DIAGS("--diags:", null, HIDDEN, BASIC) {
 516         @Override
 517         public void process(OptionHelper helper, String option) throws InvalidValueException {
 518             HiddenGroup.DIAGS.process(helper, option);
 519         }
 520     },
 521 
 522     /* This is a back door to the compiler's option table.
 523      * -XDx=y sets the option x to the value y.
 524      * -XDx sets the option x to the value x.
 525      */
 526     XD("-XD", null, HIDDEN, BASIC) {
 527         @Override
 528         public boolean matches(String s) {
 529             return s.startsWith(primaryName);
 530         }
 531         @Override
 532         public void process(OptionHelper helper, String option) {
 533             process(helper, option, option.substring(primaryName.length()));
 534         }
 535 
 536         @Override
 537         public void process(OptionHelper helper, String option, String arg) {
 538             int eq = arg.indexOf('=');
 539             String key = (eq < 0) ? arg : arg.substring(0, eq);
 540             String value = (eq < 0) ? arg : arg.substring(eq+1);
 541             helper.put(key, value);
 542         }
 543     },
 544 
 545     ADD_EXPORTS("--add-exports", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
 546         @Override
 547         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 548             if (arg.isEmpty()) {
 549                 throw helper.newInvalidValueException("err.no.value.for.option", option);
 550             } else if (getPattern().matcher(arg).matches()) {
 551                 String prev = helper.get(ADD_EXPORTS);
 552                 helper.put(ADD_EXPORTS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
 553             } else {
 554                 throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
 555             }
 556         }
 557 
 558         @Override
 559         public Pattern getPattern() {
 560             return Pattern.compile("([^/]+)/([^=]+)=(,*[^,].*)");
 561         }
 562     },
 563 
 564     ADD_OPENS("--add-opens", null, null, HIDDEN, BASIC),
 565 
 566     ADD_READS("--add-reads", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
 567         @Override
 568         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 569             if (arg.isEmpty()) {
 570                 throw helper.newInvalidValueException("err.no.value.for.option", option);
 571             } else if (getPattern().matcher(arg).matches()) {
 572                 String prev = helper.get(ADD_READS);
 573                 helper.put(ADD_READS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
 574             } else {
 575                 throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
 576             }
 577         }
 578 
 579         @Override
 580         public Pattern getPattern() {
 581             return Pattern.compile("([^=]+)=(,*[^,].*)");
 582         }
 583     },
 584 
 585     XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
 586         @Override
 587         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 588             String prev = helper.get(XMODULE);
 589             if (prev != null) {
 590                 throw helper.newInvalidValueException("err.option.too.many", XMODULE.primaryName);
 591             }
 592             helper.put(XMODULE.primaryName, arg);
 593         }
 594     },
 595 
 596     MODULE("--module -m", "opt.arg.m", "opt.m", STANDARD, BASIC),
 597 
 598     ADD_MODULES("--add-modules", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC) {
 599         @Override
 600         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 601             if (arg.isEmpty()) {
 602                 throw helper.newInvalidValueException("err.no.value.for.option", option);
 603             } else if (getPattern().matcher(arg).matches()) {
 604                 String prev = helper.get(ADD_MODULES);
 605                 // since the individual values are simple names, we can simply join the
 606                 // values of multiple --add-modules options with ','
 607                 helper.put(ADD_MODULES.primaryName, (prev == null) ? arg : prev + ',' + arg);
 608             } else {
 609                 throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
 610             }
 611         }
 612 
 613         @Override
 614         public Pattern getPattern() {
 615             return Pattern.compile(",*[^,].*");
 616         }
 617     },
 618 
 619     LIMIT_MODULES("--limit-modules", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC) {
 620         @Override
 621         public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
 622             if (arg.isEmpty()) {
 623                 throw helper.newInvalidValueException("err.no.value.for.option", option);
 624             } else if (getPattern().matcher(arg).matches()) {
 625                 helper.put(LIMIT_MODULES.primaryName, arg); // last one wins
 626             } else {
 627                 throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
 628             }
 629         }
 630 
 631         @Override
 632         public Pattern getPattern() {
 633             return Pattern.compile(",*[^,].*");
 634         }
 635     },
 636 
 637     // This option exists only for the purpose of documenting itself.
 638     // It's actually implemented by the CommandLine class.
 639     AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, ArgKind.ADJACENT) {
 640         @Override
 641         public void process(OptionHelper helper, String option) {
 642             throw new AssertionError("the @ flag should be caught by CommandLine.");
 643         }
 644     },
 645 
 646     // Standalone positional argument: source file or type name.
 647     SOURCEFILE("sourcefile", null, HIDDEN, INFO) {
 648         @Override
 649         public boolean matches(String s) {
 650             if (s.endsWith(".java"))  // Java source file
 651                 return true;
 652             int sep = s.indexOf('/');
 653             if (sep != -1) {
 654                 return SourceVersion.isName(s.substring(0, sep))
 655                         && SourceVersion.isName(s.substring(sep + 1));
 656             } else {
 657                 return SourceVersion.isName(s);   // Legal type name
 658             }
 659         }
 660         @Override
 661         public void process(OptionHelper helper, String option) throws InvalidValueException {
 662             if (option.endsWith(".java") ) {
 663                 Path p = Paths.get(option);
 664                 if (!Files.exists(p)) {
 665                     throw helper.newInvalidValueException("err.file.not.found", p);
 666                 }
 667                 if (!Files.isRegularFile(p)) {
 668                     throw helper.newInvalidValueException("err.file.not.file", p);
 669                 }
 670                 helper.addFile(p);
 671             } else {
 672                 helper.addClassName(option);
 673             }
 674         }
 675     },
 676 
 677     MULTIRELEASE("--multi-release", "opt.arg.multi-release", "opt.multi-release", HIDDEN, FILEMANAGER),
 678 
 679     INHERIT_RUNTIME_ENVIRONMENT("--inherit-runtime-environment", "opt.inherit_runtime_environment",
 680             EXTENDED, BASIC) {
 681         @Override
 682         public void process(OptionHelper helper, String option) throws InvalidValueException {
 683             try {
 684                 Class.forName(JDK9Wrappers.VMHelper.VM_CLASSNAME);
 685                 String[] runtimeArgs = JDK9Wrappers.VMHelper.getRuntimeArguments();
 686                 for (String arg : runtimeArgs) {
 687                     // Handle any supported runtime options; ignore all others.
 688                     // The runtime arguments always use the single token form, e.g. "--name=value".
 689                     for (Option o : getSupportedRuntimeOptions()) {
 690                         if (o.matches(arg)) {
 691                             switch (o) {
 692                                 case ADD_MODULES:
 693                                     int eq = arg.indexOf('=');
 694                                     Assert.check(eq > 0, () -> ("invalid runtime option:" + arg));
 695                                     // --add-modules=ALL-DEFAULT is not supported at compile-time
 696                                     // so remove it from list, and only process the rest
 697                                     // if the set is non-empty.
 698                                     // Note that --add-modules=ALL-DEFAULT is automatically added
 699                                     // by the standard javac launcher.
 700                                     String mods = Arrays.stream(arg.substring(eq + 1).split(","))
 701                                             .filter(s -> !s.isEmpty() && !s.equals("ALL-DEFAULT"))
 702                                             .collect(Collectors.joining(","));
 703                                     if (!mods.isEmpty()) {
 704                                         String updatedArg = arg.substring(0, eq + 1) + mods;
 705                                         o.handleOption(helper, updatedArg, Collections.emptyIterator());
 706                                     }
 707                                     break;
 708                                 default:
 709                                     o.handleOption(helper, arg, Collections.emptyIterator());
 710                                     break;
 711                             }
 712                             break;
 713                         }
 714                     }
 715                 }
 716             } catch (ClassNotFoundException | SecurityException e) {
 717                 throw helper.newInvalidValueException("err.cannot.access.runtime.env");
 718             }
 719         }
 720 
 721         private Option[] getSupportedRuntimeOptions() {
 722             Option[] supportedRuntimeOptions = {
 723                 ADD_EXPORTS,
 724                 ADD_MODULES,
 725                 LIMIT_MODULES,
 726                 MODULE_PATH,
 727                 UPGRADE_MODULE_PATH,
 728                 PATCH_MODULE
 729             };
 730             return supportedRuntimeOptions;
 731         }
 732     };
 733 
 734     /**
 735      * This exception is thrown when an invalid value is given for an option.
 736      * The detail string gives a detailed, localized message, suitable for use
 737      * in error messages reported to the user.
 738      */
 739     public static class InvalidValueException extends Exception {
 740         private static final long serialVersionUID = -1;
 741 
 742         public InvalidValueException(String msg) {
 743             super(msg);
 744         }
 745 
 746         public InvalidValueException(String msg, Throwable cause) {
 747             super(msg, cause);
 748         }
 749     }
 750 
 751     /**
 752      * The kind of argument, if any, accepted by this option. The kind is augmented
 753      * by characters in the name of the option.
 754      */
 755     public enum ArgKind {
 756         /** This option does not take any argument. */
 757         NONE,
 758 
 759 // Not currently supported
 760 //        /**
 761 //         * This option takes an optional argument, which may be provided directly after an '='
 762 //         * separator, or in the following argument position if that word does not itself appear
 763 //         * to be the name of an option.
 764 //         */
 765 //        OPTIONAL,
 766 
 767         /**
 768          * This option takes an argument.
 769          * If the name of option ends with ':' or '=', the argument must be provided directly
 770          * after that separator.
 771          * Otherwise, it may appear after an '=' or in the following argument position.
 772          */
 773         REQUIRED,
 774 
 775         /**
 776          * This option takes an argument immediately after the option name, with no separator
 777          * character.
 778          */
 779         ADJACENT
 780     }
 781 
 782     /**
 783      * The kind of an Option. This is used by the -help and -X options.
 784      */
 785     public enum OptionKind {
 786         /** A standard option, documented by -help. */
 787         STANDARD,
 788         /** An extended option, documented by -X. */
 789         EXTENDED,
 790         /** A hidden option, not documented. */
 791         HIDDEN,
 792     }
 793 
 794     /**
 795      * The group for an Option. This determines the situations in which the
 796      * option is applicable.
 797      */
 798     enum OptionGroup {
 799         /** A basic option, available for use on the command line or via the
 800          *  Compiler API. */
 801         BASIC,
 802         /** An option for javac's standard JavaFileManager. Other file managers
 803          *  may or may not support these options. */
 804         FILEMANAGER,
 805         /** A command-line option that requests information, such as -help. */
 806         INFO,
 807         /** A command-line "option" representing a file or class name. */
 808         OPERAND
 809     }
 810 
 811     /**
 812      * The kind of choice for "choice" options.
 813      */
 814     enum ChoiceKind {
 815         /** The expected value is exactly one of the set of choices. */
 816         ONEOF,
 817         /** The expected value is one of more of the set of choices. */
 818         ANYOF
 819     }
 820 
 821     enum HiddenGroup {
 822         DIAGS("diags"),
 823         DEBUG("debug"),
 824         SHOULDSTOP("should-stop");
 825 
 826         static final Set<String> skipSet = new java.util.HashSet<>(
 827                 Arrays.asList("--diags:", "--debug:", "--should-stop:"));
 828 
 829         final String text;
 830 
 831         HiddenGroup(String text) {
 832             this.text = text;
 833         }
 834 
 835         public void process(OptionHelper helper, String option) throws InvalidValueException {
 836             String p = option.substring(option.indexOf(':') + 1).trim();
 837             String[] subOptions = p.split(";");
 838             for (String subOption : subOptions) {
 839                 subOption = text + "." + subOption.trim();
 840                 XD.process(helper, subOption, subOption);
 841             }
 842         }
 843 
 844         static boolean skip(String name) {
 845             return skipSet.contains(name);
 846         }
 847     }
 848 
 849     /**
 850      * The "primary name" for this option.
 851      * This is the name that is used to put values in the {@link Options} table.
 852      */
 853     public final String primaryName;
 854 
 855     /**
 856      * The set of names (primary name and aliases) for this option.
 857      * Note that some names may end in a separator, to indicate that an argument must immediately
 858      * follow the separator (and cannot appear in the following argument position.
 859      */
 860     public final String[] names;
 861 
 862     /** Documentation key for arguments. */
 863     protected final String argsNameKey;
 864 
 865     /** Documentation key for description.
 866      */
 867     protected final String descrKey;
 868 
 869     /** The kind of this option. */
 870     private final OptionKind kind;
 871 
 872     /** The group for this option. */
 873     private final OptionGroup group;
 874 
 875     /** The kind of argument for this option. */
 876     private final ArgKind argKind;
 877 
 878     /** The kind of choices for this option, if any. */
 879     private final ChoiceKind choiceKind;
 880 
 881     /** The choices for this option, if any. */
 882     private final Set<String> choices;
 883 
 884     /**
 885      * Looks up the first option matching the given argument in the full set of options.
 886      * @param arg the argument to be matches
 887      * @return the first option that matches, or null if none.
 888      */
 889     public static Option lookup(String arg) {
 890         return lookup(arg, EnumSet.allOf(Option.class));
 891     }
 892 
 893     /**
 894      * Looks up the first option matching the given argument within a set of options.
 895      * @param arg the argument to be matched
 896      * @param options the set of possible options
 897      * @return the first option that matches, or null if none.
 898      */
 899     public static Option lookup(String arg, Set<Option> options) {
 900         for (Option option: options) {
 901             if (option.matches(arg))
 902                 return option;
 903         }
 904         return null;
 905     }
 906 
 907     /**
 908      * Writes the "command line help" for given kind of option to the log.
 909      * @param log the log
 910      * @param kind  the kind of options to select
 911      */
 912     private static void showHelp(Log log, OptionKind kind) {
 913         Comparator<Option> comp = new Comparator<Option>() {
 914             final Collator collator = Collator.getInstance(Locale.US);
 915             { collator.setStrength(Collator.PRIMARY); }
 916 
 917             @Override
 918             public int compare(Option o1, Option o2) {
 919                 return collator.compare(o1.primaryName, o2.primaryName);
 920             }
 921         };
 922 
 923         getJavaCompilerOptions()
 924                 .stream()
 925                 .filter(o -> o.kind == kind)
 926                 .sorted(comp)
 927                 .forEach(o -> {
 928                     o.help(log);
 929                 });
 930     }
 931 
 932     Option(String text, String descrKey,
 933             OptionKind kind, OptionGroup group) {
 934         this(text, null, descrKey, kind, group, null, null, ArgKind.NONE);
 935     }
 936 
 937     Option(String text, String argsNameKey, String descrKey,
 938             OptionKind kind, OptionGroup group) {
 939         this(text, argsNameKey, descrKey, kind, group, null, null, ArgKind.REQUIRED);
 940     }
 941 
 942     Option(String text, String argsNameKey, String descrKey,
 943             OptionKind kind, OptionGroup group, ArgKind ak) {
 944         this(text, argsNameKey, descrKey, kind, group, null, null, ak);
 945     }
 946 
 947     Option(String text, String argsNameKey, String descrKey, OptionKind kind, OptionGroup group,
 948             ChoiceKind choiceKind, Set<String> choices) {
 949         this(text, argsNameKey, descrKey, kind, group, choiceKind, choices, ArgKind.REQUIRED);
 950     }
 951 
 952     Option(String text, String descrKey,
 953             OptionKind kind, OptionGroup group,
 954             ChoiceKind choiceKind, String... choices) {
 955         this(text, null, descrKey, kind, group, choiceKind,
 956                 new LinkedHashSet<>(Arrays.asList(choices)), ArgKind.REQUIRED);
 957     }
 958 
 959     private Option(String text, String argsNameKey, String descrKey,
 960             OptionKind kind, OptionGroup group,
 961             ChoiceKind choiceKind, Set<String> choices,
 962             ArgKind argKind) {
 963         this.names = text.trim().split("\\s+");
 964         Assert.check(names.length >= 1);
 965         this.primaryName = names[0];
 966         this.argsNameKey = argsNameKey;
 967         this.descrKey = descrKey;
 968         this.kind = kind;
 969         this.group = group;
 970         this.choiceKind = choiceKind;
 971         this.choices = choices;
 972         this.argKind = argKind;
 973     }
 974 
 975     public String getPrimaryName() {
 976         return primaryName;
 977     }
 978 
 979     public OptionKind getKind() {
 980         return kind;
 981     }
 982 
 983     public ArgKind getArgKind() {
 984         return argKind;
 985     }
 986 
 987     public boolean hasArg() {
 988         return (argKind != ArgKind.NONE);
 989     }
 990 
 991     public boolean matches(String option) {
 992         for (String name: names) {
 993             if (matches(option, name))
 994                 return true;
 995         }
 996         return false;
 997     }
 998 
 999     private boolean matches(String option, String name) {
1000         if (name.startsWith("--") && !HiddenGroup.skip(name)) {
1001             return option.equals(name)
1002                     || hasArg() && option.startsWith(name + "=");
1003         }
1004 
1005         boolean hasSuffix = (argKind == ArgKind.ADJACENT)
1006                 || name.endsWith(":") || name.endsWith("=");
1007 
1008         if (!hasSuffix)
1009             return option.equals(name);
1010 
1011         if (!option.startsWith(name))
1012             return false;
1013 
1014         if (choices != null) {
1015             String arg = option.substring(name.length());
1016             if (choiceKind == ChoiceKind.ONEOF)
1017                 return choices.contains(arg);
1018             else {
1019                 for (String a: arg.split(",+")) {
1020                     if (!choices.contains(a))
1021                         return false;
1022                 }
1023             }
1024         }
1025 
1026         return true;
1027     }
1028 
1029     /**
1030      * Handles an option.
1031      * If an argument for the option is required, depending on spec of the option, it will be found
1032      * as part of the current arg (following ':' or '=') or in the following argument.
1033      * This is the recommended way to handle an option directly, instead of calling the underlying
1034      * {@link #process process} methods.
1035      * @param helper a helper to provide access to the environment
1036      * @param arg the arg string that identified this option
1037      * @param rest the remaining strings to be analysed
1038      * @return true if the operation was successful, and false otherwise
1039      * @implNote The return value is the opposite of that used by {@link #process}.
1040      */
1041     public void handleOption(OptionHelper helper, String arg, Iterator<String> rest) throws InvalidValueException {
1042         if (hasArg()) {
1043             String option;
1044             String operand;
1045             int sep = findSeparator(arg);
1046             if (getArgKind() == Option.ArgKind.ADJACENT) {
1047                 option = primaryName; // aliases not supported
1048                 operand = arg.substring(primaryName.length());
1049             } else if (sep > 0) {
1050                 option = arg.substring(0, sep);
1051                 operand = arg.substring(sep + 1);
1052             } else {
1053                 if (!rest.hasNext()) {
1054                     throw helper.newInvalidValueException("err.req.arg", arg);
1055                 }
1056                 option = arg;
1057                 operand = rest.next();
1058             }
1059             process(helper, option, operand);
1060         } else {
1061             process(helper, arg);
1062         }
1063     }
1064 
1065     /**
1066      * Processes an option that either does not need an argument,
1067      * or which contains an argument within it, following a separator.
1068      * @param helper a helper to provide access to the environment
1069      * @param option the option to be processed
1070      * @throws InvalidValueException if an error occurred
1071      */
1072     public void process(OptionHelper helper, String option) throws InvalidValueException {
1073         if (argKind == ArgKind.NONE) {
1074             process(helper, primaryName, option);
1075         } else {
1076             int sep = findSeparator(option);
1077             process(helper, primaryName, option.substring(sep + 1));
1078         }
1079     }
1080 
1081     /**
1082      * Processes an option by updating the environment via a helper object.
1083      * @param helper a helper to provide access to the environment
1084      * @param option the option to be processed
1085      * @param arg the value to associate with the option, or a default value
1086      *  to be used if the option does not otherwise take an argument.
1087      */
1088     public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
1089         if (choices != null) {
1090             if (choiceKind == ChoiceKind.ONEOF) {
1091                 // some clients like to see just one of option+choice set
1092                 for (String s : choices)
1093                     helper.remove(primaryName + s);
1094                 String opt = primaryName + arg;
1095                 helper.put(opt, opt);
1096                 // some clients like to see option (without trailing ":")
1097                 // set to arg
1098                 String nm = primaryName.substring(0, primaryName.length() - 1);
1099                 helper.put(nm, arg);
1100             } else {
1101                 // set option+word for each word in arg
1102                 for (String a: arg.split(",+")) {
1103                     String opt = primaryName + a;
1104                     helper.put(opt, opt);
1105                 }
1106             }
1107         }
1108         helper.put(primaryName, arg);
1109         if (group == OptionGroup.FILEMANAGER)
1110             helper.handleFileManagerOption(this, arg);
1111     }
1112 
1113     /**
1114      * Returns a pattern to analyze the value for an option.
1115      * @return the pattern
1116      * @throws UnsupportedOperationException if an option does not provide a pattern.
1117      */
1118     public Pattern getPattern() {
1119         throw new UnsupportedOperationException();
1120     }
1121 
1122     /**
1123      * Scans a word to find the first separator character, either colon or equals.
1124      * @param word the word to be scanned
1125      * @return the position of the first':' or '=' character in the word,
1126      *  or -1 if none found
1127      */
1128     private static int findSeparator(String word) {
1129         for (int i = 0; i < word.length(); i++) {
1130             switch (word.charAt(i)) {
1131                 case ':': case '=':
1132                     return i;
1133             }
1134         }
1135         return -1;
1136     }
1137 
1138     /** The indent for the option synopsis. */
1139     private static final String SMALL_INDENT = "  ";
1140     /** The automatic indent for the description. */
1141     private static final String LARGE_INDENT = "        ";
1142     /** The space allowed for the synopsis, if the description is to be shown on the same line. */
1143     private static final int DEFAULT_SYNOPSIS_WIDTH = 28;
1144     /** The nominal maximum line length, when seeing if text will fit on a line. */
1145     private static final int DEFAULT_MAX_LINE_LENGTH = 80;
1146     /** The format for a single-line help entry. */
1147     private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
1148 
1149     /**
1150      * Writes help text for this option to the log.
1151      * @param log the log
1152      */
1153     protected void help(Log log) {
1154         help(log, log.localize(PrefixKind.JAVAC, descrKey));
1155     }
1156 
1157     protected void help(Log log, String descr) {
1158         String synopses = Arrays.stream(names)
1159                 .map(s -> helpSynopsis(s, log))
1160                 .collect(Collectors.joining(", "));
1161 
1162         // If option synopses and description fit on a single line of reasonable length,
1163         // display using COMPACT_FORMAT
1164         if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
1165                 && !descr.contains("\n")
1166                 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + descr.length() <= DEFAULT_MAX_LINE_LENGTH)) {
1167             log.printRawLines(WriterKind.STDOUT, String.format(COMPACT_FORMAT, synopses, descr));
1168             return;
1169         }
1170 
1171         // If option synopses fit on a single line of reasonable length, show that;
1172         // otherwise, show 1 per line
1173         if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
1174             log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + synopses);
1175         } else {
1176             for (String name: names) {
1177                 log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + helpSynopsis(name, log));
1178             }
1179         }
1180 
1181         // Finally, show the description
1182         log.printRawLines(WriterKind.STDOUT, LARGE_INDENT + descr.replace("\n", "\n" + LARGE_INDENT));
1183     }
1184 
1185     /**
1186      * Composes the initial synopsis of one of the forms for this option.
1187      * @param name the name of this form of the option
1188      * @param log the log used to localize the description of the arguments
1189      * @return  the synopsis
1190      */
1191     private String helpSynopsis(String name, Log log) {
1192         StringBuilder sb = new StringBuilder();
1193         sb.append(name);
1194         if (argsNameKey == null) {
1195             if (choices != null) {
1196                 String sep = "{";
1197                 for (String choice : choices) {
1198                     sb.append(sep);
1199                     sb.append(choices);
1200                     sep = ",";
1201                 }
1202                 sb.append("}");
1203             }
1204         } else {
1205             if (!name.matches(".*[=:]$") && argKind != ArgKind.ADJACENT)
1206                 sb.append(" ");
1207             sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
1208         }
1209 
1210         return sb.toString();
1211     }
1212 
1213     // For -XpkgInfo:value
1214     public enum PkgInfo {
1215         /**
1216          * Always generate package-info.class for every package-info.java file.
1217          * The file may be empty if there annotations with a RetentionPolicy
1218          * of CLASS or RUNTIME.  This option may be useful in conjunction with
1219          * build systems (such as Ant) that expect javac to generate at least
1220          * one .class file for every .java file.
1221          */
1222         ALWAYS,
1223         /**
1224          * Generate a package-info.class file if package-info.java contains
1225          * annotations. The file may be empty if all the annotations have
1226          * a RetentionPolicy of SOURCE.
1227          * This value is just for backwards compatibility with earlier behavior.
1228          * Either of the other two values are to be preferred to using this one.
1229          */
1230         LEGACY,
1231         /**
1232          * Generate a package-info.class file if and only if there are annotations
1233          * in package-info.java to be written into it.
1234          */
1235         NONEMPTY;
1236 
1237         public static PkgInfo get(Options options) {
1238             String v = options.get(XPKGINFO);
1239             return (v == null
1240                     ? PkgInfo.LEGACY
1241                     : PkgInfo.valueOf(StringUtils.toUpperCase(v)));
1242         }
1243     }
1244 
1245     private static Set<String> getXLintChoices() {
1246         Set<String> choices = new LinkedHashSet<>();
1247         choices.add("all");
1248         for (Lint.LintCategory c : Lint.LintCategory.values()) {
1249             choices.add(c.option);
1250             choices.add("-" + c.option);
1251         }
1252         choices.add("none");
1253         return choices;
1254     }
1255 
1256     /**
1257      * Returns the set of options supported by the command line tool.
1258      * @return the set of options.
1259      */
1260     static Set<Option> getJavaCompilerOptions() {
1261         return EnumSet.allOf(Option.class);
1262     }
1263 
1264     /**
1265      * Returns the set of options supported by the built-in file manager.
1266      * @return the set of options.
1267      */
1268     public static Set<Option> getJavacFileManagerOptions() {
1269         return getOptions(FILEMANAGER);
1270     }
1271 
1272     /**
1273      * Returns the set of options supported by this implementation of
1274      * the JavaCompiler API, via {@link JavaCompiler#getTask}.
1275      * @return the set of options.
1276      */
1277     public static Set<Option> getJavacToolOptions() {
1278         return getOptions(BASIC);
1279     }
1280 
1281     private static Set<Option> getOptions(OptionGroup group) {
1282         return Arrays.stream(Option.values())
1283                 .filter(o -> o.group == group)
1284                 .collect(Collectors.toCollection(() -> EnumSet.noneOf(Option.class)));
1285     }
1286 
1287 }