src/share/classes/com/sun/tools/apt/main/Main.java

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

mercurial