duke@1: /* xdono@54: * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.apt.main; duke@1: duke@1: import java.io.File; duke@1: import java.io.FileWriter; duke@1: import java.io.IOException; duke@1: import java.io.PrintWriter; duke@1: import java.text.MessageFormat; duke@1: import java.util.ResourceBundle; duke@1: import java.util.MissingResourceException; duke@1: import java.util.StringTokenizer; duke@1: import java.util.Map; duke@1: import java.util.HashMap; duke@1: import java.util.Collections; duke@1: duke@1: import java.net.URLClassLoader; duke@1: import java.net.URL; duke@1: import java.net.MalformedURLException; duke@1: jjg@285: import javax.tools.JavaFileManager; jjg@285: import javax.tools.StandardLocation; jjg@285: jjg@285: import com.sun.tools.javac.file.JavacFileManager; duke@1: import com.sun.tools.javac.code.Source; duke@1: import com.sun.tools.javac.code.Symbol; duke@1: import com.sun.tools.javac.code.Type; duke@1: import com.sun.tools.javac.jvm.Target; duke@1: import com.sun.tools.javac.util.*; duke@1: duke@1: import com.sun.tools.apt.comp.AnnotationProcessingError; duke@1: import com.sun.tools.apt.comp.UsageMessageNeededException; duke@1: import com.sun.tools.apt.util.Bark; duke@1: import com.sun.mirror.apt.AnnotationProcessorFactory; duke@1: duke@1: /** This class provides a commandline interface to the apt build-time duke@1: * tool. duke@1: * duke@1: *

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