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"); + } + } +}