Wed, 20 May 2009 13:36:23 -0700
6827026: Change javac source and target default to 7
Reviewed-by: darcy, ohair
1 /*
2 * Copyright 1999-2009 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.CacheFSInfo;
35 import com.sun.tools.javac.file.JavacFileManager;
36 import com.sun.tools.javac.jvm.Target;
37 import com.sun.tools.javac.main.JavacOption.Option;
38 import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
39 import com.sun.tools.javac.util.*;
40 import com.sun.tools.javac.processing.AnnotationProcessingError;
41 import javax.tools.JavaFileManager;
42 import javax.tools.JavaFileObject;
43 import javax.annotation.processing.Processor;
45 /** This class provides a commandline interface to the GJC compiler.
46 *
47 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
48 * you write code that depends on this, you do so at your own risk.
49 * This code and its internal interfaces are subject to change or
50 * deletion without notice.</b>
51 */
52 public class Main {
54 /** The name of the compiler, for use in diagnostics.
55 */
56 String ownName;
58 /** The writer to use for diagnostic output.
59 */
60 PrintWriter out;
62 /**
63 * If true, any command line arg errors will cause an exception.
64 */
65 boolean fatalErrors;
67 /** Result codes.
68 */
69 static final int
70 EXIT_OK = 0, // Compilation completed with no errors.
71 EXIT_ERROR = 1, // Completed but reported errors.
72 EXIT_CMDERR = 2, // Bad command-line arguments
73 EXIT_SYSERR = 3, // System error or resource exhaustion.
74 EXIT_ABNORMAL = 4; // Compiler terminated abnormally
76 private Option[] recognizedOptions = RecognizedOptions.getJavaCompilerOptions(new OptionHelper() {
78 public void setOut(PrintWriter out) {
79 Main.this.out = out;
80 }
82 public void error(String key, Object... args) {
83 Main.this.error(key, args);
84 }
86 public void printVersion() {
87 Log.printLines(out, getLocalizedString("version", ownName, JavaCompiler.version()));
88 }
90 public void printFullVersion() {
91 Log.printLines(out, getLocalizedString("fullVersion", ownName, JavaCompiler.fullVersion()));
92 }
94 public void printHelp() {
95 help();
96 }
98 public void printXhelp() {
99 xhelp();
100 }
102 public void addFile(File f) {
103 if (!filenames.contains(f))
104 filenames.append(f);
105 }
107 public void addClassName(String s) {
108 classnames.append(s);
109 }
111 });
113 /**
114 * Construct a compiler instance.
115 */
116 public Main(String name) {
117 this(name, new PrintWriter(System.err, true));
118 }
120 /**
121 * Construct a compiler instance.
122 */
123 public Main(String name, PrintWriter out) {
124 this.ownName = name;
125 this.out = out;
126 }
127 /** A table of all options that's passed to the JavaCompiler constructor. */
128 private Options options = null;
130 /** The list of source files to process
131 */
132 public ListBuffer<File> filenames = null; // XXX sb protected
134 /** List of class files names passed on the command line
135 */
136 public ListBuffer<String> classnames = null; // XXX sb protected
138 /** Print a string that explains usage.
139 */
140 void help() {
141 Log.printLines(out, getLocalizedString("msg.usage.header", ownName));
142 for (int i=0; i<recognizedOptions.length; i++) {
143 recognizedOptions[i].help(out);
144 }
145 out.println();
146 }
148 /** Print a string that explains usage for X options.
149 */
150 void xhelp() {
151 for (int i=0; i<recognizedOptions.length; i++) {
152 recognizedOptions[i].xhelp(out);
153 }
154 out.println();
155 Log.printLines(out, getLocalizedString("msg.usage.nonstandard.footer"));
156 }
158 /** Report a usage error.
159 */
160 void error(String key, Object... args) {
161 if (fatalErrors) {
162 String msg = getLocalizedString(key, args);
163 throw new PropagatedException(new IllegalStateException(msg));
164 }
165 warning(key, args);
166 Log.printLines(out, getLocalizedString("msg.usage", ownName));
167 }
169 /** Report a warning.
170 */
171 void warning(String key, Object... args) {
172 Log.printLines(out, ownName + ": "
173 + getLocalizedString(key, args));
174 }
176 public Option getOption(String flag) {
177 for (Option option : recognizedOptions) {
178 if (option.matches(flag))
179 return option;
180 }
181 return null;
182 }
184 public void setOptions(Options options) {
185 if (options == null)
186 throw new NullPointerException();
187 this.options = options;
188 }
190 public void setFatalErrors(boolean fatalErrors) {
191 this.fatalErrors = fatalErrors;
192 }
194 /** Process command line arguments: store all command line options
195 * in `options' table and return all source filenames.
196 * @param flags The array of command line arguments.
197 */
198 public List<File> processArgs(String[] flags) { // XXX sb protected
199 int ac = 0;
200 while (ac < flags.length) {
201 String flag = flags[ac];
202 ac++;
204 Option option = null;
206 if (flag.length() > 0) {
207 // quick hack to speed up file processing:
208 // if the option does not begin with '-', there is no need to check
209 // most of the compiler options.
210 int firstOptionToCheck = flag.charAt(0) == '-' ? 0 : recognizedOptions.length-1;
211 for (int j=firstOptionToCheck; j<recognizedOptions.length; j++) {
212 if (recognizedOptions[j].matches(flag)) {
213 option = recognizedOptions[j];
214 break;
215 }
216 }
217 }
219 if (option == null) {
220 error("err.invalid.flag", flag);
221 return null;
222 }
224 if (option.hasArg()) {
225 if (ac == flags.length) {
226 error("err.req.arg", flag);
227 return null;
228 }
229 String operand = flags[ac];
230 ac++;
231 if (option.process(options, flag, operand))
232 return null;
233 } else {
234 if (option.process(options, flag))
235 return null;
236 }
237 }
239 if (!checkDirectory("-d"))
240 return null;
241 if (!checkDirectory("-s"))
242 return null;
244 String sourceString = options.get("-source");
245 Source source = (sourceString != null)
246 ? Source.lookup(sourceString)
247 : Source.DEFAULT;
248 String targetString = options.get("-target");
249 Target target = (targetString != null)
250 ? Target.lookup(targetString)
251 : Target.DEFAULT;
252 // We don't check source/target consistency for CLDC, as J2ME
253 // profiles are not aligned with J2SE targets; moreover, a
254 // single CLDC target may have many profiles. In addition,
255 // this is needed for the continued functioning of the JSR14
256 // prototype.
257 if (Character.isDigit(target.name.charAt(0))) {
258 if (target.compareTo(source.requiredTarget()) < 0) {
259 if (targetString != null) {
260 if (sourceString == null) {
261 warning("warn.target.default.source.conflict",
262 targetString,
263 source.requiredTarget().name);
264 } else {
265 warning("warn.source.target.conflict",
266 sourceString,
267 source.requiredTarget().name);
268 }
269 return null;
270 } else {
271 target = source.requiredTarget();
272 options.put("-target", target.name);
273 }
274 } else {
275 if (targetString == null && !source.allowGenerics()) {
276 target = Target.JDK1_4;
277 options.put("-target", target.name);
278 }
279 }
280 }
281 if (target.hasInvokedynamic()) {
282 options.put("invokedynamic", "invokedynamic");
283 }
284 return filenames.toList();
285 }
286 // where
287 private boolean checkDirectory(String optName) {
288 String value = options.get(optName);
289 if (value == null)
290 return true;
291 File file = new File(value);
292 if (!file.exists()) {
293 error("err.dir.not.found", value);
294 return false;
295 }
296 if (!file.isDirectory()) {
297 error("err.file.not.directory", value);
298 return false;
299 }
300 return true;
301 }
303 /** Programmatic interface for main function.
304 * @param args The command line parameters.
305 */
306 public int compile(String[] args) {
307 Context context = new Context();
308 JavacFileManager.preRegister(context); // can't create it until Log has been set up
309 int result = compile(args, context);
310 if (fileManager instanceof JavacFileManager) {
311 // A fresh context was created above, so jfm must be a JavacFileManager
312 ((JavacFileManager)fileManager).close();
313 }
314 return result;
315 }
317 public int compile(String[] args, Context context) {
318 return compile(args, context, List.<JavaFileObject>nil(), null);
319 }
321 /** Programmatic interface for main function.
322 * @param args The command line parameters.
323 */
324 public int compile(String[] args,
325 Context context,
326 List<JavaFileObject> fileObjects,
327 Iterable<? extends Processor> processors)
328 {
329 if (options == null)
330 options = Options.instance(context); // creates a new one
332 filenames = new ListBuffer<File>();
333 classnames = new ListBuffer<String>();
334 JavaCompiler comp = null;
335 /*
336 * TODO: Logic below about what is an acceptable command line
337 * should be updated to take annotation processing semantics
338 * into account.
339 */
340 try {
341 if (args.length == 0 && fileObjects.isEmpty()) {
342 help();
343 return EXIT_CMDERR;
344 }
346 List<File> files;
347 try {
348 files = processArgs(CommandLine.parse(args));
349 if (files == null) {
350 // null signals an error in options, abort
351 return EXIT_CMDERR;
352 } else if (files.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) {
353 // it is allowed to compile nothing if just asking for help or version info
354 if (options.get("-help") != null
355 || options.get("-X") != null
356 || options.get("-version") != null
357 || options.get("-fullversion") != null)
358 return EXIT_OK;
359 error("err.no.source.files");
360 return EXIT_CMDERR;
361 }
362 } catch (java.io.FileNotFoundException e) {
363 Log.printLines(out, ownName + ": " +
364 getLocalizedString("err.file.not.found",
365 e.getMessage()));
366 return EXIT_SYSERR;
367 }
369 boolean forceStdOut = options.get("stdout") != null;
370 if (forceStdOut) {
371 out.flush();
372 out = new PrintWriter(System.out, true);
373 }
375 context.put(Log.outKey, out);
377 // allow System property in following line as a Mustang legacy
378 boolean batchMode = (options.get("nonBatchMode") == null
379 && System.getProperty("nonBatchMode") == null);
380 if (batchMode)
381 CacheFSInfo.preRegister(context);
383 fileManager = context.get(JavaFileManager.class);
385 comp = JavaCompiler.instance(context);
386 if (comp == null) return EXIT_SYSERR;
388 Log log = Log.instance(context);
390 if (!files.isEmpty()) {
391 // add filenames to fileObjects
392 comp = JavaCompiler.instance(context);
393 List<JavaFileObject> otherFiles = List.nil();
394 JavacFileManager dfm = (JavacFileManager)fileManager;
395 for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files))
396 otherFiles = otherFiles.prepend(fo);
397 for (JavaFileObject fo : otherFiles)
398 fileObjects = fileObjects.prepend(fo);
399 }
400 comp.compile(fileObjects,
401 classnames.toList(),
402 processors);
404 if (log.expectDiagKeys != null) {
405 if (log.expectDiagKeys.size() == 0) {
406 Log.printLines(log.noticeWriter, "all expected diagnostics found");
407 return EXIT_OK;
408 } else {
409 Log.printLines(log.noticeWriter, "expected diagnostic keys not found: " + log.expectDiagKeys);
410 return EXIT_ERROR;
411 }
412 }
414 if (comp.errorCount() != 0)
415 return EXIT_ERROR;
416 } catch (IOException ex) {
417 ioMessage(ex);
418 return EXIT_SYSERR;
419 } catch (OutOfMemoryError ex) {
420 resourceMessage(ex);
421 return EXIT_SYSERR;
422 } catch (StackOverflowError ex) {
423 resourceMessage(ex);
424 return EXIT_SYSERR;
425 } catch (FatalError ex) {
426 feMessage(ex);
427 return EXIT_SYSERR;
428 } catch(AnnotationProcessingError ex) {
429 apMessage(ex);
430 return EXIT_SYSERR;
431 } catch (ClientCodeException ex) {
432 // as specified by javax.tools.JavaCompiler#getTask
433 // and javax.tools.JavaCompiler.CompilationTask#call
434 throw new RuntimeException(ex.getCause());
435 } catch (PropagatedException ex) {
436 throw ex.getCause();
437 } catch (Throwable ex) {
438 // Nasty. If we've already reported an error, compensate
439 // for buggy compiler error recovery by swallowing thrown
440 // exceptions.
441 if (comp == null || comp.errorCount() == 0 ||
442 options == null || options.get("dev") != null)
443 bugMessage(ex);
444 return EXIT_ABNORMAL;
445 } finally {
446 if (comp != null) comp.close();
447 filenames = null;
448 options = null;
449 }
450 return EXIT_OK;
451 }
453 /** Print a message reporting an internal error.
454 */
455 void bugMessage(Throwable ex) {
456 Log.printLines(out, getLocalizedString("msg.bug",
457 JavaCompiler.version()));
458 ex.printStackTrace(out);
459 }
461 /** Print a message reporting an fatal error.
462 */
463 void feMessage(Throwable ex) {
464 Log.printLines(out, ex.getMessage());
465 }
467 /** Print a message reporting an input/output error.
468 */
469 void ioMessage(Throwable ex) {
470 Log.printLines(out, getLocalizedString("msg.io"));
471 ex.printStackTrace(out);
472 }
474 /** Print a message reporting an out-of-resources error.
475 */
476 void resourceMessage(Throwable ex) {
477 Log.printLines(out, getLocalizedString("msg.resource"));
478 // System.out.println("(name buffer len = " + Name.names.length + " " + Name.nc);//DEBUG
479 ex.printStackTrace(out);
480 }
482 /** Print a message reporting an uncaught exception from an
483 * annotation processor.
484 */
485 void apMessage(AnnotationProcessingError ex) {
486 Log.printLines(out,
487 getLocalizedString("msg.proc.annotation.uncaught.exception"));
488 ex.getCause().printStackTrace();
489 }
491 private JavaFileManager fileManager;
493 /* ************************************************************************
494 * Internationalization
495 *************************************************************************/
497 /** Find a localized string in the resource bundle.
498 * @param key The key for the localized string.
499 */
500 public static String getLocalizedString(String key, Object... args) { // FIXME sb private
501 try {
502 if (messages == null)
503 messages = new JavacMessages(javacBundleName);
504 return messages.getLocalizedString("javac." + key, args);
505 }
506 catch (MissingResourceException e) {
507 throw new Error("Fatal Error: Resource for javac is missing", e);
508 }
509 }
511 public static void useRawMessages(boolean enable) {
512 if (enable) {
513 messages = new JavacMessages(javacBundleName) {
514 public String getLocalizedString(String key, Object... args) {
515 return key;
516 }
517 };
518 } else {
519 messages = new JavacMessages(javacBundleName);
520 }
521 }
523 private static final String javacBundleName =
524 "com.sun.tools.javac.resources.javac";
526 private static JavacMessages messages;
527 }