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