001: /*
002: * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javac.main;
027:
028: import com.sun.tools.javac.util.Options;
029: import java.io.File;
030: import java.io.IOException;
031: import java.io.PrintWriter;
032: import java.util.MissingResourceException;
033:
034: import com.sun.tools.javac.code.Source;
035: import com.sun.tools.javac.jvm.Target;
036: import com.sun.tools.javac.main.JavacOption.Option;
037: import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
038: import com.sun.tools.javac.util.*;
039: import com.sun.tools.javac.processing.AnnotationProcessingError;
040: import javax.tools.JavaFileManager;
041: import javax.tools.JavaFileObject;
042: import javax.annotation.processing.Processor;
043:
044: /** This class provides a commandline interface to the GJC compiler.
045: *
046: * <p><b>This is NOT part of any API supported by Sun Microsystems. If
047: * you write code that depends on this, you do so at your own risk.
048: * This code and its internal interfaces are subject to change or
049: * deletion without notice.</b>
050: */
051: @Version("@(#)Main.java 1.119 07/05/05")
052: public class Main {
053:
054: /** The name of the compiler, for use in diagnostics.
055: */
056: String ownName;
057:
058: /** The writer to use for diagnostic output.
059: */
060: PrintWriter out;
061:
062: /**
063: * If true, any command line arg errors will cause an exception.
064: */
065: boolean fatalErrors;
066:
067: /** Result codes.
068: */
069: static final int EXIT_OK = 0, // Compilation completed with no errors.
070: EXIT_ERROR = 1, // Completed but reported errors.
071: EXIT_CMDERR = 2, // Bad command-line arguments
072: EXIT_SYSERR = 3, // System error or resource exhaustion.
073: EXIT_ABNORMAL = 4; // Compiler terminated abnormally
074:
075: private Option[] recognizedOptions = RecognizedOptions
076: .getJavaCompilerOptions(new OptionHelper() {
077:
078: public void setOut(PrintWriter out) {
079: Main.this .out = out;
080: }
081:
082: public void error(String key, Object... args) {
083: Main.this .error(key, args);
084: }
085:
086: public void printVersion() {
087: Log.printLines(out, getLocalizedString("version",
088: ownName, JavaCompiler.version()));
089: }
090:
091: public void printFullVersion() {
092: Log.printLines(out, getLocalizedString(
093: "fullVersion", ownName, JavaCompiler
094: .fullVersion()));
095: }
096:
097: public void printHelp() {
098: help();
099: }
100:
101: public void printXhelp() {
102: xhelp();
103: }
104:
105: public void addFile(File f) {
106: if (!filenames.contains(f))
107: filenames.append(f);
108: }
109:
110: public void addClassName(String s) {
111: classnames.append(s);
112: }
113:
114: });
115:
116: /**
117: * Construct a compiler instance.
118: */
119: public Main(String name) {
120: this (name, new PrintWriter(System.err, true));
121: }
122:
123: /**
124: * Construct a compiler instance.
125: */
126: public Main(String name, PrintWriter out) {
127: this .ownName = name;
128: this .out = out;
129: }
130:
131: /** A table of all options that's passed to the JavaCompiler constructor. */
132: private Options options = null;
133:
134: /** The list of source files to process
135: */
136: public ListBuffer<File> filenames = null; // XXX sb protected
137:
138: /** List of class files names passed on the command line
139: */
140: public ListBuffer<String> classnames = null; // XXX sb protected
141:
142: /** Print a string that explains usage.
143: */
144: void help() {
145: Log.printLines(out, getLocalizedString("msg.usage.header",
146: ownName));
147: for (int i = 0; i < recognizedOptions.length; i++) {
148: recognizedOptions[i].help(out);
149: }
150: out.println();
151: }
152:
153: /** Print a string that explains usage for X options.
154: */
155: void xhelp() {
156: for (int i = 0; i < recognizedOptions.length; i++) {
157: recognizedOptions[i].xhelp(out);
158: }
159: out.println();
160: Log.printLines(out,
161: getLocalizedString("msg.usage.nonstandard.footer"));
162: }
163:
164: /** Report a usage error.
165: */
166: void error(String key, Object... args) {
167: if (fatalErrors) {
168: String msg = getLocalizedString(key, args);
169: throw new PropagatedException(
170: new IllegalStateException(msg));
171: }
172: warning(key, args);
173: Log.printLines(out, getLocalizedString("msg.usage", ownName));
174: }
175:
176: /** Report a warning.
177: */
178: void warning(String key, Object... args) {
179: Log.printLines(out, ownName + ": "
180: + getLocalizedString(key, args));
181: }
182:
183: public Option getOption(String flag) {
184: for (Option option : recognizedOptions) {
185: if (option.matches(flag))
186: return option;
187: }
188: return null;
189: }
190:
191: public void setOptions(Options options) {
192: if (options == null)
193: throw new NullPointerException();
194: this .options = options;
195: }
196:
197: public void setFatalErrors(boolean fatalErrors) {
198: this .fatalErrors = fatalErrors;
199: }
200:
201: /** Process command line arguments: store all command line options
202: * in `options' table and return all source filenames.
203: * @param flags The array of command line arguments.
204: */
205: public List<File> processArgs(String[] flags) { // XXX sb protected
206: int ac = 0;
207: while (ac < flags.length) {
208: String flag = flags[ac];
209: ac++;
210:
211: int j;
212: // quick hack to speed up file processing:
213: // if the option does not begin with '-', there is no need to check
214: // most of the compiler options.
215: int firstOptionToCheck = flag.charAt(0) == '-' ? 0
216: : recognizedOptions.length - 1;
217: for (j = firstOptionToCheck; j < recognizedOptions.length; j++)
218: if (recognizedOptions[j].matches(flag))
219: break;
220:
221: if (j == recognizedOptions.length) {
222: error("err.invalid.flag", flag);
223: return null;
224: }
225:
226: Option option = recognizedOptions[j];
227: if (option.hasArg()) {
228: if (ac == flags.length) {
229: error("err.req.arg", flag);
230: return null;
231: }
232: String operand = flags[ac];
233: ac++;
234: if (option.process(options, flag, operand))
235: return null;
236: } else {
237: if (option.process(options, flag))
238: return null;
239: }
240: }
241:
242: if (!checkDirectory("-d"))
243: return null;
244: if (!checkDirectory("-s"))
245: return null;
246:
247: String sourceString = options.get("-source");
248: Source source = (sourceString != null) ? Source
249: .lookup(sourceString) : Source.DEFAULT;
250: String targetString = options.get("-target");
251: Target target = (targetString != null) ? Target
252: .lookup(targetString) : Target.DEFAULT;
253: // We don't check source/target consistency for CLDC, as J2ME
254: // profiles are not aligned with J2SE targets; moreover, a
255: // single CLDC target may have many profiles. In addition,
256: // this is needed for the continued functioning of the JSR14
257: // prototype.
258: if (Character.isDigit(target.name.charAt(0))) {
259: if (target.compareTo(source.requiredTarget()) < 0) {
260: if (targetString != null) {
261: if (sourceString == null) {
262: warning("warn.target.default.source.conflict",
263: targetString,
264: source.requiredTarget().name);
265: } else {
266: warning("warn.source.target.conflict",
267: sourceString,
268: source.requiredTarget().name);
269: }
270: return null;
271: } else {
272: options
273: .put("-target",
274: source.requiredTarget().name);
275: }
276: } else {
277: if (targetString == null && !source.allowGenerics()) {
278: options.put("-target", Target.JDK1_4.name);
279: }
280: }
281: }
282: return filenames.toList();
283: }
284:
285: // where
286: private boolean checkDirectory(String optName) {
287: String value = options.get(optName);
288: if (value == null)
289: return true;
290: File file = new File(value);
291: if (!file.exists()) {
292: error("err.dir.not.found", value);
293: return false;
294: }
295: if (!file.isDirectory()) {
296: error("err.file.not.directory", value);
297: return false;
298: }
299: return true;
300: }
301:
302: /** Programmatic interface for main function.
303: * @param args The command line parameters.
304: */
305: public int compile(String[] args) {
306: Context context = new Context();
307: JavacFileManager.preRegister(context); // can't create it until Log has been set up
308: int result = compile(args, context);
309: if (fileManager instanceof JavacFileManager) {
310: // A fresh context was created above, so jfm must be a JavacFileManager
311: ((JavacFileManager) fileManager).close();
312: }
313: return result;
314: }
315:
316: public int compile(String[] args, Context context) {
317: return compile(args, context, List.<JavaFileObject> nil(), null);
318: }
319:
320: /** Programmatic interface for main function.
321: * @param args The command line parameters.
322: */
323: public int compile(String[] args, Context context,
324: List<JavaFileObject> fileObjects,
325: Iterable<? extends Processor> processors) {
326: if (options == null)
327: options = Options.instance(context); // creates a new one
328:
329: filenames = new ListBuffer<File>();
330: classnames = new ListBuffer<String>();
331: JavaCompiler comp = null;
332: /*
333: * TODO: Logic below about what is an acceptable command line
334: * should be updated to take annotation processing semantics
335: * into account.
336: */
337: try {
338: if (args.length == 0 && fileObjects.isEmpty()) {
339: help();
340: return EXIT_CMDERR;
341: }
342:
343: List<File> filenames;
344: try {
345: filenames = processArgs(CommandLine.parse(args));
346: if (filenames == null) {
347: // null signals an error in options, abort
348: return EXIT_CMDERR;
349: } else if (filenames.isEmpty() && fileObjects.isEmpty()
350: && classnames.isEmpty()) {
351: // it is allowed to compile nothing if just asking for help or version info
352: if (options.get("-help") != null
353: || options.get("-X") != null
354: || options.get("-version") != null
355: || options.get("-fullversion") != null)
356: return EXIT_OK;
357: error("err.no.source.files");
358: return EXIT_CMDERR;
359: }
360: } catch (java.io.FileNotFoundException e) {
361: Log.printLines(out, ownName
362: + ": "
363: + getLocalizedString("err.file.not.found", e
364: .getMessage()));
365: return EXIT_SYSERR;
366: }
367:
368: boolean forceStdOut = options.get("stdout") != null;
369: if (forceStdOut) {
370: out.flush();
371: out = new PrintWriter(System.out, true);
372: }
373:
374: context.put(Log.outKey, out);
375:
376: fileManager = context.get(JavaFileManager.class);
377:
378: comp = JavaCompiler.instance(context);
379: if (comp == null)
380: return EXIT_SYSERR;
381:
382: if (!filenames.isEmpty()) {
383: // add filenames to fileObjects
384: comp = JavaCompiler.instance(context);
385: List<JavaFileObject> otherFiles = List.nil();
386: JavacFileManager dfm = (JavacFileManager) fileManager;
387: for (JavaFileObject fo : dfm
388: .getJavaFileObjectsFromFiles(filenames))
389: otherFiles = otherFiles.prepend(fo);
390: for (JavaFileObject fo : otherFiles)
391: fileObjects = fileObjects.prepend(fo);
392: }
393: comp.compile(fileObjects, classnames.toList(), processors);
394:
395: if (comp.errorCount() != 0
396: || options.get("-Werror") != null
397: && comp.warningCount() != 0)
398: return EXIT_ERROR;
399: } catch (IOException ex) {
400: ioMessage(ex);
401: return EXIT_SYSERR;
402: } catch (OutOfMemoryError ex) {
403: resourceMessage(ex);
404: return EXIT_SYSERR;
405: } catch (StackOverflowError ex) {
406: resourceMessage(ex);
407: return EXIT_SYSERR;
408: } catch (FatalError ex) {
409: feMessage(ex);
410: return EXIT_SYSERR;
411: } catch (AnnotationProcessingError ex) {
412: apMessage(ex);
413: return EXIT_SYSERR;
414: } catch (ClientCodeException ex) {
415: // as specified by javax.tools.JavaCompiler#getTask
416: // and javax.tools.JavaCompiler.CompilationTask#call
417: throw new RuntimeException(ex.getCause());
418: } catch (PropagatedException ex) {
419: throw ex.getCause();
420: } catch (Throwable ex) {
421: // Nasty. If we've already reported an error, compensate
422: // for buggy compiler error recovery by swallowing thrown
423: // exceptions.
424: if (comp == null || comp.errorCount() == 0
425: || options == null || options.get("dev") != null)
426: bugMessage(ex);
427: return EXIT_ABNORMAL;
428: } finally {
429: if (comp != null)
430: comp.close();
431: filenames = null;
432: options = null;
433: }
434: return EXIT_OK;
435: }
436:
437: /** Print a message reporting an internal error.
438: */
439: void bugMessage(Throwable ex) {
440: Log.printLines(out, getLocalizedString("msg.bug", JavaCompiler
441: .version()));
442: ex.printStackTrace(out);
443: }
444:
445: /** Print a message reporting an fatal error.
446: */
447: void feMessage(Throwable ex) {
448: Log.printLines(out, ex.getMessage());
449: }
450:
451: /** Print a message reporting an input/output error.
452: */
453: void ioMessage(Throwable ex) {
454: Log.printLines(out, getLocalizedString("msg.io"));
455: ex.printStackTrace(out);
456: }
457:
458: /** Print a message reporting an out-of-resources error.
459: */
460: void resourceMessage(Throwable ex) {
461: Log.printLines(out, getLocalizedString("msg.resource"));
462: // System.out.println("(name buffer len = " + Name.names.length + " " + Name.nc);//DEBUG
463: ex.printStackTrace(out);
464: }
465:
466: /** Print a message reporting an uncaught exception from an
467: * annotation processor.
468: */
469: void apMessage(AnnotationProcessingError ex) {
470: Log
471: .printLines(
472: out,
473: getLocalizedString("msg.proc.annotation.uncaught.exception"));
474: ex.getCause().printStackTrace();
475: }
476:
477: private JavaFileManager fileManager;
478:
479: /* ************************************************************************
480: * Internationalization
481: *************************************************************************/
482:
483: /** Find a localized string in the resource bundle.
484: * @param key The key for the localized string.
485: */
486: public static String getLocalizedString(String key, Object... args) { // FIXME sb private
487: try {
488: if (messages == null)
489: messages = new Messages(javacBundleName);
490: return messages.getLocalizedString("javac." + key, args);
491: } catch (MissingResourceException e) {
492: throw new Error(
493: "Fatal Error: Resource for javac is missing", e);
494: }
495: }
496:
497: public static void useRawMessages(boolean enable) {
498: if (enable) {
499: messages = new Messages(javacBundleName) {
500: public String getLocalizedString(String key,
501: Object... args) {
502: return key;
503: }
504: };
505: } else {
506: messages = new Messages(javacBundleName);
507: }
508: }
509:
510: private static final String javacBundleName = "com.sun.tools.javac.resources.javac";
511:
512: private static Messages messages;
513: }
|