Mon, 16 Jun 2008 13:28:00 -0700
6714364: refactor javac File handling code into new javac.file package
Reviewed-by: mcimadamore
1 /*
2 * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javac.main;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.util.MissingResourceException;
33 import com.sun.tools.javac.code.Source;
34 import com.sun.tools.javac.file.JavacFileManager;
35 import com.sun.tools.javac.jvm.Target;
36 import com.sun.tools.javac.main.JavacOption.Option;
37 import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
38 import com.sun.tools.javac.util.*;
39 import com.sun.tools.javac.processing.AnnotationProcessingError;
40 import javax.tools.JavaFileManager;
41 import javax.tools.JavaFileObject;
42 import javax.annotation.processing.Processor;
44 /** This class provides a commandline interface to the GJC compiler.
45 *
46 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
47 * you write code that depends on this, you do so at your own risk.
48 * This code and its internal interfaces are subject to change or
49 * deletion without notice.</b>
50 */
51 public class Main {
53 /** The name of the compiler, for use in diagnostics.
54 */
55 String ownName;
57 /** The writer to use for diagnostic output.
58 */
59 PrintWriter out;
61 /**
62 * If true, any command line arg errors will cause an exception.
63 */
64 boolean fatalErrors;
66 /** Result codes.
67 */
68 static final int
69 EXIT_OK = 0, // Compilation completed with no errors.
70 EXIT_ERROR = 1, // Completed but reported errors.
71 EXIT_CMDERR = 2, // Bad command-line arguments
72 EXIT_SYSERR = 3, // System error or resource exhaustion.
73 EXIT_ABNORMAL = 4; // Compiler terminated abnormally
75 private Option[] recognizedOptions = RecognizedOptions.getJavaCompilerOptions(new OptionHelper() {
77 public void setOut(PrintWriter out) {
78 Main.this.out = out;
79 }
81 public void error(String key, Object... args) {
82 Main.this.error(key, args);
83 }
85 public void printVersion() {
86 Log.printLines(out, getLocalizedString("version", ownName, JavaCompiler.version()));
87 }
89 public void printFullVersion() {
90 Log.printLines(out, getLocalizedString("fullVersion", ownName, JavaCompiler.fullVersion()));
91 }
93 public void printHelp() {
94 help();
95 }
97 public void printXhelp() {
98 xhelp();
99 }
101 public void addFile(File f) {
102 if (!filenames.contains(f))
103 filenames.append(f);
104 }
106 public void addClassName(String s) {
107 classnames.append(s);
108 }
110 });
112 /**
113 * Construct a compiler instance.
114 */
115 public Main(String name) {
116 this(name, new PrintWriter(System.err, true));
117 }
119 /**
120 * Construct a compiler instance.
121 */
122 public Main(String name, PrintWriter out) {
123 this.ownName = name;
124 this.out = out;
125 }
126 /** A table of all options that's passed to the JavaCompiler constructor. */
127 private Options options = null;
129 /** The list of source files to process
130 */
131 public ListBuffer<File> filenames = null; // XXX sb protected
133 /** List of class files names passed on the command line
134 */
135 public ListBuffer<String> classnames = null; // XXX sb protected
137 /** Print a string that explains usage.
138 */
139 void help() {
140 Log.printLines(out, getLocalizedString("msg.usage.header", ownName));
141 for (int i=0; i<recognizedOptions.length; i++) {
142 recognizedOptions[i].help(out);
143 }
144 out.println();
145 }
147 /** Print a string that explains usage for X options.
148 */
149 void xhelp() {
150 for (int i=0; i<recognizedOptions.length; i++) {
151 recognizedOptions[i].xhelp(out);
152 }
153 out.println();
154 Log.printLines(out, getLocalizedString("msg.usage.nonstandard.footer"));
155 }
157 /** Report a usage error.
158 */
159 void error(String key, Object... args) {
160 if (fatalErrors) {
161 String msg = getLocalizedString(key, args);
162 throw new PropagatedException(new IllegalStateException(msg));
163 }
164 warning(key, args);
165 Log.printLines(out, getLocalizedString("msg.usage", ownName));
166 }
168 /** Report a warning.
169 */
170 void warning(String key, Object... args) {
171 Log.printLines(out, ownName + ": "
172 + getLocalizedString(key, args));
173 }
175 public Option getOption(String flag) {
176 for (Option option : recognizedOptions) {
177 if (option.matches(flag))
178 return option;
179 }
180 return null;
181 }
183 public void setOptions(Options options) {
184 if (options == null)
185 throw new NullPointerException();
186 this.options = options;
187 }
189 public void setFatalErrors(boolean fatalErrors) {
190 this.fatalErrors = fatalErrors;
191 }
193 /** Process command line arguments: store all command line options
194 * in `options' table and return all source filenames.
195 * @param flags The array of command line arguments.
196 */
197 public List<File> processArgs(String[] flags) { // XXX sb protected
198 int ac = 0;
199 while (ac < flags.length) {
200 String flag = flags[ac];
201 ac++;
203 Option option = null;
205 if (flag.length() > 0) {
206 // quick hack to speed up file processing:
207 // if the option does not begin with '-', there is no need to check
208 // most of the compiler options.
209 int firstOptionToCheck = flag.charAt(0) == '-' ? 0 : recognizedOptions.length-1;
210 for (int j=firstOptionToCheck; j<recognizedOptions.length; j++) {
211 if (recognizedOptions[j].matches(flag)) {
212 option = recognizedOptions[j];
213 break;
214 }
215 }
216 }
218 if (option == null) {
219 error("err.invalid.flag", flag);
220 return null;
221 }
223 if (option.hasArg()) {
224 if (ac == flags.length) {
225 error("err.req.arg", flag);
226 return null;
227 }
228 String operand = flags[ac];
229 ac++;
230 if (option.process(options, flag, operand))
231 return null;
232 } else {
233 if (option.process(options, flag))
234 return null;
235 }
236 }
238 if (!checkDirectory("-d"))
239 return null;
240 if (!checkDirectory("-s"))
241 return null;
243 String sourceString = options.get("-source");
244 Source source = (sourceString != null)
245 ? Source.lookup(sourceString)
246 : Source.DEFAULT;
247 String targetString = options.get("-target");
248 Target target = (targetString != null)
249 ? Target.lookup(targetString)
250 : Target.DEFAULT;
251 // We don't check source/target consistency for CLDC, as J2ME
252 // profiles are not aligned with J2SE targets; moreover, a
253 // single CLDC target may have many profiles. In addition,
254 // this is needed for the continued functioning of the JSR14
255 // prototype.
256 if (Character.isDigit(target.name.charAt(0))) {
257 if (target.compareTo(source.requiredTarget()) < 0) {
258 if (targetString != null) {
259 if (sourceString == null) {
260 warning("warn.target.default.source.conflict",
261 targetString,
262 source.requiredTarget().name);
263 } else {
264 warning("warn.source.target.conflict",
265 sourceString,
266 source.requiredTarget().name);
267 }
268 return null;
269 } else {
270 options.put("-target", source.requiredTarget().name);
271 }
272 } else {
273 if (targetString == null && !source.allowGenerics()) {
274 options.put("-target", Target.JDK1_4.name);
275 }
276 }
277 }
278 return filenames.toList();
279 }
280 // where
281 private boolean checkDirectory(String optName) {
282 String value = options.get(optName);
283 if (value == null)
284 return true;
285 File file = new File(value);
286 if (!file.exists()) {
287 error("err.dir.not.found", value);
288 return false;
289 }
290 if (!file.isDirectory()) {
291 error("err.file.not.directory", value);
292 return false;
293 }
294 return true;
295 }
297 /** Programmatic interface for main function.
298 * @param args The command line parameters.
299 */
300 public int compile(String[] args) {
301 Context context = new Context();
302 JavacFileManager.preRegister(context); // can't create it until Log has been set up
303 int result = compile(args, context);
304 if (fileManager instanceof JavacFileManager) {
305 // A fresh context was created above, so jfm must be a JavacFileManager
306 ((JavacFileManager)fileManager).close();
307 }
308 return result;
309 }
311 public int compile(String[] args, Context context) {
312 return compile(args, context, List.<JavaFileObject>nil(), null);
313 }
315 /** Programmatic interface for main function.
316 * @param args The command line parameters.
317 */
318 public int compile(String[] args,
319 Context context,
320 List<JavaFileObject> fileObjects,
321 Iterable<? extends Processor> processors)
322 {
323 if (options == null)
324 options = Options.instance(context); // creates a new one
326 filenames = new ListBuffer<File>();
327 classnames = new ListBuffer<String>();
328 JavaCompiler comp = null;
329 /*
330 * TODO: Logic below about what is an acceptable command line
331 * should be updated to take annotation processing semantics
332 * into account.
333 */
334 try {
335 if (args.length == 0 && fileObjects.isEmpty()) {
336 help();
337 return EXIT_CMDERR;
338 }
340 List<File> filenames;
341 try {
342 filenames = processArgs(CommandLine.parse(args));
343 if (filenames == null) {
344 // null signals an error in options, abort
345 return EXIT_CMDERR;
346 } else if (filenames.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) {
347 // it is allowed to compile nothing if just asking for help or version info
348 if (options.get("-help") != null
349 || options.get("-X") != null
350 || options.get("-version") != null
351 || options.get("-fullversion") != null)
352 return EXIT_OK;
353 error("err.no.source.files");
354 return EXIT_CMDERR;
355 }
356 } catch (java.io.FileNotFoundException e) {
357 Log.printLines(out, ownName + ": " +
358 getLocalizedString("err.file.not.found",
359 e.getMessage()));
360 return EXIT_SYSERR;
361 }
363 boolean forceStdOut = options.get("stdout") != null;
364 if (forceStdOut) {
365 out.flush();
366 out = new PrintWriter(System.out, true);
367 }
369 context.put(Log.outKey, out);
371 fileManager = context.get(JavaFileManager.class);
373 comp = JavaCompiler.instance(context);
374 if (comp == null) return EXIT_SYSERR;
376 if (!filenames.isEmpty()) {
377 // add filenames to fileObjects
378 comp = JavaCompiler.instance(context);
379 List<JavaFileObject> otherFiles = List.nil();
380 JavacFileManager dfm = (JavacFileManager)fileManager;
381 for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(filenames))
382 otherFiles = otherFiles.prepend(fo);
383 for (JavaFileObject fo : otherFiles)
384 fileObjects = fileObjects.prepend(fo);
385 }
386 comp.compile(fileObjects,
387 classnames.toList(),
388 processors);
390 if (comp.errorCount() != 0 ||
391 options.get("-Werror") != null && comp.warningCount() != 0)
392 return EXIT_ERROR;
393 } catch (IOException ex) {
394 ioMessage(ex);
395 return EXIT_SYSERR;
396 } catch (OutOfMemoryError ex) {
397 resourceMessage(ex);
398 return EXIT_SYSERR;
399 } catch (StackOverflowError ex) {
400 resourceMessage(ex);
401 return EXIT_SYSERR;
402 } catch (FatalError ex) {
403 feMessage(ex);
404 return EXIT_SYSERR;
405 } catch(AnnotationProcessingError ex) {
406 apMessage(ex);
407 return EXIT_SYSERR;
408 } catch (ClientCodeException ex) {
409 // as specified by javax.tools.JavaCompiler#getTask
410 // and javax.tools.JavaCompiler.CompilationTask#call
411 throw new RuntimeException(ex.getCause());
412 } catch (PropagatedException ex) {
413 throw ex.getCause();
414 } catch (Throwable ex) {
415 // Nasty. If we've already reported an error, compensate
416 // for buggy compiler error recovery by swallowing thrown
417 // exceptions.
418 if (comp == null || comp.errorCount() == 0 ||
419 options == null || options.get("dev") != null)
420 bugMessage(ex);
421 return EXIT_ABNORMAL;
422 } finally {
423 if (comp != null) comp.close();
424 filenames = null;
425 options = null;
426 }
427 return EXIT_OK;
428 }
430 /** Print a message reporting an internal error.
431 */
432 void bugMessage(Throwable ex) {
433 Log.printLines(out, getLocalizedString("msg.bug",
434 JavaCompiler.version()));
435 ex.printStackTrace(out);
436 }
438 /** Print a message reporting an fatal error.
439 */
440 void feMessage(Throwable ex) {
441 Log.printLines(out, ex.getMessage());
442 }
444 /** Print a message reporting an input/output error.
445 */
446 void ioMessage(Throwable ex) {
447 Log.printLines(out, getLocalizedString("msg.io"));
448 ex.printStackTrace(out);
449 }
451 /** Print a message reporting an out-of-resources error.
452 */
453 void resourceMessage(Throwable ex) {
454 Log.printLines(out, getLocalizedString("msg.resource"));
455 // System.out.println("(name buffer len = " + Name.names.length + " " + Name.nc);//DEBUG
456 ex.printStackTrace(out);
457 }
459 /** Print a message reporting an uncaught exception from an
460 * annotation processor.
461 */
462 void apMessage(AnnotationProcessingError ex) {
463 Log.printLines(out,
464 getLocalizedString("msg.proc.annotation.uncaught.exception"));
465 ex.getCause().printStackTrace();
466 }
468 private JavaFileManager fileManager;
470 /* ************************************************************************
471 * Internationalization
472 *************************************************************************/
474 /** Find a localized string in the resource bundle.
475 * @param key The key for the localized string.
476 */
477 public static String getLocalizedString(String key, Object... args) { // FIXME sb private
478 try {
479 if (messages == null)
480 messages = new Messages(javacBundleName);
481 return messages.getLocalizedString("javac." + key, args);
482 }
483 catch (MissingResourceException e) {
484 throw new Error("Fatal Error: Resource for javac is missing", e);
485 }
486 }
488 public static void useRawMessages(boolean enable) {
489 if (enable) {
490 messages = new Messages(javacBundleName) {
491 public String getLocalizedString(String key, Object... args) {
492 return key;
493 }
494 };
495 } else {
496 messages = new Messages(javacBundleName);
497 }
498 }
500 private static final String javacBundleName =
501 "com.sun.tools.javac.resources.javac";
503 private static Messages messages;
504 }