Sun, 12 Dec 2010 10:05:40 -0800
6990134: minor (but red) findbugs warnings
Reviewed-by: mcimadamore
1 /*
2 * Copyright (c) 2004, 2008, 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 */
26 package com.sun.tools.apt.main;
28 import java.io.File;
29 import java.io.FileWriter;
30 import java.io.IOException;
31 import java.io.PrintWriter;
32 import java.text.MessageFormat;
33 import java.util.ResourceBundle;
34 import java.util.MissingResourceException;
35 import java.util.StringTokenizer;
36 import java.util.Map;
37 import java.util.HashMap;
38 import java.util.Collections;
40 import java.net.URLClassLoader;
41 import java.net.URL;
42 import java.net.MalformedURLException;
44 import javax.tools.JavaFileManager;
45 import javax.tools.StandardLocation;
47 import com.sun.tools.javac.file.JavacFileManager;
48 import com.sun.tools.javac.code.Source;
49 import com.sun.tools.javac.code.Symbol;
50 import com.sun.tools.javac.code.Type;
51 import com.sun.tools.javac.jvm.Target;
52 import com.sun.tools.javac.util.*;
54 import com.sun.tools.apt.comp.AnnotationProcessingError;
55 import com.sun.tools.apt.comp.UsageMessageNeededException;
56 import com.sun.tools.apt.util.Bark;
57 import com.sun.mirror.apt.AnnotationProcessorFactory;
59 import static com.sun.tools.javac.file.Paths.pathToURLs;
61 /** This class provides a commandline interface to the apt build-time
62 * tool.
63 *
64 * <p><b>This is NOT part of any supported API.
65 * If you write code that depends on this, you do so at your own
66 * risk. This code and its internal interfaces are subject to change
67 * or deletion without notice.</b>
68 */
69 @SuppressWarnings("deprecation")
70 public class Main {
72 /** For testing: enter any options you want to be set implicitly
73 * here.
74 */
75 static String[] forcedOpts = {
76 // Preserve parameter names from class files if the class was
77 // compiled with debug enabled
78 "-XDsave-parameter-names"
79 };
81 /** The name of the compiler, for use in diagnostics.
82 */
83 String ownName;
85 /** The writer to use for diagnostic output.
86 */
87 PrintWriter out;
90 /** Instantiated factory to use in lieu of discovery process.
91 */
92 AnnotationProcessorFactory providedFactory = null;
94 /** Map representing original command-line arguments.
95 */
96 Map<String,String> origOptions = new HashMap<String, String>();
98 /** Classloader to use for finding factories.
99 */
100 ClassLoader aptCL = null;
102 /** Result codes.
103 */
104 static final int
105 EXIT_OK = 0, // Compilation completed with no errors.
106 EXIT_ERROR = 1, // Completed but reported errors.
107 EXIT_CMDERR = 2, // Bad command-line arguments
108 EXIT_SYSERR = 3, // System error or resource exhaustion.
109 EXIT_ABNORMAL = 4; // Compiler terminated abnormally
111 /** This class represents an option recognized by the main program
112 */
113 private class Option {
114 /** Whether or not the option is used only aptOnly.
115 */
116 boolean aptOnly = false;
118 /** Option string.
119 */
120 String name;
122 /** Documentation key for arguments.
123 */
124 String argsNameKey;
126 /** Documentation key for description.
127 */
128 String descrKey;
130 /** Suffix option (-foo=bar or -foo:bar)
131 */
132 boolean hasSuffix;
134 Option(String name, String argsNameKey, String descrKey) {
135 this.name = name;
136 this.argsNameKey = argsNameKey;
137 this.descrKey = descrKey;
138 char lastChar = name.charAt(name.length()-1);
139 hasSuffix = lastChar == ':' || lastChar == '=';
140 }
141 Option(String name, String descrKey) {
142 this(name, null, descrKey);
143 }
145 public String toString() {
146 return name;
147 }
149 /** Does this option take a (separate) operand?
150 */
151 boolean hasArg() {
152 return argsNameKey != null && !hasSuffix;
153 }
155 /** Does argument string match option pattern?
156 * @param arg The command line argument string.
157 */
158 boolean matches(String arg) {
159 return hasSuffix ? arg.startsWith(name) : arg.equals(name);
160 }
162 /** For javac-only options, print nothing.
163 */
164 void help() {
165 }
167 String helpSynopsis() {
168 return name +
169 (argsNameKey == null ? "" :
170 ((hasSuffix ? "" : " ") +
171 getLocalizedString(argsNameKey)));
172 }
174 /** Print a line of documentation describing this option, if non-standard.
175 */
176 void xhelp() {}
178 /** Process the option (with arg). Return true if error detected.
179 */
180 boolean process(String option, String arg) {
181 options.put(option, arg);
182 return false;
183 }
185 /** Process the option (without arg). Return true if error detected.
186 */
187 boolean process(String option) {
188 if (hasSuffix)
189 return process(name, option.substring(name.length()));
190 else
191 return process(option, option);
192 }
193 };
195 private class SharedOption extends Option {
196 SharedOption(String name, String argsNameKey, String descrKey) {
197 super(name, argsNameKey, descrKey);
198 }
200 SharedOption(String name, String descrKey) {
201 super(name, descrKey);
202 }
204 void help() {
205 String s = " " + helpSynopsis();
206 out.print(s);
207 for (int j = s.length(); j < 29; j++) out.print(" ");
208 Bark.printLines(out, getLocalizedString(descrKey));
209 }
211 }
213 private class AptOption extends Option {
214 AptOption(String name, String argsNameKey, String descrKey) {
215 super(name, argsNameKey, descrKey);
216 aptOnly = true;
217 }
219 AptOption(String name, String descrKey) {
220 super(name, descrKey);
221 aptOnly = true;
222 }
224 /** Print a line of documentation describing this option, if standard.
225 */
226 void help() {
227 String s = " " + helpSynopsis();
228 out.print(s);
229 for (int j = s.length(); j < 29; j++) out.print(" ");
230 Bark.printLines(out, getLocalizedString(descrKey));
231 }
233 }
235 /** A nonstandard or extended (-X) option
236 */
237 private class XOption extends Option {
238 XOption(String name, String argsNameKey, String descrKey) {
239 super(name, argsNameKey, descrKey);
240 }
241 XOption(String name, String descrKey) {
242 this(name, null, descrKey);
243 }
244 void help() {}
245 void xhelp() {}
246 };
248 /** A nonstandard or extended (-X) option
249 */
250 private class AptXOption extends Option {
251 AptXOption(String name, String argsNameKey, String descrKey) {
252 super(name, argsNameKey, descrKey);
253 aptOnly = true;
254 }
255 AptXOption(String name, String descrKey) {
256 this(name, null, descrKey);
257 }
258 void xhelp() {
259 String s = " " + helpSynopsis();
260 out.print(s);
261 for (int j = s.length(); j < 29; j++) out.print(" ");
262 Log.printLines(out, getLocalizedString(descrKey));
263 }
264 };
266 /** A hidden (implementor) option
267 */
268 private class HiddenOption extends Option {
269 HiddenOption(String name) {
270 super(name, null, null);
271 }
272 HiddenOption(String name, String argsNameKey) {
273 super(name, argsNameKey, null);
274 }
275 void help() {}
276 void xhelp() {}
277 };
279 private class AptHiddenOption extends HiddenOption {
280 AptHiddenOption(String name) {
281 super(name);
282 aptOnly = true;
283 }
284 AptHiddenOption(String name, String argsNameKey) {
285 super(name, argsNameKey);
286 aptOnly = true;
287 }
288 }
290 private Option[] recognizedOptions = {
291 new Option("-g", "opt.g"),
292 new Option("-g:none", "opt.g.none") {
293 boolean process(String option) {
294 options.put("-g:", "none");
295 return false;
296 }
297 },
299 new Option("-g:{lines,vars,source}", "opt.g.lines.vars.source") {
300 boolean matches(String s) {
301 return s.startsWith("-g:");
302 }
303 boolean process(String option) {
304 String suboptions = option.substring(3);
305 options.put("-g:", suboptions);
306 // enter all the -g suboptions as "-g:suboption"
307 for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
308 String tok = t.nextToken();
309 String opt = "-g:" + tok;
310 options.put(opt, opt);
311 }
312 return false;
313 }
314 },
316 new XOption("-Xlint", "opt.Xlint"),
317 new XOption("-Xlint:{"
318 + "all,"
319 + "cast,deprecation,divzero,empty,unchecked,fallthrough,path,serial,finally,overrides,"
320 + "-cast,-deprecation,-divzero,-empty,-unchecked,-fallthrough,-path,-serial,-finally,-overrides,"
321 + "none}",
322 "opt.Xlint.suboptlist") {
323 boolean matches(String s) {
324 return s.startsWith("-Xlint:");
325 }
326 boolean process(String option) {
327 String suboptions = option.substring(7);
328 options.put("-Xlint:", suboptions);
329 // enter all the -Xlint suboptions as "-Xlint:suboption"
330 for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
331 String tok = t.nextToken();
332 String opt = "-Xlint:" + tok;
333 options.put(opt, opt);
334 }
335 return false;
336 }
337 },
339 new Option("-nowarn", "opt.nowarn"),
340 new Option("-verbose", "opt.verbose"),
342 // -deprecation is retained for command-line backward compatibility
343 new Option("-deprecation", "opt.deprecation") {
344 boolean process(String option) {
345 options.put("-Xlint:deprecation", option);
346 return false;
347 }
348 },
350 new SharedOption("-classpath", "opt.arg.path", "opt.classpath"),
351 new SharedOption("-cp", "opt.arg.path", "opt.classpath") {
352 boolean process(String option, String arg) {
353 return super.process("-classpath", arg);
354 }
355 },
356 new Option("-sourcepath", "opt.arg.path", "opt.sourcepath"),
357 new Option("-bootclasspath", "opt.arg.path", "opt.bootclasspath") {
358 boolean process(String option, String arg) {
359 options.remove("-Xbootclasspath/p:");
360 options.remove("-Xbootclasspath/a:");
361 return super.process(option, arg);
362 }
363 },
364 new XOption("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p"),
365 new XOption("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a"),
366 new XOption("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath") {
367 boolean process(String option, String arg) {
368 options.remove("-Xbootclasspath/p:");
369 options.remove("-Xbootclasspath/a:");
370 return super.process("-bootclasspath", arg);
371 }
372 },
373 new Option("-extdirs", "opt.arg.dirs", "opt.extdirs"),
374 new XOption("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs") {
375 boolean process(String option, String arg) {
376 return super.process("-extdirs", arg);
377 }
378 },
379 new Option("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs"),
380 new XOption("-Djava.endorsed.dirs=","opt.arg.dirs", "opt.endorseddirs") {
381 boolean process(String option, String arg) {
382 return super.process("-endorseddirs", arg);
383 }
384 },
385 new Option("-proc:{none, only}", "opt.proc.none.only") {
386 public boolean matches(String s) {
387 return s.equals("-proc:none") || s.equals("-proc:only");
388 }
389 },
390 new Option("-processor", "opt.arg.class", "opt.processor"),
391 new Option("-processorpath", "opt.arg.path", "opt.processorpath"),
393 new SharedOption("-d", "opt.arg.path", "opt.d"),
394 new SharedOption("-s", "opt.arg.path", "opt.s"),
395 new Option("-encoding", "opt.arg.encoding", "opt.encoding"),
396 new SharedOption("-source", "opt.arg.release", "opt.source") {
397 boolean process(String option, String operand) {
398 Source source = Source.lookup(operand);
399 if (source == null) {
400 error("err.invalid.source", operand);
401 return true;
402 } else if (source.compareTo(Source.JDK1_5) > 0) {
403 error("err.unsupported.source.version", operand);
404 return true;
405 }
406 return super.process(option, operand);
407 }
408 },
409 new Option("-target", "opt.arg.release", "opt.target") {
410 boolean process(String option, String operand) {
411 Target target = Target.lookup(operand);
412 if (target == null) {
413 error("err.invalid.target", operand);
414 return true;
415 } else if (target.compareTo(Target.JDK1_5) > 0) {
416 error("err.unsupported.target.version", operand);
417 return true;
418 }
419 return super.process(option, operand);
420 }
421 },
422 new AptOption("-version", "opt.version") {
423 boolean process(String option) {
424 Bark.printLines(out, ownName + " " + AptJavaCompiler.version());
425 return super.process(option);
426 }
427 },
428 new HiddenOption("-fullversion"),
429 new AptOption("-help", "opt.help") {
430 boolean process(String option) {
431 Main.this.help();
432 return super.process(option);
433 }
434 },
435 new SharedOption("-X", "opt.X") {
436 boolean process(String option) {
437 Main.this.xhelp();
438 return super.process(option);
439 }
440 },
442 // This option exists only for the purpose of documenting itself.
443 // It's actually implemented by the launcher.
444 new AptOption("-J", "opt.arg.flag", "opt.J") {
445 String helpSynopsis() {
446 hasSuffix = true;
447 return super.helpSynopsis();
448 }
449 boolean process(String option) {
450 throw new AssertionError
451 ("the -J flag should be caught by the launcher.");
452 }
453 },
456 new SharedOption("-A", "opt.proc.flag", "opt.A") {
457 String helpSynopsis() {
458 hasSuffix = true;
459 return super.helpSynopsis();
460 }
462 boolean matches(String arg) {
463 return arg.startsWith("-A");
464 }
466 boolean hasArg() {
467 return false;
468 }
470 boolean process(String option) {
471 return process(option, option);
472 }
473 },
475 new AptOption("-nocompile", "opt.nocompile"),
477 new AptOption("-print", "opt.print"),
479 new AptOption("-factorypath", "opt.arg.path", "opt.factorypath"),
481 new AptOption("-factory", "opt.arg.class", "opt.factory"),
483 new AptXOption("-XListAnnotationTypes", "opt.XListAnnotationTypes"),
485 new AptXOption("-XListDeclarations", "opt.XListDeclarations"),
487 new AptXOption("-XPrintAptRounds", "opt.XPrintAptRounds"),
489 new AptXOption("-XPrintFactoryInfo", "opt.XPrintFactoryInfo"),
491 /*
492 * Option to treat both classes and source files as
493 * declarations that can be given on the command line and
494 * processed as the result of an apt round.
495 */
496 new AptXOption("-XclassesAsDecls", "opt.XClassesAsDecls"),
498 // new Option("-moreinfo", "opt.moreinfo") {
499 new HiddenOption("-moreinfo") {
500 boolean process(String option) {
501 Type.moreInfo = true;
502 return super.process(option);
503 }
504 },
506 // treat warnings as errors
507 new HiddenOption("-Werror"),
509 // use complex inference from context in the position of a method call argument
510 new HiddenOption("-complexinference"),
512 // prompt after each error
513 // new Option("-prompt", "opt.prompt"),
514 new HiddenOption("-prompt"),
516 // dump stack on error
517 new HiddenOption("-doe"),
519 // display warnings for generic unchecked and unsafe operations
520 new HiddenOption("-warnunchecked") {
521 boolean process(String option) {
522 options.put("-Xlint:unchecked", option);
523 return false;
524 }
525 },
527 new HiddenOption("-Xswitchcheck") {
528 boolean process(String option) {
529 options.put("-Xlint:switchcheck", option);
530 return false;
531 }
532 },
534 // generate trace output for subtyping operations
535 new HiddenOption("-debugsubtyping"),
537 new XOption("-Xmaxerrs", "opt.arg.number", "opt.maxerrs"),
538 new XOption("-Xmaxwarns", "opt.arg.number", "opt.maxwarns"),
539 new XOption("-Xstdout", "opt.arg.file", "opt.Xstdout") {
540 boolean process(String option, String arg) {
541 try {
542 out = new PrintWriter(new FileWriter(arg), true);
543 } catch (java.io.IOException e) {
544 error("err.error.writing.file", arg, e);
545 return true;
546 }
547 return super.process(option, arg);
548 }
549 },
551 new XOption("-Xprint", "opt.print"),
553 new XOption("-XprintRounds", "opt.printRounds"),
555 new XOption("-XprintProcessorInfo", "opt.printProcessorInfo"),
558 /* -O is a no-op, accepted for backward compatibility. */
559 new HiddenOption("-O"),
561 /* -Xjcov produces tables to support the code coverage tool jcov. */
562 new HiddenOption("-Xjcov"),
564 /* This is a back door to the compiler's option table.
565 * -Dx=y sets the option x to the value y.
566 * -Dx sets the option x to the value x.
567 */
568 new HiddenOption("-XD") {
569 String s;
570 boolean matches(String s) {
571 this.s = s;
572 return s.startsWith(name);
573 }
574 boolean process(String option) {
575 s = s.substring(name.length());
576 int eq = s.indexOf('=');
577 String key = (eq < 0) ? s : s.substring(0, eq);
578 String value = (eq < 0) ? s : s.substring(eq+1);
579 options.put(key, value);
580 return false;
581 }
582 },
584 new HiddenOption("sourcefile") {
585 String s;
586 boolean matches(String s) {
587 this.s = s;
588 return s.endsWith(".java") ||
589 (options.get("-XclassesAsDecls") != null);
590 }
591 boolean process(String option) {
592 if (s.endsWith(".java")) {
593 if (!sourceFileNames.contains(s))
594 sourceFileNames.add(s);
595 } else if (options.get("-XclassesAsDecls") != null) {
596 classFileNames.add(s);
597 }
598 return false;
599 }
600 },
601 };
603 /**
604 * Construct a compiler instance.
605 */
606 public Main(String name) {
607 this(name, new PrintWriter(System.err, true));
608 }
610 /**
611 * Construct a compiler instance.
612 */
613 public Main(String name, PrintWriter out) {
614 this.ownName = name;
615 this.out = out;
616 }
618 /** A table of all options that's passed to the JavaCompiler constructor. */
619 private Options options = null;
621 /** The list of source files to process
622 */
623 java.util.List<String> sourceFileNames = new java.util.LinkedList<String>();
625 /** The list of class files to process
626 */
627 java.util.List<String> classFileNames = new java.util.LinkedList<String>();
629 /** List of top level names of generated source files from most recent apt round.
630 */
631 java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>();
633 /** List of names of generated class files from most recent apt round.
634 */
635 java.util.Set<String> genClassFileNames = new java.util.LinkedHashSet<String>();
637 /**
638 * List of all the generated source file names across all apt rounds.
639 */
640 java.util.Set<String> aggregateGenSourceFileNames = new java.util.LinkedHashSet<String>();
642 /**
643 * List of all the generated class file names across all apt rounds.
644 */
645 java.util.Set<String> aggregateGenClassFileNames = new java.util.LinkedHashSet<String>();
647 /**
648 * List of all the generated file names across all apt rounds.
649 */
650 java.util.Set<java.io.File> aggregateGenFiles = new java.util.LinkedHashSet<java.io.File>();
652 /**
653 * Set of all factories that have provided a processor on some apt round.
654 */
655 java.util.Set<Class<? extends AnnotationProcessorFactory> > productiveFactories =
656 new java.util.LinkedHashSet<Class<? extends AnnotationProcessorFactory> >();
660 /** Print a string that explains usage.
661 */
662 void help() {
663 Bark.printLines(out, getLocalizedString("msg.usage.header", ownName));
664 for (int i=0; i < recognizedOptions.length; i++) {
665 recognizedOptions[i].help();
666 }
667 Bark.printLines(out, getLocalizedString("msg.usage.footer"));
668 out.println();
669 }
671 /** Print a string that explains usage for X options.
672 */
673 void xhelp() {
674 for (int i=0; i<recognizedOptions.length; i++) {
675 recognizedOptions[i].xhelp();
676 }
677 out.println();
678 Bark.printLines(out, getLocalizedString("msg.usage.nonstandard.footer"));
679 }
681 /** Report a usage error.
682 */
683 void error(String key, Object... args) {
684 warning(key, args);
685 help();
686 }
688 /** Report a warning.
689 */
690 void warning(String key, Object... args) {
691 Bark.printLines(out, ownName + ": "
692 + getLocalizedString(key, args));
693 }
695 /** Process command line arguments: store all command line options
696 * in `options' table and return all source filenames.
697 * @param args The array of command line arguments.
698 */
699 protected java.util.List<String> processArgs(String[] flags) {
700 int ac = 0;
701 while (ac < flags.length) {
702 String flag = flags[ac];
703 ac++;
705 int j;
706 for (j=0; j < recognizedOptions.length; j++)
707 if (recognizedOptions[j].matches(flag))
708 break;
710 if (j == recognizedOptions.length) {
711 error("err.invalid.flag", flag);
712 return null;
713 }
715 Option option = recognizedOptions[j];
716 if (option.hasArg()) {
717 if (ac == flags.length) {
718 error("err.req.arg", flag);
719 return null;
720 }
721 String operand = flags[ac];
722 ac++;
723 if (option.process(flag, operand))
724 return null;
725 } else {
726 if (option.process(flag))
727 return null;
728 }
729 }
731 String sourceString = options.get("-source");
732 Source source = (sourceString != null)
733 ? Source.lookup(sourceString)
734 : Source.JDK1_5; // JDK 5 is the latest supported source version
735 String targetString = options.get("-target");
736 Target target = (targetString != null)
737 ? Target.lookup(targetString)
738 : Target.JDK1_5; // JDK 5 is the latest supported source version
739 // We don't check source/target consistency for CLDC, as J2ME
740 // profiles are not aligned with J2SE targets; moreover, a
741 // single CLDC target may have many profiles. In addition,
742 // this is needed for the continued functioning of the JSR14
743 // prototype.
744 if (Character.isDigit(target.name.charAt(0)) &&
745 target.compareTo(source.requiredTarget()) < 0) {
746 if (targetString != null) {
747 if (sourceString == null) {
748 warning("warn.target.default.source.conflict",
749 targetString,
750 source.requiredTarget().name);
751 } else {
752 warning("warn.source.target.conflict",
753 sourceString,
754 source.requiredTarget().name);
755 }
756 return null;
757 } else {
758 options.put("-target", source.requiredTarget().name);
759 }
760 }
761 return sourceFileNames;
762 }
764 /** Programmatic interface for main function.
765 * @param args The command line parameters.
766 */
767 public int compile(String[] args, AnnotationProcessorFactory factory) {
768 int returnCode = 0;
769 providedFactory = factory;
771 Context context = new Context();
772 JavacFileManager.preRegister(context);
773 options = Options.instance(context);
774 Bark bark;
776 /*
777 * Process the command line options to create the intial
778 * options data. This processing is at least partially reused
779 * by any recursive apt calls.
780 */
782 // For testing: assume all arguments in forcedOpts are
783 // prefixed to command line arguments.
784 processArgs(forcedOpts);
786 /*
787 * A run of apt only gets passed the most recently generated
788 * files; the initial run of apt gets passed the files from
789 * the command line.
790 */
792 java.util.List<String> origFilenames;
793 try {
794 // assign args the result of parse to capture results of
795 // '@file' expansion
796 origFilenames = processArgs((args=CommandLine.parse(args)));
798 if (options.get("suppress-tool-api-removal-message") == null) {
799 Bark.printLines(out, getLocalizedString("misc.Deprecation"));
800 }
802 if (origFilenames == null) {
803 return EXIT_CMDERR;
804 } else if (origFilenames.size() == 0) {
805 // it is allowed to compile nothing if just asking for help
806 if (options.get("-help") != null ||
807 options.get("-X") != null)
808 return EXIT_OK;
809 }
810 } catch (java.io.FileNotFoundException e) {
811 Bark.printLines(out, ownName + ": " +
812 getLocalizedString("err.file.not.found",
813 e.getMessage()));
814 return EXIT_SYSERR;
815 } catch (IOException ex) {
816 ioMessage(ex);
817 return EXIT_SYSERR;
818 } catch (OutOfMemoryError ex) {
819 resourceMessage(ex);
820 return EXIT_SYSERR;
821 } catch (StackOverflowError ex) {
822 resourceMessage(ex);
823 return EXIT_SYSERR;
824 } catch (FatalError ex) {
825 feMessage(ex);
826 return EXIT_SYSERR;
827 } catch (sun.misc.ServiceConfigurationError sce) {
828 sceMessage(sce);
829 return EXIT_ABNORMAL;
830 } catch (Throwable ex) {
831 bugMessage(ex);
832 return EXIT_ABNORMAL;
833 }
836 boolean firstRound = true;
837 boolean needSourcePath = false;
838 boolean needClassPath = false;
839 boolean classesAsDecls = options.get("-XclassesAsDecls") != null;
841 /*
842 * Create augumented classpath and sourcepath values.
843 *
844 * If any of the prior apt rounds generated any new source
845 * files, the n'th apt round (and any javac invocation) has the
846 * source destination path ("-s path") as the last element of
847 * the "-sourcepath" to the n'th call.
848 *
849 * If any of the prior apt rounds generated any new class files,
850 * the n'th apt round (and any javac invocation) has the class
851 * destination path ("-d path") as the last element of the
852 * "-classpath" to the n'th call.
853 */
854 String augmentedSourcePath = "";
855 String augmentedClassPath = "";
856 String baseClassPath = "";
858 try {
859 /*
860 * Record original options for future annotation processor
861 * invocations.
862 */
863 origOptions = new HashMap<String, String>(options.size());
864 for(String s: options.keySet()) {
865 String value;
866 if (s.equals(value = options.get(s)))
867 origOptions.put(s, (String)null);
868 else
869 origOptions.put(s, value);
870 }
871 origOptions = Collections.unmodifiableMap(origOptions);
873 JavacFileManager fm = (JavacFileManager) context.get(JavaFileManager.class);
874 {
875 // Note: it might be necessary to check for an empty
876 // component ("") of the source path or class path
878 String sourceDest = options.get("-s");
879 if (fm.hasLocation(StandardLocation.SOURCE_PATH)) {
880 for(File f: fm.getLocation(StandardLocation.SOURCE_PATH))
881 augmentedSourcePath += (f + File.pathSeparator);
882 augmentedSourcePath += (sourceDest == null)?".":sourceDest;
883 } else {
884 augmentedSourcePath = ".";
886 if (sourceDest != null)
887 augmentedSourcePath += (File.pathSeparator + sourceDest);
888 }
890 String classDest = options.get("-d");
891 if (fm.hasLocation(StandardLocation.CLASS_PATH)) {
892 for(File f: fm.getLocation(StandardLocation.CLASS_PATH))
893 baseClassPath += (f + File.pathSeparator);
894 // put baseClassPath into map to handle any
895 // value needed for the classloader
896 options.put("-classpath", baseClassPath);
898 augmentedClassPath = baseClassPath + ((classDest == null)?".":classDest);
899 } else {
900 baseClassPath = ".";
901 if (classDest != null)
902 augmentedClassPath = baseClassPath + (File.pathSeparator + classDest);
903 }
904 assert options.get("-classpath") != null;
905 }
907 /*
908 * Create base and augmented class loaders
909 */
910 ClassLoader augmentedAptCL = null;
911 {
912 /*
913 * Use a url class loader to look for classes on the
914 * user-specified class path. Prepend computed bootclass
915 * path, which includes extdirs, to the URLClassLoader apt
916 * uses.
917 */
918 String aptclasspath = "";
919 String bcp = "";
920 Iterable<? extends File> bootclasspath = fm.getLocation(StandardLocation.PLATFORM_CLASS_PATH);
922 if (bootclasspath != null) {
923 for(File f: bootclasspath)
924 bcp += (f + File.pathSeparator);
925 }
927 // If the factory path is set, use that path
928 if (providedFactory == null)
929 aptclasspath = options.get("-factorypath");
930 if (aptclasspath == null)
931 aptclasspath = options.get("-classpath");
933 assert aptclasspath != null;
934 aptclasspath = (bcp + aptclasspath);
935 aptCL = new URLClassLoader(pathToURLs(aptclasspath));
937 if (providedFactory == null &&
938 options.get("-factorypath") != null) // same CL even if new class files written
939 augmentedAptCL = aptCL;
940 else {
941 // Create class loader in case new class files are
942 // written
943 augmentedAptCL = new URLClassLoader(pathToURLs(augmentedClassPath.
944 substring(baseClassPath.length())),
945 aptCL);
946 }
947 }
949 int round = 0; // For -XPrintAptRounds
950 do {
951 round++;
953 Context newContext = new Context();
954 Options newOptions = Options.instance(newContext); // creates a new context
955 newOptions.putAll(options);
957 // populate with old options... don't bother reparsing command line, etc.
959 // if genSource files, must add destination to source path
960 if (genSourceFileNames.size() > 0 && !firstRound) {
961 newOptions.put("-sourcepath", augmentedSourcePath);
962 needSourcePath = true;
963 }
964 aggregateGenSourceFileNames.addAll(genSourceFileNames);
965 sourceFileNames.addAll(genSourceFileNames);
966 genSourceFileNames.clear();
968 // Don't really need to track this; just have to add -d
969 // "foo" to class path if any class files are generated
970 if (genClassFileNames.size() > 0) {
971 newOptions.put("-classpath", augmentedClassPath);
972 aptCL = augmentedAptCL;
973 needClassPath = true;
974 }
975 aggregateGenClassFileNames.addAll(genClassFileNames);
976 classFileNames.addAll(genClassFileNames);
977 genClassFileNames.clear();
979 options = newOptions;
981 if (options.get("-XPrintAptRounds") != null) {
982 out.println("apt Round : " + round);
983 out.println("filenames: " + sourceFileNames);
984 if (classesAsDecls)
985 out.println("classnames: " + classFileNames);
986 out.println("options: " + options);
987 }
989 returnCode = compile(args, newContext);
990 firstRound = false;
992 // Check for reported errors before continuing
993 bark = Bark.instance(newContext);
994 } while(((genSourceFileNames.size() != 0 ) ||
995 (classesAsDecls && genClassFileNames.size() != 0)) &&
996 bark.nerrors == 0);
997 } catch (UsageMessageNeededException umne) {
998 help();
999 return EXIT_CMDERR; // will cause usage message to be printed
1000 }
1002 /*
1003 * Do not compile if a processor has reported an error or if
1004 * there are no source files to process. A more sophisticated
1005 * test would also fail for syntax errors caught by javac.
1006 */
1007 if (options.get("-nocompile") == null &&
1008 options.get("-print") == null &&
1009 bark.nerrors == 0 &&
1010 (origFilenames.size() > 0 || aggregateGenSourceFileNames.size() > 0 )) {
1011 /*
1012 * Need to create new argument string for calling javac:
1013 * 1. apt specific arguments (e.g. -factory) must be stripped out
1014 * 2. proper settings for sourcepath and classpath must be used
1015 * 3. generated class names must be added
1016 * 4. class file names as declarations must be removed
1017 */
1019 int newArgsLength = args.length +
1020 (needSourcePath?1:0) +
1021 (needClassPath?1:0) +
1022 aggregateGenSourceFileNames.size();
1024 // Null out apt-specific options and don't copy over into
1025 // newArgs. This loop should be a lot faster; the options
1026 // array should be replaced with a better data structure
1027 // which includes a map from strings to options.
1028 //
1029 // If treating classes as declarations, must strip out
1030 // class names from the javac argument list
1031 argLoop:
1032 for(int i = 0; i < args.length; i++) {
1033 int matchPosition = -1;
1035 // "-A" by itself is recognized by apt but not javac
1036 if (args[i] != null && args[i].equals("-A")) {
1037 newArgsLength--;
1038 args[i] = null;
1039 continue argLoop;
1040 } else {
1041 optionLoop:
1042 for(int j = 0; j < recognizedOptions.length; j++) {
1043 if (args[i] != null && recognizedOptions[j].matches(args[i])) {
1044 matchPosition = j;
1045 break optionLoop;
1046 }
1047 }
1049 if (matchPosition != -1) {
1050 Option op = recognizedOptions[matchPosition];
1051 if (op.aptOnly) {
1052 newArgsLength--;
1053 args[i] = null;
1054 if (op.hasArg()) {
1055 newArgsLength--;
1056 args[i+1] = null;
1057 }
1058 } else {
1059 if (op.hasArg()) { // skip over next string
1060 i++;
1061 continue argLoop;
1062 }
1064 if ((options.get("-XclassesAsDecls") != null) &&
1065 (matchPosition == (recognizedOptions.length-1)) ){
1066 // Remove class file names from
1067 // consideration by javac.
1068 if (! args[i].endsWith(".java")) {
1069 newArgsLength--;
1070 args[i] = null;
1071 }
1072 }
1073 }
1074 }
1075 }
1076 }
1078 String newArgs[] = new String[newArgsLength];
1080 int j = 0;
1081 for(int i=0; i < args.length; i++) {
1082 if (args[i] != null)
1083 newArgs[j++] = args[i];
1084 }
1086 if (needClassPath)
1087 newArgs[j++] = "-XD-classpath=" + augmentedClassPath;
1089 if (needSourcePath) {
1090 newArgs[j++] = "-XD-sourcepath=" + augmentedSourcePath;
1092 for(String s: aggregateGenSourceFileNames)
1093 newArgs[j++] = s;
1094 }
1096 returnCode = com.sun.tools.javac.Main.compile(newArgs);
1097 }
1099 return returnCode;
1100 }
1102 /** Programmatic interface for main function.
1103 * @param args The command line parameters.
1104 */
1105 int compile(String[] args, Context context) {
1106 boolean assertionsEnabled = false;
1107 assert assertionsEnabled = true;
1108 if (!assertionsEnabled) {
1109 // Bark.printLines(out, "fatal error: assertions must be enabled when running javac");
1110 // return EXIT_ABNORMAL;
1111 }
1112 int exitCode = EXIT_OK;
1114 AptJavaCompiler comp = null;
1115 try {
1116 context.put(Bark.outKey, out);
1118 comp = AptJavaCompiler.instance(context);
1119 if (comp == null)
1120 return EXIT_SYSERR;
1122 java.util.List<String> nameList = new java.util.LinkedList<String>();
1123 nameList.addAll(sourceFileNames);
1124 if (options.get("-XclassesAsDecls") != null)
1125 nameList.addAll(classFileNames);
1127 List<Symbol.ClassSymbol> cs
1128 = comp.compile(List.from(nameList.toArray(new String[0])),
1129 origOptions,
1130 aptCL,
1131 providedFactory,
1132 productiveFactories,
1133 aggregateGenFiles);
1135 /*
1136 * If there aren't new source files, we shouldn't bother
1137 * running javac if there were errors.
1138 *
1139 * If there are new files, we should try running javac in
1140 * case there were typing errors.
1141 *
1142 */
1144 if (comp.errorCount() != 0 ||
1145 options.get("-Werror") != null && comp.warningCount() != 0)
1146 return EXIT_ERROR;
1147 } catch (IOException ex) {
1148 ioMessage(ex);
1149 return EXIT_SYSERR;
1150 } catch (OutOfMemoryError ex) {
1151 resourceMessage(ex);
1152 return EXIT_SYSERR;
1153 } catch (StackOverflowError ex) {
1154 resourceMessage(ex);
1155 return EXIT_SYSERR;
1156 } catch (FatalError ex) {
1157 feMessage(ex);
1158 return EXIT_SYSERR;
1159 } catch (UsageMessageNeededException umne) {
1160 help();
1161 return EXIT_CMDERR; // will cause usage message to be printed
1162 } catch (AnnotationProcessingError ex) {
1163 apMessage(ex);
1164 return EXIT_ABNORMAL;
1165 } catch (sun.misc.ServiceConfigurationError sce) {
1166 sceMessage(sce);
1167 return EXIT_ABNORMAL;
1168 } catch (Throwable ex) {
1169 bugMessage(ex);
1170 return EXIT_ABNORMAL;
1171 } finally {
1172 if (comp != null) {
1173 comp.close();
1174 genSourceFileNames.addAll(comp.getSourceFileNames());
1175 genClassFileNames.addAll(comp.getClassFileNames());
1176 }
1177 sourceFileNames = new java.util.LinkedList<String>();
1178 classFileNames = new java.util.LinkedList<String>();
1179 }
1180 return exitCode;
1181 }
1183 /** Print a message reporting an internal error.
1184 */
1185 void bugMessage(Throwable ex) {
1186 Bark.printLines(out, getLocalizedString("msg.bug",
1187 AptJavaCompiler.version()));
1188 ex.printStackTrace(out);
1189 }
1191 /** Print a message reporting an fatal error.
1192 */
1193 void apMessage(AnnotationProcessingError ex) {
1194 Bark.printLines(out, getLocalizedString("misc.Problem"));
1195 ex.getCause().printStackTrace(out);
1196 }
1198 /** Print a message about sun.misc.Service problem.
1199 */
1200 void sceMessage(sun.misc.ServiceConfigurationError ex) {
1201 Bark.printLines(out, getLocalizedString("misc.SunMiscService"));
1202 ex.printStackTrace(out);
1203 }
1205 /** Print a message reporting an fatal error.
1206 */
1207 void feMessage(Throwable ex) {
1208 Bark.printLines(out, ex.toString());
1209 }
1211 /** Print a message reporting an input/output error.
1212 */
1213 void ioMessage(Throwable ex) {
1214 Bark.printLines(out, getLocalizedString("msg.io"));
1215 ex.printStackTrace(out);
1216 }
1218 /** Print a message reporting an out-of-resources error.
1219 */
1220 void resourceMessage(Throwable ex) {
1221 Bark.printLines(out, getLocalizedString("msg.resource"));
1222 ex.printStackTrace(out);
1223 }
1225 /* ************************************************************************
1226 * Internationalization
1227 *************************************************************************/
1229 /** Find a localized string in the resource bundle.
1230 * @param key The key for the localized string.
1231 */
1232 private static String getLocalizedString(String key, Object... args) {
1233 return getText(key, args);
1234 }
1236 private static final String javacRB =
1237 "com.sun.tools.javac.resources.javac";
1239 private static final String aptRB =
1240 "com.sun.tools.apt.resources.apt";
1242 private static ResourceBundle messageRBjavac;
1243 private static ResourceBundle messageRBapt;
1245 /** Initialize ResourceBundle.
1246 */
1247 private static void initResource() {
1248 try {
1249 messageRBapt = ResourceBundle.getBundle(aptRB);
1250 messageRBjavac = ResourceBundle.getBundle(javacRB);
1251 } catch (MissingResourceException e) {
1252 Error x = new FatalError("Fatal Error: Resource for apt or javac is missing");
1253 x.initCause(e);
1254 throw x;
1255 }
1256 }
1258 /** Get and format message string from resource.
1259 */
1260 private static String getText(String key, Object... _args) {
1261 String[] args = new String[_args.length];
1262 for (int i=0; i<_args.length; i++) {
1263 args[i] = "" + _args[i];
1264 }
1265 if (messageRBapt == null || messageRBjavac == null )
1266 initResource();
1267 try {
1268 return MessageFormat.format(messageRBapt.getString("apt." + key),
1269 (Object[]) args);
1270 } catch (MissingResourceException e) {
1271 try {
1272 return MessageFormat.format(messageRBjavac.getString("javac." + key),
1273 (Object[]) args);
1274 } catch (MissingResourceException f) {
1275 String msg = "apt or javac message file broken: key={0} "
1276 + "arguments={1}, {2}";
1277 return MessageFormat.format(msg, (Object[]) args);
1278 }
1279 }
1280 }
1281 }