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