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