1 /* 2 * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.main; 27 28 import java.io.IOException; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.util.Arrays; 33 import java.util.Collection; 34 import java.util.Collections; 35 import java.util.EnumSet; 36 import java.util.HashSet; 37 import java.util.Iterator; 38 import java.util.LinkedHashMap; 39 import java.util.LinkedHashSet; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 import java.util.stream.Stream; 45 46 import javax.tools.JavaFileManager; 47 import javax.tools.JavaFileManager.Location; 48 import javax.tools.JavaFileObject; 49 import javax.tools.JavaFileObject.Kind; 50 import javax.tools.StandardJavaFileManager; 51 import javax.tools.StandardLocation; 52 53 import com.sun.tools.doclint.DocLint; 54 import com.sun.tools.javac.code.Lint.LintCategory; 55 import com.sun.tools.javac.code.Source; 56 import com.sun.tools.javac.file.BaseFileManager; 57 import com.sun.tools.javac.file.JavacFileManager; 58 import com.sun.tools.javac.jvm.Profile; 59 import com.sun.tools.javac.jvm.Target; 60 import com.sun.tools.javac.main.OptionHelper.GrumpyHelper; 61 import com.sun.tools.javac.platform.PlatformDescription; 62 import com.sun.tools.javac.platform.PlatformUtils; 63 import com.sun.tools.javac.resources.CompilerProperties.Errors; 64 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 65 import com.sun.tools.javac.util.Context; 66 import com.sun.tools.javac.util.JCDiagnostic; 67 import com.sun.tools.javac.util.List; 68 import com.sun.tools.javac.util.ListBuffer; 69 import com.sun.tools.javac.util.Log; 70 import com.sun.tools.javac.util.Log.PrefixKind; 71 import com.sun.tools.javac.util.Log.WriterKind; 72 import com.sun.tools.javac.util.Options; 73 import com.sun.tools.javac.util.PropagatedException; 74 75 /** 76 * Shared option and argument handling for command line and API usage of javac. 77 */ 78 public class Arguments { 79 80 /** 81 * The context key for the arguments. 82 */ 83 public static final Context.Key<Arguments> argsKey = new Context.Key<>(); 84 85 private String ownName; 86 private Set<String> classNames; 87 private Set<Path> files; 88 private Map<Option, String> deferredFileManagerOptions; 89 private Set<JavaFileObject> fileObjects; 90 private boolean emptyAllowed; 91 private final Options options; 92 93 private JavaFileManager fileManager; 94 private final Log log; 95 private final Context context; 96 97 private enum ErrorMode { ILLEGAL_ARGUMENT, ILLEGAL_STATE, LOG }; 98 private ErrorMode errorMode; 99 private boolean errors; 100 101 /** 102 * Gets the Arguments instance for this context. 103 * 104 * @param context the content 105 * @return the Arguments instance for this context. 106 */ 107 public static Arguments instance(Context context) { 108 Arguments instance = context.get(argsKey); 109 if (instance == null) { 110 instance = new Arguments(context); 111 } 112 return instance; 113 } 114 115 protected Arguments(Context context) { 116 context.put(argsKey, this); 117 options = Options.instance(context); 118 log = Log.instance(context); 119 this.context = context; 120 121 // Ideally, we could init this here and update/configure it as 122 // needed, but right now, initializing a file manager triggers 123 // initialization of other items in the context, such as Lint 124 // and FSInfo, which should not be initialized until after 125 // processArgs 126 // fileManager = context.get(JavaFileManager.class); 127 } 128 129 private final OptionHelper cmdLineHelper = new OptionHelper() { 130 @Override 131 public String get(Option option) { 132 return options.get(option); 133 } 134 135 @Override 136 public void put(String name, String value) { 137 options.put(name, value); 138 } 139 140 @Override 141 public void remove(String name) { 142 options.remove(name); 143 } 144 145 @Override 146 public boolean handleFileManagerOption(Option option, String value) { 147 options.put(option, value); 148 deferredFileManagerOptions.put(option, value); 149 return true; 150 } 151 152 @Override 153 public Log getLog() { 154 return log; 155 } 156 157 @Override 158 public String getOwnName() { 159 return ownName; 160 } 161 162 @Override 163 public void error(String key, Object... args) { 164 Arguments.this.error(key, args); 165 } 166 167 @Override 168 public void error(JCDiagnostic.Error error) { 169 Arguments.this.error(error); 170 } 171 172 @Override 173 public void addFile(Path p) { 174 files.add(p); 175 } 176 177 @Override 178 public void addClassName(String s) { 179 classNames.add(s); 180 } 181 182 }; 183 184 /** 185 * Initializes this Args instance with a set of command line args. 186 * The args will be processed in conjunction with the full set of 187 * command line options, including -help, -version etc. 188 * The args may also contain class names and filenames. 189 * Any errors during this call, and later during validate, will be reported 190 * to the log. 191 * @param ownName the name of this tool; used to prefix messages 192 * @param args the args to be processed 193 */ 194 public void init(String ownName, String... args) { 195 this.ownName = ownName; 196 errorMode = ErrorMode.LOG; 197 files = new LinkedHashSet<>(); 198 deferredFileManagerOptions = new LinkedHashMap<>(); 199 fileObjects = null; 200 classNames = new LinkedHashSet<>(); 201 processArgs(List.from(args), Option.getJavaCompilerOptions(), cmdLineHelper, true, false); 202 if (errors) { 203 log.printLines(PrefixKind.JAVAC, "msg.usage", ownName); 204 } 205 } 206 207 private final OptionHelper apiHelper = new GrumpyHelper(null) { 208 @Override 209 public String get(Option option) { 210 return options.get(option); 211 } 212 213 @Override 214 public void put(String name, String value) { 215 options.put(name, value); 216 } 217 218 @Override 219 public void remove(String name) { 220 options.remove(name); 221 } 222 223 @Override 224 public void error(String key, Object... args) { 225 Arguments.this.error(key, args); 226 } 227 228 @Override 229 public Log getLog() { 230 return Arguments.this.log; 231 } 232 }; 233 234 /** 235 * Initializes this Args instance with the parameters for a JavacTask. 236 * The options will be processed in conjunction with the restricted set 237 * of tool options, which does not include -help, -version, etc, 238 * nor does it include classes and filenames, which should be specified 239 * separately. 240 * File manager options are handled directly by the file manager. 241 * Any errors found while processing individual args will be reported 242 * via IllegalArgumentException. 243 * Any subsequent errors during validate will be reported via IllegalStateException. 244 * @param ownName the name of this tool; used to prefix messages 245 * @param options the options to be processed 246 * @param classNames the classes to be subject to annotation processing 247 * @param files the files to be compiled 248 */ 249 public void init(String ownName, 250 Iterable<String> options, 251 Iterable<String> classNames, 252 Iterable<? extends JavaFileObject> files) { 253 this.ownName = ownName; 254 this.classNames = toSet(classNames); 255 this.fileObjects = toSet(files); 256 this.files = null; 257 errorMode = ErrorMode.ILLEGAL_ARGUMENT; 258 if (options != null) { 259 processArgs(toList(options), Option.getJavacToolOptions(), apiHelper, false, true); 260 } 261 errorMode = ErrorMode.ILLEGAL_STATE; 262 } 263 264 /** 265 * Gets the files to be compiled. 266 * @return the files to be compiled 267 */ 268 public Set<JavaFileObject> getFileObjects() { 269 if (fileObjects == null) { 270 fileObjects = new LinkedHashSet<>(); 271 } 272 if (files != null) { 273 JavacFileManager jfm = (JavacFileManager) getFileManager(); 274 for (JavaFileObject fo: jfm.getJavaFileObjectsFromPaths(files)) 275 fileObjects.add(fo); 276 } 277 return fileObjects; 278 } 279 280 /** 281 * Gets the classes to be subject to annotation processing. 282 * @return the classes to be subject to annotation processing 283 */ 284 public Set<String> getClassNames() { 285 return classNames; 286 } 287 288 /** 289 * Processes strings containing options and operands. 290 * @param args the strings to be processed 291 * @param allowableOpts the set of option declarations that are applicable 292 * @param helper a help for use by Option.process 293 * @param allowOperands whether or not to check for files and classes 294 * @param checkFileManager whether or not to check if the file manager can handle 295 * options which are not recognized by any of allowableOpts 296 * @return true if all the strings were successfully processed; false otherwise 297 * @throws IllegalArgumentException if a problem occurs and errorMode is set to 298 * ILLEGAL_ARGUMENT 299 */ 300 private boolean processArgs(Iterable<String> args, 301 Set<Option> allowableOpts, OptionHelper helper, 302 boolean allowOperands, boolean checkFileManager) { 303 if (!doProcessArgs(args, allowableOpts, helper, allowOperands, checkFileManager)) 304 return false; 305 306 String platformString = options.get(Option.RELEASE); 307 308 checkOptionAllowed(platformString == null, 309 option -> error("err.release.bootclasspath.conflict", option.getPrimaryName()), 310 Option.BOOT_CLASS_PATH, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND, 311 Option.XBOOTCLASSPATH_PREPEND, 312 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 313 Option.EXTDIRS, Option.DJAVA_EXT_DIRS, 314 Option.SOURCE, Option.TARGET); 315 316 if (platformString != null) { 317 PlatformDescription platformDescription = PlatformUtils.lookupPlatformDescription(platformString); 318 319 if (platformDescription == null) { 320 error("err.unsupported.release.version", platformString); 321 return false; 322 } 323 324 options.put(Option.SOURCE, platformDescription.getSourceVersion()); 325 options.put(Option.TARGET, platformDescription.getTargetVersion()); 326 327 context.put(PlatformDescription.class, platformDescription); 328 329 if (!doProcessArgs(platformDescription.getAdditionalOptions(), allowableOpts, helper, allowOperands, checkFileManager)) 330 return false; 331 332 Collection<Path> platformCP = platformDescription.getPlatformPath(); 333 334 if (platformCP != null) { 335 JavaFileManager fm = getFileManager(); 336 337 if (!(fm instanceof StandardJavaFileManager)) { 338 error("err.release.not.standard.file.manager"); 339 return false; 340 } 341 342 try { 343 StandardJavaFileManager sfm = (StandardJavaFileManager) fm; 344 345 sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP); 346 } catch (IOException ex) { 347 log.printLines(PrefixKind.JAVAC, "msg.io"); 348 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 349 return false; 350 } 351 } 352 } 353 354 options.notifyListeners(); 355 356 return true; 357 } 358 359 private boolean doProcessArgs(Iterable<String> args, 360 Set<Option> allowableOpts, OptionHelper helper, 361 boolean allowOperands, boolean checkFileManager) { 362 JavaFileManager fm = checkFileManager ? getFileManager() : null; 363 Iterator<String> argIter = args.iterator(); 364 while (argIter.hasNext()) { 365 String arg = argIter.next(); 366 if (arg.isEmpty()) { 367 error("err.invalid.flag", arg); 368 return false; 369 } 370 371 Option option = null; 372 373 // first, check the provided set of javac options 374 if (arg.startsWith("-")) { 375 option = Option.lookup(arg, allowableOpts); 376 } else if (allowOperands && Option.SOURCEFILE.matches(arg)) { 377 option = Option.SOURCEFILE; 378 } 379 380 if (option != null) { 381 if (!option.handleOption(helper, arg, argIter)) { 382 return false; 383 } 384 continue; 385 } 386 387 // check file manager option 388 if (fm != null && fm.handleOption(arg, argIter)) { 389 continue; 390 } 391 392 // none of the above 393 error("err.invalid.flag", arg); 394 return false; 395 } 396 397 return true; 398 } 399 400 /** 401 * Validates the overall consistency of the options and operands 402 * processed by processOptions. 403 * @return true if all args are successfully validating; false otherwise. 404 * @throws IllegalStateException if a problem is found and errorMode is set to 405 * ILLEGAL_STATE 406 */ 407 public boolean validate() { 408 JavaFileManager fm = getFileManager(); 409 if (options.isSet(Option.MODULE)) { 410 if (!fm.hasLocation(StandardLocation.CLASS_OUTPUT)) { 411 log.error(Errors.OutputDirMustBeSpecifiedWithDashMOption); 412 } else if (!fm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) { 413 log.error(Errors.ModulesourcepathMustBeSpecifiedWithDashMOption); 414 } else { 415 java.util.List<String> modules = Arrays.asList(options.get(Option.MODULE).split(",")); 416 try { 417 for (String module : modules) { 418 Location sourceLoc = fm.getModuleLocation(StandardLocation.MODULE_SOURCE_PATH, module); 419 if (sourceLoc == null) { 420 log.error(Errors.ModuleNotFoundInModuleSourcePath(module)); 421 } else { 422 Location classLoc = fm.getModuleLocation(StandardLocation.CLASS_OUTPUT, module); 423 424 for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(JavaFileObject.Kind.SOURCE), true)) { 425 String className = fm.inferBinaryName(sourceLoc, file); 426 JavaFileObject classFile = fm.getJavaFileForInput(classLoc, className, Kind.CLASS); 427 428 if (classFile == null || classFile.getLastModified() < file.getLastModified()) { 429 if (fileObjects == null) 430 fileObjects = new HashSet<>(); 431 fileObjects.add(file); 432 } 433 } 434 } 435 } 436 } catch (IOException ex) { 437 log.printLines(PrefixKind.JAVAC, "msg.io"); 438 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 439 return false; 440 } 441 } 442 } 443 444 if (isEmpty()) { 445 // It is allowed to compile nothing if just asking for help or version info. 446 // But also note that none of these options are supported in API mode. 447 if (options.isSet(Option.HELP) 448 || options.isSet(Option.X) 449 || options.isSet(Option.VERSION) 450 || options.isSet(Option.FULLVERSION) 451 || options.isSet(Option.MODULE)) 452 return true; 453 454 if (emptyAllowed) 455 return true; 456 457 if (!errors) { 458 if (JavaCompiler.explicitAnnotationProcessingRequested(options)) { 459 error("err.no.source.files.classes"); 460 } else { 461 error("err.no.source.files"); 462 } 463 } 464 465 return false; 466 } 467 468 if (!checkDirectory(Option.D)) { 469 return false; 470 } 471 if (!checkDirectory(Option.S)) { 472 return false; 473 } 474 if (!checkDirectory(Option.H)) { 475 return false; 476 } 477 478 // The following checks are to help avoid accidental confusion between 479 // directories of modules and exploded module directories. 480 if (fm instanceof StandardJavaFileManager) { 481 StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager; 482 if (sfm.hasLocation(StandardLocation.CLASS_OUTPUT)) { 483 Path outDir = sfm.getLocationAsPaths(StandardLocation.CLASS_OUTPUT).iterator().next(); 484 if (sfm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) { 485 // multi-module mode 486 if (Files.exists(outDir.resolve("module-info.class"))) { 487 log.error(Errors.MultiModuleOutdirCannotBeExplodedModule(outDir)); 488 } 489 } else { 490 // single-module or legacy mode 491 boolean lintPaths = options.isUnset(Option.XLINT_CUSTOM, 492 "-" + LintCategory.PATH.option); 493 if (lintPaths) { 494 Path outDirParent = outDir.getParent(); 495 if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { 496 log.warning(LintCategory.PATH, Warnings.OutdirIsInExplodedModule(outDir)); 497 } 498 } 499 } 500 } 501 } 502 503 504 String sourceString = options.get(Option.SOURCE); 505 Source source = (sourceString != null) 506 ? Source.lookup(sourceString) 507 : Source.DEFAULT; 508 String targetString = options.get(Option.TARGET); 509 Target target = (targetString != null) 510 ? Target.lookup(targetString) 511 : Target.DEFAULT; 512 513 // We don't check source/target consistency for CLDC, as J2ME 514 // profiles are not aligned with J2SE targets; moreover, a 515 // single CLDC target may have many profiles. In addition, 516 // this is needed for the continued functioning of the JSR14 517 // prototype. 518 if (Character.isDigit(target.name.charAt(0))) { 519 if (target.compareTo(source.requiredTarget()) < 0) { 520 if (targetString != null) { 521 if (sourceString == null) { 522 error("warn.target.default.source.conflict", 523 targetString, 524 source.requiredTarget().name); 525 } else { 526 error("warn.source.target.conflict", 527 sourceString, 528 source.requiredTarget().name); 529 } 530 return false; 531 } else { 532 target = source.requiredTarget(); 533 options.put("-target", target.name); 534 } 535 } 536 } 537 538 String profileString = options.get(Option.PROFILE); 539 if (profileString != null) { 540 Profile profile = Profile.lookup(profileString); 541 if (!profile.isValid(target)) { 542 error("warn.profile.target.conflict", profileString, target.name); 543 } 544 545 // This check is only effective in command line mode, 546 // where the file manager options are added to options 547 if (options.get(Option.BOOT_CLASS_PATH) != null) { 548 error("err.profile.bootclasspath.conflict"); 549 } 550 } 551 552 if (options.isSet(Option.SOURCE_PATH) && options.isSet(Option.MODULE_SOURCE_PATH)) { 553 error("err.sourcepath.modulesourcepath.conflict"); 554 } 555 556 boolean lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); 557 558 if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { 559 if (fm instanceof BaseFileManager) { 560 if (((BaseFileManager) fm).isDefaultBootClassPath()) 561 log.warning(LintCategory.OPTIONS, "source.no.bootclasspath", source.name); 562 } 563 } 564 565 boolean obsoleteOptionFound = false; 566 567 if (source.compareTo(Source.MIN) < 0) { 568 log.error(Errors.OptionRemovedSource(source.name, Source.MIN.name)); 569 } else if (source == Source.MIN && lintOptions) { 570 log.warning(LintCategory.OPTIONS, Warnings.OptionObsoleteSource(source.name)); 571 obsoleteOptionFound = true; 572 } 573 574 if (target.compareTo(Target.MIN) < 0) { 575 log.error(Errors.OptionRemovedTarget(target.name, Target.MIN.name)); 576 } else if (target == Target.MIN && lintOptions) { 577 log.warning(LintCategory.OPTIONS, Warnings.OptionObsoleteTarget(target.name)); 578 obsoleteOptionFound = true; 579 } 580 581 final Target t = target; 582 checkOptionAllowed(t.compareTo(Target.JDK1_8) <= 0, 583 option -> error("err.option.not.allowed.with.target", option.getPrimaryName(), t.name), 584 Option.BOOT_CLASS_PATH, 585 Option.XBOOTCLASSPATH_PREPEND, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND, 586 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 587 Option.EXTDIRS, Option.DJAVA_EXT_DIRS); 588 589 checkOptionAllowed(t.compareTo(Target.JDK1_9) >= 0, 590 option -> error("err.option.not.allowed.with.target", option.getPrimaryName(), t.name), 591 Option.MODULE_SOURCE_PATH, Option.UPGRADE_MODULE_PATH, 592 Option.SYSTEM, Option.MODULE_PATH, Option.ADD_MODULES, Option.LIMIT_MODULES, 593 Option.PATCH_MODULE); 594 595 if (fm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) { 596 if (!options.isSet(Option.PROC, "only") 597 && !fm.hasLocation(StandardLocation.CLASS_OUTPUT)) { 598 log.error(Errors.NoOutputDir); 599 } 600 if (options.isSet(Option.XMODULE)) { 601 log.error(Errors.XmoduleNoModuleSourcepath); 602 } 603 } 604 605 if (fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH) && 606 fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH)) { 607 log.error(Errors.ProcessorpathNoProcessormodulepath); 608 } 609 610 if (obsoleteOptionFound) 611 log.warning(LintCategory.OPTIONS, "option.obsolete.suppression"); 612 613 String addExports = options.get(Option.ADD_EXPORTS); 614 if (addExports != null) { 615 // Each entry must be of the form module/package=target-list where target-list is a 616 // comma-separated list of module or ALL-UNNAMED. 617 // All module/package pairs must be unique. 618 Pattern p = Pattern.compile("([^/]+)/([^=]+)=(.*)"); 619 Map<String,List<String>> map = new LinkedHashMap<>(); 620 for (String e: addExports.split("\0")) { 621 Matcher m = p.matcher(e); 622 if (!m.matches()) { 623 log.error(Errors.XaddexportsMalformedEntry(e)); 624 continue; 625 } 626 String eModule = m.group(1); // TODO: check a valid dotted identifier 627 String ePackage = m.group(2); // TODO: check a valid dotted identifier 628 String eTargets = m.group(3); // TODO: check a valid list of dotted identifier or ALL-UNNAMED 629 String eModPkg = eModule + '/' + ePackage; 630 List<String> l = map.get(eModPkg); 631 map.put(eModPkg, (l == null) ? List.of(eTargets) : l.prepend(eTargets)); 632 } 633 map.forEach((key, value) -> { 634 if (value.size() > 1) { 635 log.error(Errors.XaddexportsTooMany(key)); 636 // TODO: consider adding diag fragments for the entries 637 } 638 }); 639 } 640 641 String addReads = options.get(Option.ADD_READS); 642 if (addReads != null) { 643 // Each entry must be of the form module=source-list where source-list is a 644 // comma separated list of module or ALL-UNNAMED. 645 // All target modules (i.e. on left of '=') must be unique. 646 Pattern p = Pattern.compile("([^=]+)=(.*)"); 647 Map<String,List<String>> map = new LinkedHashMap<>(); 648 for (String e: addReads.split("\0")) { 649 Matcher m = p.matcher(e); 650 if (!m.matches()) { 651 log.error(Errors.XaddreadsMalformedEntry(e)); 652 continue; 653 } 654 String eModule = m.group(1); // TODO: check a valid dotted identifier 655 String eSources = m.group(2); // TODO: check a valid list of dotted identifier or ALL-UNNAMED 656 List<String> l = map.get(eModule); 657 map.put(eModule, (l == null) ? List.of(eSources) : l.prepend(eSources)); 658 } 659 map.forEach((key, value) -> { 660 if (value.size() > 1) { 661 log.error(Errors.XaddreadsTooMany(key)); 662 // TODO: consider adding diag fragments for the entries 663 } 664 }); 665 } 666 667 668 return !errors; 669 } 670 671 /** 672 * Returns true if there are no files or classes specified for use. 673 * @return true if there are no files or classes specified for use 674 */ 675 public boolean isEmpty() { 676 return ((files == null) || files.isEmpty()) 677 && ((fileObjects == null) || fileObjects.isEmpty()) 678 && classNames.isEmpty(); 679 } 680 681 public void allowEmpty() { 682 this.emptyAllowed = true; 683 } 684 685 /** 686 * Gets the file manager options which may have been deferred 687 * during processArgs. 688 * @return the deferred file manager options 689 */ 690 public Map<Option, String> getDeferredFileManagerOptions() { 691 return deferredFileManagerOptions; 692 } 693 694 /** 695 * Gets any options specifying plugins to be run. 696 * @return options for plugins 697 */ 698 public Set<List<String>> getPluginOpts() { 699 String plugins = options.get(Option.PLUGIN); 700 if (plugins == null) 701 return Collections.emptySet(); 702 703 Set<List<String>> pluginOpts = new LinkedHashSet<>(); 704 for (String plugin: plugins.split("\\x00")) { 705 pluginOpts.add(List.from(plugin.split("\\s+"))); 706 } 707 return Collections.unmodifiableSet(pluginOpts); 708 } 709 710 /** 711 * Gets any options specifying how doclint should be run. 712 * An empty list is returned if no doclint options are specified 713 * or if the only doclint option is -Xdoclint:none. 714 * @return options for doclint 715 */ 716 public List<String> getDocLintOpts() { 717 String xdoclint = options.get(Option.XDOCLINT); 718 String xdoclintCustom = options.get(Option.XDOCLINT_CUSTOM); 719 if (xdoclint == null && xdoclintCustom == null) 720 return List.nil(); 721 722 Set<String> doclintOpts = new LinkedHashSet<>(); 723 if (xdoclint != null) 724 doclintOpts.add(DocLint.XMSGS_OPTION); 725 if (xdoclintCustom != null) { 726 for (String s: xdoclintCustom.split("\\s+")) { 727 if (s.isEmpty()) 728 continue; 729 doclintOpts.add(DocLint.XMSGS_CUSTOM_PREFIX + s); 730 } 731 } 732 733 if (doclintOpts.equals(Collections.singleton(DocLint.XMSGS_CUSTOM_PREFIX + "none"))) 734 return List.nil(); 735 736 String checkPackages = options.get(Option.XDOCLINT_PACKAGE); 737 738 if (checkPackages != null) { 739 for (String s : checkPackages.split("\\s+")) { 740 doclintOpts.add(DocLint.XCHECK_PACKAGE + s); 741 } 742 } 743 744 // standard doclet normally generates H1, H2, 745 // so for now, allow user comments to assume that 746 doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2"); 747 return List.from(doclintOpts.toArray(new String[doclintOpts.size()])); 748 } 749 750 private boolean checkDirectory(Option option) { 751 String value = options.get(option); 752 if (value == null) { 753 return true; 754 } 755 Path file = Paths.get(value); 756 if (Files.exists(file) && !Files.isDirectory(file)) { 757 error("err.file.not.directory", value); 758 return false; 759 } 760 return true; 761 } 762 763 private interface ErrorReporter { 764 void report(Option o); 765 } 766 767 void checkOptionAllowed(boolean allowed, ErrorReporter r, Option... opts) { 768 if (!allowed) { 769 Stream.of(opts) 770 .filter(options :: isSet) 771 .forEach(r :: report); 772 } 773 } 774 775 void error(JCDiagnostic.Error error) { 776 errors = true; 777 switch (errorMode) { 778 case ILLEGAL_ARGUMENT: { 779 String msg = log.localize(error); 780 throw new PropagatedException(new IllegalArgumentException(msg)); 781 } 782 case ILLEGAL_STATE: { 783 String msg = log.localize(error); 784 throw new PropagatedException(new IllegalStateException(msg)); 785 } 786 case LOG: 787 report(error); 788 } 789 } 790 791 void error(String key, Object... args) { 792 errors = true; 793 switch (errorMode) { 794 case ILLEGAL_ARGUMENT: { 795 String msg = log.localize(PrefixKind.JAVAC, key, args); 796 throw new PropagatedException(new IllegalArgumentException(msg)); 797 } 798 case ILLEGAL_STATE: { 799 String msg = log.localize(PrefixKind.JAVAC, key, args); 800 throw new PropagatedException(new IllegalStateException(msg)); 801 } 802 case LOG: 803 report(key, args); 804 } 805 } 806 807 void warning(String key, Object... args) { 808 report(key, args); 809 } 810 811 private void report(String key, Object... args) { 812 // Would be good to have support for -XDrawDiagnostics here 813 log.printRawLines(ownName + ": " + log.localize(PrefixKind.JAVAC, key, args)); 814 } 815 816 private void report(JCDiagnostic.Error error) { 817 // Would be good to have support for -XDrawDiagnostics here 818 log.printRawLines(ownName + ": " + log.localize(error)); 819 } 820 821 private JavaFileManager getFileManager() { 822 if (fileManager == null) 823 fileManager = context.get(JavaFileManager.class); 824 return fileManager; 825 } 826 827 <T> ListBuffer<T> toList(Iterable<? extends T> items) { 828 ListBuffer<T> list = new ListBuffer<>(); 829 if (items != null) { 830 for (T item : items) { 831 list.add(item); 832 } 833 } 834 return list; 835 } 836 837 <T> Set<T> toSet(Iterable<? extends T> items) { 838 Set<T> set = new LinkedHashSet<>(); 839 if (items != null) { 840 for (T item : items) { 841 set.add(item); 842 } 843 } 844 return set; 845 } 846 }