duke@1: /*
ohair@798: * Copyright (c) 2004, 2010, Oracle and/or its affiliates. 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
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle 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: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * 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:
darcy@497: import static com.sun.tools.javac.file.Paths.pathToURLs;
darcy@497:
duke@1: /** This class provides a commandline interface to the apt build-time
duke@1: * tool.
duke@1: *
jjg@581: *
This is NOT part of any supported API.
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) {
jjg@789: Bark.printLines(out, ownName + " " + AptJavaCompiler.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 extends File> 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:
jjg@789: AptJavaCompiler comp = null;
duke@1: try {
duke@1: context.put(Bark.outKey, out);
duke@1:
jjg@789: comp = AptJavaCompiler.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",
jjg@789: AptJavaCompiler.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: }