diff -r 000000000000 -r 9a66ca7c79fa src/share/classes/com/sun/tools/apt/main/Main.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/apt/main/Main.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1329 @@
+/*
+ * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.apt.main;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+import java.util.StringTokenizer;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.Collection;
+
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.io.File;
+import java.net.MalformedURLException;
+
+import com.sun.tools.javac.util.Paths;
+import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.util.*;
+
+import com.sun.tools.apt.comp.AnnotationProcessingError;
+import com.sun.tools.apt.comp.UsageMessageNeededException;
+import com.sun.tools.apt.util.Bark;
+import com.sun.mirror.apt.AnnotationProcessorFactory;
+
+/** This class provides a commandline interface to the apt build-time
+ * tool.
+ *
+ *
This is NOT part of any API supported by Sun Microsystems.
+ * If you write code that depends on this, you do so at your own
+ * risk. This code and its internal interfaces are subject to change
+ * or deletion without notice.
+ */
+public class Main {
+
+ /** For testing: enter any options you want to be set implicitly
+ * here.
+ */
+ static String[] forcedOpts = {
+ // Preserve parameter names from class files if the class was
+ // compiled with debug enabled
+ "-XDsave-parameter-names"
+ };
+
+ /** The name of the compiler, for use in diagnostics.
+ */
+ String ownName;
+
+ /** The writer to use for diagnostic output.
+ */
+ PrintWriter out;
+
+
+ /** Instantiated factory to use in lieu of discovery process.
+ */
+ AnnotationProcessorFactory providedFactory = null;
+
+ /** Map representing original command-line arguments.
+ */
+ Map origOptions = new HashMap();
+
+ /** Classloader to use for finding factories.
+ */
+ ClassLoader aptCL = null;
+
+ /** Result codes.
+ */
+ static final int
+ EXIT_OK = 0, // Compilation completed with no errors.
+ EXIT_ERROR = 1, // Completed but reported errors.
+ EXIT_CMDERR = 2, // Bad command-line arguments
+ EXIT_SYSERR = 3, // System error or resource exhaustion.
+ EXIT_ABNORMAL = 4; // Compiler terminated abnormally
+
+ /** This class represents an option recognized by the main program
+ */
+ private class Option {
+ /** Whether or not the option is used only aptOnly.
+ */
+ boolean aptOnly = false;
+
+ /** Option string.
+ */
+ String name;
+
+ /** Documentation key for arguments.
+ */
+ String argsNameKey;
+
+ /** Documentation key for description.
+ */
+ String descrKey;
+
+ /** Suffix option (-foo=bar or -foo:bar)
+ */
+ boolean hasSuffix;
+
+ Option(String name, String argsNameKey, String descrKey) {
+ this.name = name;
+ this.argsNameKey = argsNameKey;
+ this.descrKey = descrKey;
+ char lastChar = name.charAt(name.length()-1);
+ hasSuffix = lastChar == ':' || lastChar == '=';
+ }
+ Option(String name, String descrKey) {
+ this(name, null, descrKey);
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ /** Does this option take a (separate) operand?
+ */
+ boolean hasArg() {
+ return argsNameKey != null && !hasSuffix;
+ }
+
+ /** Does argument string match option pattern?
+ * @param arg The command line argument string.
+ */
+ boolean matches(String arg) {
+ return hasSuffix ? arg.startsWith(name) : arg.equals(name);
+ }
+
+ /** For javac-only options, print nothing.
+ */
+ void help() {
+ }
+
+ String helpSynopsis() {
+ return name +
+ (argsNameKey == null ? "" :
+ ((hasSuffix ? "" : " ") +
+ getLocalizedString(argsNameKey)));
+ }
+
+ /** Print a line of documentation describing this option, if non-standard.
+ */
+ void xhelp() {}
+
+ /** Process the option (with arg). Return true if error detected.
+ */
+ boolean process(String option, String arg) {
+ options.put(option, arg);
+ return false;
+ }
+
+ /** Process the option (without arg). Return true if error detected.
+ */
+ boolean process(String option) {
+ if (hasSuffix)
+ return process(name, option.substring(name.length()));
+ else
+ return process(option, option);
+ }
+ };
+
+ private class SharedOption extends Option {
+ SharedOption(String name, String argsNameKey, String descrKey) {
+ super(name, argsNameKey, descrKey);
+ }
+
+ SharedOption(String name, String descrKey) {
+ super(name, descrKey);
+ }
+
+ void help() {
+ String s = " " + helpSynopsis();
+ out.print(s);
+ for (int j = s.length(); j < 29; j++) out.print(" ");
+ Bark.printLines(out, getLocalizedString(descrKey));
+ }
+
+ }
+
+ private class AptOption extends Option {
+ AptOption(String name, String argsNameKey, String descrKey) {
+ super(name, argsNameKey, descrKey);
+ aptOnly = true;
+ }
+
+ AptOption(String name, String descrKey) {
+ super(name, descrKey);
+ aptOnly = true;
+ }
+
+ /** Print a line of documentation describing this option, if standard.
+ */
+ void help() {
+ String s = " " + helpSynopsis();
+ out.print(s);
+ for (int j = s.length(); j < 29; j++) out.print(" ");
+ Bark.printLines(out, getLocalizedString(descrKey));
+ }
+
+ }
+
+ /** A nonstandard or extended (-X) option
+ */
+ private class XOption extends Option {
+ XOption(String name, String argsNameKey, String descrKey) {
+ super(name, argsNameKey, descrKey);
+ }
+ XOption(String name, String descrKey) {
+ this(name, null, descrKey);
+ }
+ void help() {}
+ void xhelp() {}
+ };
+
+ /** A nonstandard or extended (-X) option
+ */
+ private class AptXOption extends Option {
+ AptXOption(String name, String argsNameKey, String descrKey) {
+ super(name, argsNameKey, descrKey);
+ aptOnly = true;
+ }
+ AptXOption(String name, String descrKey) {
+ this(name, null, descrKey);
+ }
+ void xhelp() {
+ String s = " " + helpSynopsis();
+ out.print(s);
+ for (int j = s.length(); j < 29; j++) out.print(" ");
+ Log.printLines(out, getLocalizedString(descrKey));
+ }
+ };
+
+ /** A hidden (implementor) option
+ */
+ private class HiddenOption extends Option {
+ HiddenOption(String name) {
+ super(name, null, null);
+ }
+ HiddenOption(String name, String argsNameKey) {
+ super(name, argsNameKey, null);
+ }
+ void help() {}
+ void xhelp() {}
+ };
+
+ private class AptHiddenOption extends HiddenOption {
+ AptHiddenOption(String name) {
+ super(name);
+ aptOnly = true;
+ }
+ AptHiddenOption(String name, String argsNameKey) {
+ super(name, argsNameKey);
+ aptOnly = true;
+ }
+ }
+
+ private Option[] recognizedOptions = {
+ new Option("-g", "opt.g"),
+ new Option("-g:none", "opt.g.none") {
+ boolean process(String option) {
+ options.put("-g:", "none");
+ return false;
+ }
+ },
+
+ new Option("-g:{lines,vars,source}", "opt.g.lines.vars.source") {
+ boolean matches(String s) {
+ return s.startsWith("-g:");
+ }
+ boolean process(String option) {
+ String suboptions = option.substring(3);
+ options.put("-g:", suboptions);
+ // enter all the -g suboptions as "-g:suboption"
+ for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
+ String tok = t.nextToken();
+ String opt = "-g:" + tok;
+ options.put(opt, opt);
+ }
+ return false;
+ }
+ },
+
+ new XOption("-Xlint", "opt.Xlint"),
+ new XOption("-Xlint:{"
+ + "all,"
+ + "cast,deprecation,divzero,empty,unchecked,fallthrough,path,serial,finally,overrides,"
+ + "-cast,-deprecation,-divzero,-empty,-unchecked,-fallthrough,-path,-serial,-finally,-overrides,"
+ + "none}",
+ "opt.Xlint.suboptlist") {
+ boolean matches(String s) {
+ return s.startsWith("-Xlint:");
+ }
+ boolean process(String option) {
+ String suboptions = option.substring(7);
+ options.put("-Xlint:", suboptions);
+ // enter all the -Xlint suboptions as "-Xlint:suboption"
+ for (StringTokenizer t = new StringTokenizer(suboptions, ","); t.hasMoreTokens(); ) {
+ String tok = t.nextToken();
+ String opt = "-Xlint:" + tok;
+ options.put(opt, opt);
+ }
+ return false;
+ }
+ },
+
+ new Option("-nowarn", "opt.nowarn"),
+ new Option("-verbose", "opt.verbose"),
+
+ // -deprecation is retained for command-line backward compatibility
+ new Option("-deprecation", "opt.deprecation") {
+ boolean process(String option) {
+ options.put("-Xlint:deprecation", option);
+ return false;
+ }
+ },
+
+ new SharedOption("-classpath", "opt.arg.path", "opt.classpath"),
+ new SharedOption("-cp", "opt.arg.path", "opt.classpath") {
+ boolean process(String option, String arg) {
+ return super.process("-classpath", arg);
+ }
+ },
+ new Option("-sourcepath", "opt.arg.path", "opt.sourcepath"),
+ new Option("-bootclasspath", "opt.arg.path", "opt.bootclasspath") {
+ boolean process(String option, String arg) {
+ options.remove("-Xbootclasspath/p:");
+ options.remove("-Xbootclasspath/a:");
+ return super.process(option, arg);
+ }
+ },
+ new XOption("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p"),
+ new XOption("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a"),
+ new XOption("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath") {
+ boolean process(String option, String arg) {
+ options.remove("-Xbootclasspath/p:");
+ options.remove("-Xbootclasspath/a:");
+ return super.process("-bootclasspath", arg);
+ }
+ },
+ new Option("-extdirs", "opt.arg.dirs", "opt.extdirs"),
+ new XOption("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs") {
+ boolean process(String option, String arg) {
+ return super.process("-extdirs", arg);
+ }
+ },
+ new Option("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs"),
+ new XOption("-Djava.endorsed.dirs=","opt.arg.dirs", "opt.endorseddirs") {
+ boolean process(String option, String arg) {
+ return super.process("-endorseddirs", arg);
+ }
+ },
+ new Option("-proc:{none, only}", "opt.proc.none.only") {
+ public boolean matches(String s) {
+ return s.equals("-proc:none") || s.equals("-proc:only");
+ }
+ },
+ new Option("-processor", "opt.arg.class", "opt.processor"),
+ new Option("-processorpath", "opt.arg.path", "opt.processorpath"),
+
+ new SharedOption("-d", "opt.arg.path", "opt.d"),
+ new SharedOption("-s", "opt.arg.path", "opt.s"),
+ new Option("-encoding", "opt.arg.encoding", "opt.encoding"),
+ new SharedOption("-source", "opt.arg.release", "opt.source") {
+ boolean process(String option, String operand) {
+ Source source = Source.lookup(operand);
+ if (source == null) {
+ error("err.invalid.source", operand);
+ return true;
+ } else if (source.compareTo(Source.JDK1_5) > 0) {
+ error("err.unsupported.source.version", operand);
+ return true;
+ }
+ return super.process(option, operand);
+ }
+ },
+ new Option("-target", "opt.arg.release", "opt.target") {
+ boolean process(String option, String operand) {
+ Target target = Target.lookup(operand);
+ if (target == null) {
+ error("err.invalid.target", operand);
+ return true;
+ } else if (target.compareTo(Target.JDK1_5) > 0) {
+ error("err.unsupported.target.version", operand);
+ return true;
+ }
+ return super.process(option, operand);
+ }
+ },
+ new AptOption("-version", "opt.version") {
+ boolean process(String option) {
+ Bark.printLines(out, ownName + " " + JavaCompiler.version());
+ return super.process(option);
+ }
+ },
+ new HiddenOption("-fullversion"),
+ new AptOption("-help", "opt.help") {
+ boolean process(String option) {
+ Main.this.help();
+ return super.process(option);
+ }
+ },
+ new SharedOption("-X", "opt.X") {
+ boolean process(String option) {
+ Main.this.xhelp();
+ return super.process(option);
+ }
+ },
+
+ // This option exists only for the purpose of documenting itself.
+ // It's actually implemented by the launcher.
+ new AptOption("-J", "opt.arg.flag", "opt.J") {
+ String helpSynopsis() {
+ hasSuffix = true;
+ return super.helpSynopsis();
+ }
+ boolean process(String option) {
+ throw new AssertionError
+ ("the -J flag should be caught by the launcher.");
+ }
+ },
+
+
+ new SharedOption("-A", "opt.proc.flag", "opt.A") {
+ String helpSynopsis() {
+ hasSuffix = true;
+ return super.helpSynopsis();
+ }
+
+ boolean matches(String arg) {
+ return arg.startsWith("-A");
+ }
+
+ boolean hasArg() {
+ return false;
+ }
+
+ boolean process(String option) {
+ return process(option, option);
+ }
+ },
+
+ new AptOption("-nocompile", "opt.nocompile"),
+
+ new AptOption("-print", "opt.print"),
+
+ new AptOption("-factorypath", "opt.arg.path", "opt.factorypath"),
+
+ new AptOption("-factory", "opt.arg.class", "opt.factory"),
+
+ new AptXOption("-XListAnnotationTypes", "opt.XListAnnotationTypes"),
+
+ new AptXOption("-XListDeclarations", "opt.XListDeclarations"),
+
+ new AptXOption("-XPrintAptRounds", "opt.XPrintAptRounds"),
+
+ new AptXOption("-XPrintFactoryInfo", "opt.XPrintFactoryInfo"),
+
+ /*
+ * Option to treat both classes and source files as
+ * declarations that can be given on the command line and
+ * processed as the result of an apt round.
+ */
+ new AptXOption("-XclassesAsDecls", "opt.XClassesAsDecls"),
+
+ // new Option("-moreinfo", "opt.moreinfo") {
+ new HiddenOption("-moreinfo") {
+ boolean process(String option) {
+ Type.moreInfo = true;
+ return super.process(option);
+ }
+ },
+
+ // treat warnings as errors
+ new HiddenOption("-Werror"),
+
+ // use complex inference from context in the position of a method call argument
+ new HiddenOption("-complexinference"),
+
+ // prompt after each error
+ // new Option("-prompt", "opt.prompt"),
+ new HiddenOption("-prompt"),
+
+ // dump stack on error
+ new HiddenOption("-doe"),
+
+ // display warnings for generic unchecked and unsafe operations
+ new HiddenOption("-warnunchecked") {
+ boolean process(String option) {
+ options.put("-Xlint:unchecked", option);
+ return false;
+ }
+ },
+
+ new HiddenOption("-Xswitchcheck") {
+ boolean process(String option) {
+ options.put("-Xlint:switchcheck", option);
+ return false;
+ }
+ },
+
+ // generate trace output for subtyping operations
+ new HiddenOption("-debugsubtyping"),
+
+ new XOption("-Xmaxerrs", "opt.arg.number", "opt.maxerrs"),
+ new XOption("-Xmaxwarns", "opt.arg.number", "opt.maxwarns"),
+ new XOption("-Xstdout", "opt.arg.file", "opt.Xstdout") {
+ boolean process(String option, String arg) {
+ try {
+ out = new PrintWriter(new FileWriter(arg), true);
+ } catch (java.io.IOException e) {
+ error("err.error.writing.file", arg, e);
+ return true;
+ }
+ return super.process(option, arg);
+ }
+ },
+
+ new XOption("-Xprint", "opt.print"),
+
+ new XOption("-XprintRounds", "opt.printRounds"),
+
+ new XOption("-XprintProcessorInfo", "opt.printProcessorInfo"),
+
+
+ /* -O is a no-op, accepted for backward compatibility. */
+ new HiddenOption("-O"),
+
+ /* -Xjcov produces tables to support the code coverage tool jcov. */
+ new HiddenOption("-Xjcov"),
+
+ /* This is a back door to the compiler's option table.
+ * -Dx=y sets the option x to the value y.
+ * -Dx sets the option x to the value x.
+ */
+ new HiddenOption("-XD") {
+ String s;
+ boolean matches(String s) {
+ this.s = s;
+ return s.startsWith(name);
+ }
+ boolean process(String option) {
+ s = s.substring(name.length());
+ int eq = s.indexOf('=');
+ String key = (eq < 0) ? s : s.substring(0, eq);
+ String value = (eq < 0) ? s : s.substring(eq+1);
+ options.put(key, value);
+ return false;
+ }
+ },
+
+ new HiddenOption("sourcefile") {
+ String s;
+ boolean matches(String s) {
+ this.s = s;
+ return s.endsWith(".java") ||
+ (options.get("-XclassesAsDecls") != null);
+ }
+ boolean process(String option) {
+ if (s.endsWith(".java")) {
+ if (!sourceFileNames.contains(s))
+ sourceFileNames.add(s);
+ } else if (options.get("-XclassesAsDecls") != null) {
+ classFileNames.add(s);
+ }
+ return false;
+ }
+ },
+ };
+
+ /**
+ * Construct a compiler instance.
+ */
+ public Main(String name) {
+ this(name, new PrintWriter(System.err, true));
+ }
+
+ /**
+ * Construct a compiler instance.
+ */
+ public Main(String name, PrintWriter out) {
+ this.ownName = name;
+ this.out = out;
+ }
+
+ /** A table of all options that's passed to the JavaCompiler constructor. */
+ private Options options = null;
+
+ /** The list of source files to process
+ */
+ java.util.List sourceFileNames = new java.util.LinkedList();
+
+ /** The list of class files to process
+ */
+ java.util.List classFileNames = new java.util.LinkedList();
+
+ /** List of top level names of generated source files from most recent apt round.
+ */
+ java.util.Set genSourceFileNames = new java.util.LinkedHashSet();
+
+ /** List of names of generated class files from most recent apt round.
+ */
+ java.util.Set genClassFileNames = new java.util.LinkedHashSet();
+
+ /**
+ * List of all the generated source file names across all apt rounds.
+ */
+ java.util.Set aggregateGenSourceFileNames = new java.util.LinkedHashSet();
+
+ /**
+ * List of all the generated class file names across all apt rounds.
+ */
+ java.util.Set aggregateGenClassFileNames = new java.util.LinkedHashSet();
+
+ /**
+ * List of all the generated file names across all apt rounds.
+ */
+ java.util.Set aggregateGenFiles = new java.util.LinkedHashSet();
+
+ /**
+ * Set of all factories that have provided a processor on some apt round.
+ */
+ java.util.Set > productiveFactories =
+ new java.util.LinkedHashSet >();
+
+
+
+ /** Print a string that explains usage.
+ */
+ void help() {
+ Bark.printLines(out, getLocalizedString("msg.usage.header", ownName));
+ for (int i=0; i < recognizedOptions.length; i++) {
+ recognizedOptions[i].help();
+ }
+ Bark.printLines(out, getLocalizedString("msg.usage.footer"));
+ out.println();
+ }
+
+ /** Print a string that explains usage for X options.
+ */
+ void xhelp() {
+ for (int i=0; i processArgs(String[] flags) {
+ int ac = 0;
+ while (ac < flags.length) {
+ String flag = flags[ac];
+ ac++;
+
+ int j;
+ for (j=0; j < recognizedOptions.length; j++)
+ if (recognizedOptions[j].matches(flag))
+ break;
+
+ if (j == recognizedOptions.length) {
+ error("err.invalid.flag", flag);
+ return null;
+ }
+
+ Option option = recognizedOptions[j];
+ if (option.hasArg()) {
+ if (ac == flags.length) {
+ error("err.req.arg", flag);
+ return null;
+ }
+ String operand = flags[ac];
+ ac++;
+ if (option.process(flag, operand))
+ return null;
+ } else {
+ if (option.process(flag))
+ return null;
+ }
+ }
+
+ String sourceString = options.get("-source");
+ Source source = (sourceString != null)
+ ? Source.lookup(sourceString)
+ : Source.JDK1_5; // JDK 5 is the latest supported source version
+ String targetString = options.get("-target");
+ Target target = (targetString != null)
+ ? Target.lookup(targetString)
+ : Target.JDK1_5; // JDK 5 is the latest supported source version
+ // We don't check source/target consistency for CLDC, as J2ME
+ // profiles are not aligned with J2SE targets; moreover, a
+ // single CLDC target may have many profiles. In addition,
+ // this is needed for the continued functioning of the JSR14
+ // prototype.
+ if (Character.isDigit(target.name.charAt(0)) &&
+ target.compareTo(source.requiredTarget()) < 0) {
+ if (targetString != null) {
+ if (sourceString == null) {
+ warning("warn.target.default.source.conflict",
+ targetString,
+ source.requiredTarget().name);
+ } else {
+ warning("warn.source.target.conflict",
+ sourceString,
+ source.requiredTarget().name);
+ }
+ return null;
+ } else {
+ options.put("-target", source.requiredTarget().name);
+ }
+ }
+ return sourceFileNames;
+ }
+
+ /** Programmatic interface for main function.
+ * @param args The command line parameters.
+ */
+ public int compile(String[] args, AnnotationProcessorFactory factory) {
+ int returnCode = 0;
+ providedFactory = factory;
+
+ Context context = new Context();
+ options = Options.instance(context);
+ Bark bark;
+
+ /*
+ * Process the command line options to create the intial
+ * options data. This processing is at least partially reused
+ * by any recursive apt calls.
+ */
+
+ // For testing: assume all arguments in forcedOpts are
+ // prefixed to command line arguments.
+ processArgs(forcedOpts);
+
+
+ /*
+ * A run of apt only gets passed the most recently generated
+ * files; the initial run of apt gets passed the files from
+ * the command line.
+ */
+
+ java.util.List origFilenames;
+ try {
+ // assign args the result of parse to capture results of
+ // '@file' expansion
+ origFilenames = processArgs((args=CommandLine.parse(args)));
+ if (origFilenames == null) {
+ return EXIT_CMDERR;
+ } else if (origFilenames.size() == 0) {
+ // it is allowed to compile nothing if just asking for help
+ if (options.get("-help") != null ||
+ options.get("-X") != null)
+ return EXIT_OK;
+ }
+ } catch (java.io.FileNotFoundException e) {
+ Bark.printLines(out, ownName + ": " +
+ getLocalizedString("err.file.not.found",
+ e.getMessage()));
+ return EXIT_SYSERR;
+ } catch (IOException ex) {
+ ioMessage(ex);
+ return EXIT_SYSERR;
+ } catch (OutOfMemoryError ex) {
+ resourceMessage(ex);
+ return EXIT_SYSERR;
+ } catch (StackOverflowError ex) {
+ resourceMessage(ex);
+ return EXIT_SYSERR;
+ } catch (FatalError ex) {
+ feMessage(ex);
+ return EXIT_SYSERR;
+ } catch (sun.misc.ServiceConfigurationError sce) {
+ sceMessage(sce);
+ return EXIT_ABNORMAL;
+ } catch (Throwable ex) {
+ bugMessage(ex);
+ return EXIT_ABNORMAL;
+ }
+
+
+ boolean firstRound = true;
+ boolean needSourcePath = false;
+ boolean needClassPath = false;
+ boolean classesAsDecls = options.get("-XclassesAsDecls") != null;
+
+ /*
+ * Create augumented classpath and sourcepath values.
+ *
+ * If any of the prior apt rounds generated any new source
+ * files, the n'th apt round (and any javac invocation) has the
+ * source destination path ("-s path") as the last element of
+ * the "-sourcepath" to the n'th call.
+ *
+ * If any of the prior apt rounds generated any new class files,
+ * the n'th apt round (and any javac invocation) has the class
+ * destination path ("-d path") as the last element of the
+ * "-classpath" to the n'th call.
+ */
+ String augmentedSourcePath = "";
+ String augmentedClassPath = "";
+ String baseClassPath = "";
+
+ try {
+ /*
+ * Record original options for future annotation processor
+ * invocations.
+ */
+ origOptions = new HashMap(options.size());
+ for(String s: options.keySet()) {
+ String value;
+ if (s.equals(value = options.get(s)))
+ origOptions.put(s, (String)null);
+ else
+ origOptions.put(s, value);
+ }
+ origOptions = Collections.unmodifiableMap(origOptions);
+
+ {
+ // Note: it might be necessary to check for an empty
+ // component ("") of the source path or class path
+ Paths paths = Paths.instance(context);
+
+ String sourceDest = options.get("-s");
+ if (paths.sourcePath() != null) {
+ for(File f: paths.sourcePath())
+ augmentedSourcePath += (f + File.pathSeparator);
+ augmentedSourcePath += (sourceDest == null)?".":sourceDest;
+ } else {
+ augmentedSourcePath = ".";
+
+ if (sourceDest != null)
+ augmentedSourcePath += (File.pathSeparator + sourceDest);
+ }
+
+ String classDest = options.get("-d");
+ if (paths.userClassPath() != null) {
+ for(File f: paths.userClassPath())
+ baseClassPath += (f + File.pathSeparator);
+ // put baseClassPath into map to handle any
+ // value needed for the classloader
+ options.put("-classpath", baseClassPath);
+
+ augmentedClassPath = baseClassPath + ((classDest == null)?".":classDest);
+ } else {
+ baseClassPath = ".";
+ if (classDest != null)
+ augmentedClassPath = baseClassPath + (File.pathSeparator + classDest);
+ }
+ assert options.get("-classpath") != null;
+ }
+
+ /*
+ * Create base and augmented class loaders
+ */
+ ClassLoader augmentedAptCL = null;
+ {
+ /*
+ * Use a url class loader to look for classes on the
+ * user-specified class path. Prepend computed bootclass
+ * path, which includes extdirs, to the URLClassLoader apt
+ * uses.
+ */
+ String aptclasspath = "";
+ Paths paths = Paths.instance(context);
+ String bcp = "";
+ Collection bootclasspath = paths.bootClassPath();
+
+ if (bootclasspath != null) {
+ for(File f: bootclasspath)
+ bcp += (f + File.pathSeparator);
+ }
+
+ // If the factory path is set, use that path
+ if (providedFactory == null)
+ aptclasspath = options.get("-factorypath");
+ if (aptclasspath == null)
+ aptclasspath = options.get("-classpath");
+
+ assert aptclasspath != null;
+ aptclasspath = (bcp + aptclasspath);
+ aptCL = new URLClassLoader(pathToURLs(aptclasspath));
+
+ if (providedFactory == null &&
+ options.get("-factorypath") != null) // same CL even if new class files written
+ augmentedAptCL = aptCL;
+ else {
+ // Create class loader in case new class files are
+ // written
+ augmentedAptCL = new URLClassLoader(pathToURLs(augmentedClassPath.
+ substring(baseClassPath.length())),
+ aptCL);
+ }
+ }
+
+ int round = 0; // For -XPrintAptRounds
+ do {
+ round++;
+
+ Context newContext = new Context();
+ Options newOptions = Options.instance(newContext); // creates a new context
+ newOptions.putAll(options);
+
+ // populate with old options... don't bother reparsing command line, etc.
+
+ // if genSource files, must add destination to source path
+ if (genSourceFileNames.size() > 0 && !firstRound) {
+ newOptions.put("-sourcepath", augmentedSourcePath);
+ needSourcePath = true;
+ }
+ aggregateGenSourceFileNames.addAll(genSourceFileNames);
+ sourceFileNames.addAll(genSourceFileNames);
+ genSourceFileNames.clear();
+
+ // Don't really need to track this; just have to add -d
+ // "foo" to class path if any class files are generated
+ if (genClassFileNames.size() > 0) {
+ newOptions.put("-classpath", augmentedClassPath);
+ aptCL = augmentedAptCL;
+ needClassPath = true;
+ }
+ aggregateGenClassFileNames.addAll(genClassFileNames);
+ classFileNames.addAll(genClassFileNames);
+ genClassFileNames.clear();
+
+ options = newOptions;
+
+ if (options.get("-XPrintAptRounds") != null) {
+ out.println("apt Round : " + round);
+ out.println("filenames: " + sourceFileNames);
+ if (classesAsDecls)
+ out.println("classnames: " + classFileNames);
+ out.println("options: " + options);
+ }
+
+ returnCode = compile(args, newContext);
+ firstRound = false;
+
+ // Check for reported errors before continuing
+ bark = Bark.instance(newContext);
+ } while(((genSourceFileNames.size() != 0 ) ||
+ (classesAsDecls && genClassFileNames.size() != 0)) &&
+ bark.nerrors == 0);
+ } catch (UsageMessageNeededException umne) {
+ help();
+ return EXIT_CMDERR; // will cause usage message to be printed
+ }
+
+ /*
+ * Do not compile if a processor has reported an error or if
+ * there are no source files to process. A more sophisticated
+ * test would also fail for syntax errors caught by javac.
+ */
+ if (options.get("-nocompile") == null &&
+ options.get("-print") == null &&
+ bark.nerrors == 0 &&
+ (origFilenames.size() > 0 || aggregateGenSourceFileNames.size() > 0 )) {
+ /*
+ * Need to create new argument string for calling javac:
+ * 1. apt specific arguments (e.g. -factory) must be stripped out
+ * 2. proper settings for sourcepath and classpath must be used
+ * 3. generated class names must be added
+ * 4. class file names as declarations must be removed
+ */
+
+ int newArgsLength = args.length +
+ (needSourcePath?1:0) +
+ (needClassPath?1:0) +
+ aggregateGenSourceFileNames.size();
+
+ // Null out apt-specific options and don't copy over into
+ // newArgs. This loop should be a lot faster; the options
+ // array should be replaced with a better data structure
+ // which includes a map from strings to options.
+ //
+ // If treating classes as declarations, must strip out
+ // class names from the javac argument list
+ argLoop:
+ for(int i = 0; i < args.length; i++) {
+ int matchPosition = -1;
+
+ // "-A" by itself is recognized by apt but not javac
+ if (args[i] != null && args[i].equals("-A")) {
+ newArgsLength--;
+ args[i] = null;
+ continue argLoop;
+ } else {
+ optionLoop:
+ for(int j = 0; j < recognizedOptions.length; j++) {
+ if (args[i] != null && recognizedOptions[j].matches(args[i])) {
+ matchPosition = j;
+ break optionLoop;
+ }
+ }
+
+ if (matchPosition != -1) {
+ Option op = recognizedOptions[matchPosition];
+ if (op.aptOnly) {
+ newArgsLength--;
+ args[i] = null;
+ if (op.hasArg()) {
+ newArgsLength--;
+ args[i+1] = null;
+ }
+ } else {
+ if (op.hasArg()) { // skip over next string
+ i++;
+ continue argLoop;
+ }
+
+ if ((options.get("-XclassesAsDecls") != null) &&
+ (matchPosition == (recognizedOptions.length-1)) ){
+ // Remove class file names from
+ // consideration by javac.
+ if (! args[i].endsWith(".java")) {
+ newArgsLength--;
+ args[i] = null;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ String newArgs[] = new String[newArgsLength];
+
+ int j = 0;
+ for(int i=0; i < args.length; i++) {
+ if (args[i] != null)
+ newArgs[j++] = args[i];
+ }
+
+ if (needClassPath)
+ newArgs[j++] = "-XD-classpath=" + augmentedClassPath;
+
+ if (needSourcePath) {
+ newArgs[j++] = "-XD-sourcepath=" + augmentedSourcePath;
+
+ for(String s: aggregateGenSourceFileNames)
+ newArgs[j++] = s;
+ }
+
+ returnCode = com.sun.tools.javac.Main.compile(newArgs);
+ }
+
+ return returnCode;
+ }
+
+ /** Programmatic interface for main function.
+ * @param args The command line parameters.
+ */
+ int compile(String[] args, Context context) {
+ boolean assertionsEnabled = false;
+ assert assertionsEnabled = true;
+ if (!assertionsEnabled) {
+ // Bark.printLines(out, "fatal error: assertions must be enabled when running javac");
+ // return EXIT_ABNORMAL;
+ }
+ int exitCode = EXIT_OK;
+
+ JavaCompiler comp = null;
+ try {
+ context.put(Bark.outKey, out);
+
+ comp = JavaCompiler.instance(context);
+ if (comp == null)
+ return EXIT_SYSERR;
+
+ java.util.List nameList = new java.util.LinkedList();
+ nameList.addAll(sourceFileNames);
+ if (options.get("-XclassesAsDecls") != null)
+ nameList.addAll(classFileNames);
+
+ List cs
+ = comp.compile(List.from(nameList.toArray(new String[0])),
+ origOptions,
+ aptCL,
+ providedFactory,
+ productiveFactories,
+ aggregateGenFiles);
+
+ /*
+ * If there aren't new source files, we shouldn't bother
+ * running javac if there were errors.
+ *
+ * If there are new files, we should try running javac in
+ * case there were typing errors.
+ *
+ */
+
+ if (comp.errorCount() != 0 ||
+ options.get("-Werror") != null && comp.warningCount() != 0)
+ return EXIT_ERROR;
+ } catch (IOException ex) {
+ ioMessage(ex);
+ return EXIT_SYSERR;
+ } catch (OutOfMemoryError ex) {
+ resourceMessage(ex);
+ return EXIT_SYSERR;
+ } catch (StackOverflowError ex) {
+ resourceMessage(ex);
+ return EXIT_SYSERR;
+ } catch (FatalError ex) {
+ feMessage(ex);
+ return EXIT_SYSERR;
+ } catch (UsageMessageNeededException umne) {
+ help();
+ return EXIT_CMDERR; // will cause usage message to be printed
+ } catch (AnnotationProcessingError ex) {
+ apMessage(ex);
+ return EXIT_ABNORMAL;
+ } catch (sun.misc.ServiceConfigurationError sce) {
+ sceMessage(sce);
+ return EXIT_ABNORMAL;
+ } catch (Throwable ex) {
+ bugMessage(ex);
+ return EXIT_ABNORMAL;
+ } finally {
+ if (comp != null) {
+ comp.close();
+ genSourceFileNames.addAll(comp.getSourceFileNames());
+ genClassFileNames.addAll(comp.getClassFileNames());
+ }
+ sourceFileNames = new java.util.LinkedList();
+ classFileNames = new java.util.LinkedList();
+ }
+ return exitCode;
+ }
+
+ /** Print a message reporting an internal error.
+ */
+ void bugMessage(Throwable ex) {
+ Bark.printLines(out, getLocalizedString("msg.bug",
+ JavaCompiler.version()));
+ ex.printStackTrace(out);
+ }
+
+ /** Print a message reporting an fatal error.
+ */
+ void apMessage(AnnotationProcessingError ex) {
+ Bark.printLines(out, getLocalizedString("misc.Problem"));
+ ex.getCause().printStackTrace(out);
+ }
+
+ /** Print a message about sun.misc.Service problem.
+ */
+ void sceMessage(sun.misc.ServiceConfigurationError ex) {
+ Bark.printLines(out, getLocalizedString("misc.SunMiscService"));
+ ex.printStackTrace(out);
+ }
+
+ /** Print a message reporting an fatal error.
+ */
+ void feMessage(Throwable ex) {
+ Bark.printLines(out, ex.toString());
+ }
+
+ /** Print a message reporting an input/output error.
+ */
+ void ioMessage(Throwable ex) {
+ Bark.printLines(out, getLocalizedString("msg.io"));
+ ex.printStackTrace(out);
+ }
+
+ /** Print a message reporting an out-of-resources error.
+ */
+ void resourceMessage(Throwable ex) {
+ Bark.printLines(out, getLocalizedString("msg.resource"));
+ ex.printStackTrace(out);
+ }
+
+ /* ************************************************************************
+ * Internationalization
+ *************************************************************************/
+
+ /** Find a localized string in the resource bundle.
+ * @param key The key for the localized string.
+ */
+ private static String getLocalizedString(String key, Object... args) {
+ return getText(key, args);
+ }
+
+ private static final String javacRB =
+ "com.sun.tools.javac.resources.javac";
+
+ private static final String aptRB =
+ "com.sun.tools.apt.resources.apt";
+
+ private static ResourceBundle messageRBjavac;
+ private static ResourceBundle messageRBapt;
+
+ /** Initialize ResourceBundle.
+ */
+ private static void initResource() {
+ try {
+ messageRBapt = ResourceBundle.getBundle(aptRB);
+ messageRBjavac = ResourceBundle.getBundle(javacRB);
+ } catch (MissingResourceException e) {
+ Error x = new FatalError("Fatal Error: Resource for apt or javac is missing");
+ x.initCause(e);
+ throw x;
+ }
+ }
+
+ /** Get and format message string from resource.
+ */
+ private static String getText(String key, Object... _args) {
+ String[] args = new String[_args.length];
+ for (int i=0; i<_args.length; i++) {
+ args[i] = "" + _args[i];
+ }
+ if (messageRBapt == null || messageRBjavac == null )
+ initResource();
+ try {
+ return MessageFormat.format(messageRBapt.getString("apt." + key),
+ (Object[]) args);
+ } catch (MissingResourceException e) {
+ try {
+ return MessageFormat.format(messageRBjavac.getString("javac." + key),
+ (Object[]) args);
+ } catch (MissingResourceException f) {
+ String msg = "apt or javac message file broken: key={0} "
+ + "arguments={1}, {2}";
+ return MessageFormat.format(msg, (Object[]) args);
+ }
+ }
+ }
+
+ // Borrowed from DocletInvoker
+ /**
+ * Utility method for converting a search path string to an array
+ * of directory and JAR file URLs.
+ *
+ * @param path the search path string
+ * @return the resulting array of directory and JAR file URLs
+ */
+ static URL[] pathToURLs(String path) {
+ StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
+ URL[] urls = new URL[st.countTokens()];
+ int count = 0;
+ while (st.hasMoreTokens()) {
+ URL url = fileToURL(new File(st.nextToken()));
+ if (url != null) {
+ urls[count++] = url;
+ }
+ }
+ if (urls.length != count) {
+ URL[] tmp = new URL[count];
+ System.arraycopy(urls, 0, tmp, 0, count);
+ urls = tmp;
+ }
+ return urls;
+ }
+
+ /**
+ * Returns the directory or JAR file URL corresponding to the specified
+ * local file name.
+ *
+ * @param file the File object
+ * @return the resulting directory or JAR file URL, or null if unknown
+ */
+ static URL fileToURL(File file) {
+ String name;
+ try {
+ name = file.getCanonicalPath();
+ } catch (IOException e) {
+ name = file.getAbsolutePath();
+ }
+ name = name.replace(File.separatorChar, '/');
+ if (!name.startsWith("/")) {
+ name = "/" + name;
+ }
+ // If the file does not exist, then assume that it's a directory
+ if (!file.isFile()) {
+ name = name + "/";
+ }
+ try {
+ return new URL("file", "", name);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("file");
+ }
+ }
+}