< prev index next >

langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java

Print this page




  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.List;
  67 import com.sun.tools.javac.util.ListBuffer;
  68 import com.sun.tools.javac.util.Log;
  69 import com.sun.tools.javac.util.Log.PrefixKind;
  70 import com.sun.tools.javac.util.Log.WriterKind;
  71 import com.sun.tools.javac.util.Options;
  72 import com.sun.tools.javac.util.PropagatedException;
  73 
  74 /**
  75  * Shared option and argument handling for command line and API usage of javac.
  76  */
  77 public class Arguments {
  78 
  79     /**
  80      * The context key for the arguments.
  81      */
  82     public static final Context.Key<Arguments> argsKey = new Context.Key<>();
  83 
  84     private String ownName;
  85     private Set<String> classNames;


 126     }
 127 
 128     private final OptionHelper cmdLineHelper = new OptionHelper() {
 129         @Override
 130         public String get(Option option) {
 131             return options.get(option);
 132         }
 133 
 134         @Override
 135         public void put(String name, String value) {
 136             options.put(name, value);
 137         }
 138 
 139         @Override
 140         public void remove(String name) {
 141             options.remove(name);
 142         }
 143 
 144         @Override
 145         public boolean handleFileManagerOption(Option option, String value) {
 146             options.put(option.getText(), value);
 147             deferredFileManagerOptions.put(option, value);
 148             return true;
 149         }
 150 
 151         @Override
 152         public Log getLog() {
 153             return log;
 154         }
 155 
 156         @Override
 157         public String getOwnName() {
 158             return ownName;
 159         }
 160 
 161         @Override
 162         public void error(String key, Object... args) {
 163             Arguments.this.error(key, args);
 164         }
 165 
 166         @Override





 167         public void addFile(Path p) {
 168             files.add(p);
 169         }
 170 
 171         @Override
 172         public void addClassName(String s) {
 173             classNames.add(s);
 174         }
 175 
 176     };
 177 
 178     /**
 179      * Initializes this Args instance with a set of command line args.
 180      * The args will be processed in conjunction with the full set of
 181      * command line options, including -help, -version etc.
 182      * The args may also contain class names and filenames.
 183      * Any errors during this call, and later during validate, will be reported
 184      * to the log.
 185      * @param ownName the name of this tool; used to prefix messages
 186      * @param args the args to be processed
 187      */
 188     public void init(String ownName, String... args) {
 189         this.ownName = ownName;
 190         errorMode = ErrorMode.LOG;
 191         files = new LinkedHashSet<>();
 192         deferredFileManagerOptions = new LinkedHashMap<>();
 193         fileObjects = null;
 194         classNames = new LinkedHashSet<>();
 195         processArgs(List.from(args), Option.getJavaCompilerOptions(), cmdLineHelper, true, false);



 196     }
 197 
 198     private final OptionHelper apiHelper = new GrumpyHelper(null) {
 199         @Override
 200         public String get(Option option) {
 201             return options.get(option.getText());
 202         }
 203 
 204         @Override
 205         public void put(String name, String value) {
 206             options.put(name, value);
 207         }
 208 
 209         @Override
 210         public void remove(String name) {
 211             options.remove(name);
 212         }
 213 
 214         @Override
 215         public void error(String key, Object... args) {
 216             Arguments.this.error(key, args);
 217         }
 218 
 219         @Override
 220         public Log getLog() {
 221             return Arguments.this.log;


 280      * Processes strings containing options and operands.
 281      * @param args the strings to be processed
 282      * @param allowableOpts the set of option declarations that are applicable
 283      * @param helper a help for use by Option.process
 284      * @param allowOperands whether or not to check for files and classes
 285      * @param checkFileManager whether or not to check if the file manager can handle
 286      *      options which are not recognized by any of allowableOpts
 287      * @return true if all the strings were successfully processed; false otherwise
 288      * @throws IllegalArgumentException if a problem occurs and errorMode is set to
 289      *      ILLEGAL_ARGUMENT
 290      */
 291     private boolean processArgs(Iterable<String> args,
 292             Set<Option> allowableOpts, OptionHelper helper,
 293             boolean allowOperands, boolean checkFileManager) {
 294         if (!doProcessArgs(args, allowableOpts, helper, allowOperands, checkFileManager))
 295             return false;
 296 
 297         String platformString = options.get(Option.RELEASE);
 298 
 299         checkOptionAllowed(platformString == null,
 300                 option -> error("err.release.bootclasspath.conflict", option.getText()),
 301                 Option.BOOTCLASSPATH, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND,
 302                 Option.XBOOTCLASSPATH_PREPEND,
 303                 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS,
 304                 Option.EXTDIRS, Option.DJAVA_EXT_DIRS,
 305                 Option.SOURCE, Option.TARGET);
 306 
 307         if (platformString != null) {
 308             PlatformDescription platformDescription = PlatformUtils.lookupPlatformDescription(platformString);
 309 
 310             if (platformDescription == null) {
 311                 error("err.unsupported.release.version", platformString);
 312                 return false;
 313             }
 314 
 315             options.put(Option.SOURCE, platformDescription.getSourceVersion());
 316             options.put(Option.TARGET, platformDescription.getTargetVersion());
 317 
 318             context.put(PlatformDescription.class, platformDescription);
 319 
 320             if (!doProcessArgs(platformDescription.getAdditionalOptions(), allowableOpts, helper, allowOperands, checkFileManager))
 321                 return false;


 343         }
 344 
 345         options.notifyListeners();
 346 
 347         return true;
 348     }
 349 
 350     private boolean doProcessArgs(Iterable<String> args,
 351             Set<Option> allowableOpts, OptionHelper helper,
 352             boolean allowOperands, boolean checkFileManager) {
 353         JavaFileManager fm = checkFileManager ? getFileManager() : null;
 354         Iterator<String> argIter = args.iterator();
 355         while (argIter.hasNext()) {
 356             String arg = argIter.next();
 357             if (arg.isEmpty()) {
 358                 error("err.invalid.flag", arg);
 359                 return false;
 360             }
 361 
 362             Option option = null;


 363             if (arg.startsWith("-")) {
 364                 for (Option o : allowableOpts) {
 365                     if (o.matches(arg)) {
 366                         option = o;
 367                         break;
 368                     }
 369                 }
 370             } else if (allowOperands && Option.SOURCEFILE.matches(arg)) {
 371                 option = Option.SOURCEFILE;
 372             }
 373 
 374             if (option == null) {
 375                 if (fm != null && fm.handleOption(arg, argIter)) {
 376                     continue;
 377                 }
 378                 error("err.invalid.flag", arg);
 379                 return false;
 380             }
 381 
 382             if (option.hasArg()) {
 383                 if (!argIter.hasNext()) {
 384                     error("err.req.arg", arg);
 385                     return false;
 386                 }
 387                 String operand = argIter.next();
 388                 if (option.process(helper, arg, operand)) {
 389                     return false;

 390                 }
 391             } else {
 392                 if (option.process(helper, arg)) {

 393                     return false;
 394                 }
 395             }
 396         }
 397 
 398         return true;
 399     }
 400 
 401     /**
 402      * Validates the overall consistency of the options and operands
 403      * processed by processOptions.
 404      * @return true if all args are successfully validating; false otherwise.
 405      * @throws IllegalStateException if a problem is found and errorMode is set to
 406      *      ILLEGAL_STATE
 407      */
 408     public boolean validate() {
 409         JavaFileManager fm = getFileManager();
 410         if (options.isSet(Option.M)) {
 411             if (!fm.hasLocation(StandardLocation.CLASS_OUTPUT)) {
 412                 log.error(Errors.OutputDirMustBeSpecifiedWithDashMOption);
 413             } else if (!fm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) {
 414                 log.error(Errors.ModulesourcepathMustBeSpecifiedWithDashMOption);
 415             } else {
 416                 java.util.List<String> modules = Arrays.asList(options.get(Option.M).split(","));
 417                 try {
 418                     for (String module : modules) {
 419                         Location sourceLoc = fm.getModuleLocation(StandardLocation.MODULE_SOURCE_PATH, module);
 420                         if (sourceLoc == null) {
 421                             log.error(Errors.ModuleNotFoundInModuleSourcePath(module));
 422                         } else {
 423                             Location classLoc = fm.getModuleLocation(StandardLocation.CLASS_OUTPUT, module);
 424 
 425                             for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(JavaFileObject.Kind.SOURCE), true)) {
 426                                 String className = fm.inferBinaryName(sourceLoc, file);
 427                                 JavaFileObject classFile = fm.getJavaFileForInput(classLoc, className, Kind.CLASS);
 428 
 429                                 if (classFile == null || classFile.getLastModified() < file.getLastModified()) {
 430                                     if (fileObjects == null)
 431                                         fileObjects = new HashSet<>();
 432                                     fileObjects.add(file);
 433                                 }
 434                             }
 435                         }
 436                     }
 437                 } catch (IOException ex) {
 438                     log.printLines(PrefixKind.JAVAC, "msg.io");
 439                     ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
 440                     return false;
 441                 }
 442             }
 443         }
 444 
 445         if (isEmpty()) {
 446             // It is allowed to compile nothing if just asking for help or version info.
 447             // But also note that none of these options are supported in API mode.
 448             if (options.isSet(Option.HELP)
 449                 || options.isSet(Option.X)
 450                 || options.isSet(Option.VERSION)
 451                 || options.isSet(Option.FULLVERSION)
 452                 || options.isSet(Option.M))
 453                 return true;
 454 
 455             if (emptyAllowed)
 456                 return true;
 457 

 458             if (JavaCompiler.explicitAnnotationProcessingRequested(options)) {
 459                 error("err.no.source.files.classes");
 460             } else {
 461                 error("err.no.source.files");
 462             }


 463             return false;
 464         }
 465 
 466         if (!checkDirectory(Option.D)) {
 467             return false;
 468         }
 469         if (!checkDirectory(Option.S)) {
 470             return false;
 471         }
 472         if (!checkDirectory(Option.H)) {
 473             return false;
 474         }
 475 
 476         // The following checks are to help avoid accidental confusion between
 477         // directories of modules and exploded module directories.
 478         if (fm instanceof StandardJavaFileManager) {
 479             StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
 480             if (sfm.hasLocation(StandardLocation.CLASS_OUTPUT)) {
 481                 Path outDir = sfm.getLocationAsPaths(StandardLocation.CLASS_OUTPUT).iterator().next();
 482                 if (sfm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) {


 525                                 sourceString,
 526                                 source.requiredTarget().name);
 527                     }
 528                     return false;
 529                 } else {
 530                     target = source.requiredTarget();
 531                     options.put("-target", target.name);
 532                 }
 533             }
 534         }
 535 
 536         String profileString = options.get(Option.PROFILE);
 537         if (profileString != null) {
 538             Profile profile = Profile.lookup(profileString);
 539             if (!profile.isValid(target)) {
 540                 error("warn.profile.target.conflict", profileString, target.name);
 541             }
 542 
 543             // This check is only effective in command line mode,
 544             // where the file manager options are added to options
 545             if (options.get(Option.BOOTCLASSPATH) != null) {
 546                 error("err.profile.bootclasspath.conflict");
 547             }
 548         }
 549 
 550         if (options.isSet(Option.SOURCEPATH) && options.isSet(Option.MODULESOURCEPATH)) {
 551             error("err.sourcepath.modulesourcepath.conflict");
 552         }
 553 
 554         boolean lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option);
 555 
 556         if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) {
 557             if (fm instanceof BaseFileManager) {
 558                 if (((BaseFileManager) fm).isDefaultBootClassPath())
 559                     log.warning(LintCategory.OPTIONS, "source.no.bootclasspath", source.name);
 560             }
 561         }
 562 
 563         boolean obsoleteOptionFound = false;
 564 
 565         if (source.compareTo(Source.MIN) < 0) {
 566             log.error(Errors.OptionRemovedSource(source.name, Source.MIN.name));
 567         } else if (source == Source.MIN && lintOptions) {
 568             log.warning(LintCategory.OPTIONS, Warnings.OptionObsoleteSource(source.name));
 569             obsoleteOptionFound = true;
 570         }
 571 
 572         if (target.compareTo(Target.MIN) < 0) {
 573             log.error(Errors.OptionRemovedTarget(target.name, Target.MIN.name));
 574         } else if (target == Target.MIN && lintOptions) {
 575             log.warning(LintCategory.OPTIONS, Warnings.OptionObsoleteTarget(target.name));
 576             obsoleteOptionFound = true;
 577         }
 578 
 579         final Target t = target;
 580         checkOptionAllowed(t.compareTo(Target.JDK1_8) <= 0,
 581                 option -> error("err.option.not.allowed.with.target", option.getText(), t.name),
 582                 Option.BOOTCLASSPATH,
 583                 Option.XBOOTCLASSPATH_PREPEND, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND,
 584                 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS,
 585                 Option.EXTDIRS, Option.DJAVA_EXT_DIRS);
 586 
 587         checkOptionAllowed(t.compareTo(Target.JDK1_9) >= 0,
 588                 option -> error("err.option.not.allowed.with.target", option.getText(), t.name),
 589                 Option.MODULESOURCEPATH, Option.UPGRADEMODULEPATH,
 590                 Option.SYSTEM, Option.MODULEPATH, Option.ADDMODS, Option.LIMITMODS,
 591                 Option.XPATCH);
 592 
 593         if (fm.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) {
 594             if (!options.isSet(Option.PROC, "only")
 595                     && !fm.hasLocation(StandardLocation.CLASS_OUTPUT)) {
 596                 log.error(Errors.NoOutputDir);
 597             }
 598             if (options.isSet(Option.XMODULE)) {
 599                 log.error(Errors.XmoduleNoModuleSourcepath);
 600             }
 601         }
 602 
 603         if (fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH) &&
 604             fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH)) {
 605             log.error(Errors.ProcessorpathNoProcessormodulepath);
 606         }
 607 
 608         if (obsoleteOptionFound)
 609             log.warning(LintCategory.OPTIONS, "option.obsolete.suppression");
 610 
 611         String addExports = options.get(Option.XADDEXPORTS);
 612         if (addExports != null) {
 613             // Each entry must be of the form module/package=target-list where target-list is a
 614             // comma-separated list of module or ALL-UNNAMED.
 615             // All module/package pairs must be unique.
 616             Pattern p = Pattern.compile("([^/]+)/([^=]+)=(.*)");
 617             Map<String,List<String>> map = new LinkedHashMap<>();
 618             for (String e: addExports.split("\0")) {
 619                 Matcher m = p.matcher(e);
 620                 if (!m.matches()) {
 621                     log.error(Errors.XaddexportsMalformedEntry(e));
 622                     continue;
 623                 }
 624                 String eModule = m.group(1);  // TODO: check a valid dotted identifier
 625                 String ePackage = m.group(2); // TODO: check a valid dotted identifier
 626                 String eTargets = m.group(3);  // TODO: check a valid list of dotted identifier or ALL-UNNAMED
 627                 String eModPkg = eModule + '/' + ePackage;
 628                 List<String> l = map.get(eModPkg);
 629                 map.put(eModPkg, (l == null) ? List.of(eTargets) : l.prepend(eTargets));
 630             }
 631             map.forEach((key, value) -> {
 632                 if (value.size() > 1) {
 633                     log.error(Errors.XaddexportsTooMany(key));
 634                     // TODO: consider adding diag fragments for the entries
 635                 }
 636             });
 637         }
 638 
 639         String addReads = options.get(Option.XADDREADS);
 640         if (addReads != null) {
 641             // Each entry must be of the form module=source-list where source-list is a
 642             // comma separated list of module or ALL-UNNAMED.
 643             // All target modules (i.e. on left of '=')  must be unique.
 644             Pattern p = Pattern.compile("([^=]+)=(.*)");
 645             Map<String,List<String>> map = new LinkedHashMap<>();
 646             for (String e: addReads.split("\0")) {
 647                 Matcher m = p.matcher(e);
 648                 if (!m.matches()) {
 649                     log.error(Errors.XaddreadsMalformedEntry(e));
 650                     continue;
 651                 }
 652                 String eModule = m.group(1);  // TODO: check a valid dotted identifier
 653                 String eSources = m.group(2);  // TODO: check a valid list of dotted identifier or ALL-UNNAMED
 654                 List<String> l = map.get(eModule);
 655                 map.put(eModule, (l == null) ? List.of(eSources) : l.prepend(eSources));
 656             }
 657             map.forEach((key, value) -> {
 658                 if (value.size() > 1) {
 659                     log.error(Errors.XaddreadsTooMany(key));


 707 
 708     /**
 709      * Gets any options specifying how doclint should be run.
 710      * An empty list is returned if no doclint options are specified
 711      * or if the only doclint option is -Xdoclint:none.
 712      * @return options for doclint
 713      */
 714     public List<String> getDocLintOpts() {
 715         String xdoclint = options.get(Option.XDOCLINT);
 716         String xdoclintCustom = options.get(Option.XDOCLINT_CUSTOM);
 717         if (xdoclint == null && xdoclintCustom == null)
 718             return List.nil();
 719 
 720         Set<String> doclintOpts = new LinkedHashSet<>();
 721         if (xdoclint != null)
 722             doclintOpts.add(DocLint.XMSGS_OPTION);
 723         if (xdoclintCustom != null) {
 724             for (String s: xdoclintCustom.split("\\s+")) {
 725                 if (s.isEmpty())
 726                     continue;
 727                 doclintOpts.add(s.replace(Option.XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
 728             }
 729         }
 730 
 731         if (doclintOpts.equals(Collections.singleton(DocLint.XMSGS_CUSTOM_PREFIX + "none")))
 732             return List.nil();
 733 
 734         String checkPackages = options.get(Option.XDOCLINT_PACKAGE);
 735 
 736         if (checkPackages != null) {
 737             for (String s : checkPackages.split("\\s+")) {
 738                 doclintOpts.add(s.replace(Option.XDOCLINT_PACKAGE.text, DocLint.XCHECK_PACKAGE));
 739             }
 740         }
 741 
 742         // standard doclet normally generates H1, H2,
 743         // so for now, allow user comments to assume that
 744         doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2");
 745 
 746         return List.from(doclintOpts.toArray(new String[doclintOpts.size()]));
 747     }
 748 
 749     private boolean checkDirectory(Option option) {
 750         String value = options.get(option);
 751         if (value == null) {
 752             return true;
 753         }
 754         Path file = Paths.get(value);
 755         if (Files.exists(file) && !Files.isDirectory(file)) {
 756             error("err.file.not.directory", value);
 757             return false;
 758         }
 759         return true;
 760     }
 761 
 762     private interface ErrorReporter {
 763         void report(Option o);
 764     }
 765 
 766     void checkOptionAllowed(boolean allowed, ErrorReporter r, Option... opts) {
 767         if (!allowed) {
 768             Stream.of(opts)
 769                   .filter(options :: isSet)
 770                   .forEach(r :: report);
 771         }
 772     }
 773 
















 774     void error(String key, Object... args) {
 775         errors = true;
 776         switch (errorMode) {
 777             case ILLEGAL_ARGUMENT: {
 778                 String msg = log.localize(PrefixKind.JAVAC, key, args);
 779                 throw new PropagatedException(new IllegalArgumentException(msg));
 780             }
 781             case ILLEGAL_STATE: {
 782                 String msg = log.localize(PrefixKind.JAVAC, key, args);
 783                 throw new PropagatedException(new IllegalStateException(msg));
 784             }
 785             case LOG:
 786                 report(key, args);
 787                 log.printLines(PrefixKind.JAVAC, "msg.usage", ownName);
 788         }
 789     }
 790 
 791     void warning(String key, Object... args) {
 792         report(key, args);
 793     }
 794 
 795     private void report(String key, Object... args) {
 796         // Would be good to have support for -XDrawDiagnostics here
 797         log.printRawLines(ownName + ": " + log.localize(PrefixKind.JAVAC, key, args));
 798     }
 799 





 800     private JavaFileManager getFileManager() {
 801         if (fileManager == null)
 802             fileManager = context.get(JavaFileManager.class);
 803         return fileManager;
 804     }
 805 
 806     <T> ListBuffer<T> toList(Iterable<? extends T> items) {
 807         ListBuffer<T> list = new ListBuffer<>();
 808         if (items != null) {
 809             for (T item : items) {
 810                 list.add(item);
 811             }
 812         }
 813         return list;
 814     }
 815 
 816     <T> Set<T> toSet(Iterable<? extends T> items) {
 817         Set<T> set = new LinkedHashSet<>();
 818         if (items != null) {
 819             for (T item : items) {


  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;


 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;


 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;


 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)) {


 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));


 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) {
< prev index next >