Sat, 19 Nov 2011 15:54:04 -0800
7110611: compiler message file broken for javac -fullversion
Reviewed-by: jjg
1 /*
2 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * 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.net.URL;
32 import java.security.DigestInputStream;
33 import java.security.MessageDigest;
34 import java.util.Collection;
35 import java.util.LinkedHashSet;
36 import java.util.Set;
37 import javax.tools.JavaFileManager;
38 import javax.tools.JavaFileObject;
39 import javax.annotation.processing.Processor;
41 import com.sun.tools.javac.code.Source;
42 import com.sun.tools.javac.file.CacheFSInfo;
43 import com.sun.tools.javac.file.JavacFileManager;
44 import com.sun.tools.javac.jvm.Target;
45 import com.sun.tools.javac.main.JavacOption.Option;
46 import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
47 import com.sun.tools.javac.util.*;
48 import com.sun.tools.javac.util.Log.WriterKind;
49 import com.sun.tools.javac.util.Log.PrefixKind;
50 import com.sun.tools.javac.processing.AnnotationProcessingError;
52 import static com.sun.tools.javac.main.OptionName.*;
54 /** This class provides a commandline interface to the GJC compiler.
55 *
56 * <p><b>This is NOT part of any supported API.
57 * If you write code that depends on this, you do so at your own risk.
58 * This code and its internal interfaces are subject to change or
59 * deletion without notice.</b>
60 */
61 public class Main {
63 /** The name of the compiler, for use in diagnostics.
64 */
65 String ownName;
67 /** The writer to use for diagnostic output.
68 */
69 PrintWriter out;
71 /** The log to use for diagnostic output.
72 */
73 Log log;
75 /**
76 * If true, certain errors will cause an exception, such as command line
77 * arg errors, or exceptions in user provided code.
78 */
79 boolean apiMode;
82 /** Result codes.
83 */
84 public enum Result {
85 OK(0), // Compilation completed with no errors.
86 ERROR(1), // Completed but reported errors.
87 CMDERR(2), // Bad command-line arguments
88 SYSERR(3), // System error or resource exhaustion.
89 ABNORMAL(4); // Compiler terminated abnormally
91 Result(int exitCode) {
92 this.exitCode = exitCode;
93 }
95 public boolean isOK() {
96 return (exitCode == 0);
97 }
99 public final int exitCode;
100 }
102 private Option[] recognizedOptions = RecognizedOptions.getJavaCompilerOptions(new OptionHelper() {
104 public void setOut(PrintWriter out) {
105 Main.this.out = out;
106 Main.this.log.setWriters(out);
107 }
109 public void error(String key, Object... args) {
110 Main.this.error(key, args);
111 }
113 public void printVersion() {
114 log.printLines(PrefixKind.JAVAC, "version", ownName, JavaCompiler.version());
115 }
117 public void printFullVersion() {
118 log.printLines(PrefixKind.JAVAC, "fullVersion", ownName, JavaCompiler.fullVersion());
119 }
121 public void printHelp() {
122 help();
123 }
125 public void printXhelp() {
126 xhelp();
127 }
129 public void addFile(File f) {
130 filenames.add(f);
131 }
133 public void addClassName(String s) {
134 classnames.append(s);
135 }
137 });
139 /**
140 * Construct a compiler instance.
141 */
142 public Main(String name) {
143 this(name, new PrintWriter(System.err, true));
144 }
146 /**
147 * Construct a compiler instance.
148 */
149 public Main(String name, PrintWriter out) {
150 this.ownName = name;
151 this.out = out;
152 }
153 /** A table of all options that's passed to the JavaCompiler constructor. */
154 private Options options = null;
156 /** The list of source files to process
157 */
158 public Set<File> filenames = null; // XXX sb protected
160 /** List of class files names passed on the command line
161 */
162 public ListBuffer<String> classnames = null; // XXX sb protected
164 /** Print a string that explains usage.
165 */
166 void help() {
167 log.printLines(PrefixKind.JAVAC, "msg.usage.header", ownName);
168 for (int i=0; i<recognizedOptions.length; i++) {
169 recognizedOptions[i].help(log);
170 }
171 log.printNewline();
172 }
174 /** Print a string that explains usage for X options.
175 */
176 void xhelp() {
177 for (int i=0; i<recognizedOptions.length; i++) {
178 recognizedOptions[i].xhelp(log);
179 }
180 log.printNewline();
181 log.printLines(PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
182 }
184 /** Report a usage error.
185 */
186 void error(String key, Object... args) {
187 if (apiMode) {
188 String msg = log.localize(PrefixKind.JAVAC, key, args);
189 throw new PropagatedException(new IllegalStateException(msg));
190 }
191 warning(key, args);
192 log.printLines(PrefixKind.JAVAC, "msg.usage", ownName);
193 }
195 /** Report a warning.
196 */
197 void warning(String key, Object... args) {
198 log.printRawLines(ownName + ": " + log.localize(PrefixKind.JAVAC, key, args));
199 }
201 public Option getOption(String flag) {
202 for (Option option : recognizedOptions) {
203 if (option.matches(flag))
204 return option;
205 }
206 return null;
207 }
209 public void setOptions(Options options) {
210 if (options == null)
211 throw new NullPointerException();
212 this.options = options;
213 }
215 public void setAPIMode(boolean apiMode) {
216 this.apiMode = apiMode;
217 }
219 /** Process command line arguments: store all command line options
220 * in `options' table and return all source filenames.
221 * @param flags The array of command line arguments.
222 */
223 public Collection<File> processArgs(String[] flags) { // XXX sb protected
224 int ac = 0;
225 while (ac < flags.length) {
226 String flag = flags[ac];
227 ac++;
229 Option option = null;
231 if (flag.length() > 0) {
232 // quick hack to speed up file processing:
233 // if the option does not begin with '-', there is no need to check
234 // most of the compiler options.
235 int firstOptionToCheck = flag.charAt(0) == '-' ? 0 : recognizedOptions.length-1;
236 for (int j=firstOptionToCheck; j<recognizedOptions.length; j++) {
237 if (recognizedOptions[j].matches(flag)) {
238 option = recognizedOptions[j];
239 break;
240 }
241 }
242 }
244 if (option == null) {
245 error("err.invalid.flag", flag);
246 return null;
247 }
249 if (option.hasArg()) {
250 if (ac == flags.length) {
251 error("err.req.arg", flag);
252 return null;
253 }
254 String operand = flags[ac];
255 ac++;
256 if (option.process(options, flag, operand))
257 return null;
258 } else {
259 if (option.process(options, flag))
260 return null;
261 }
262 }
264 if (!checkDirectory(D))
265 return null;
266 if (!checkDirectory(S))
267 return null;
269 String sourceString = options.get(SOURCE);
270 Source source = (sourceString != null)
271 ? Source.lookup(sourceString)
272 : Source.DEFAULT;
273 String targetString = options.get(TARGET);
274 Target target = (targetString != null)
275 ? Target.lookup(targetString)
276 : Target.DEFAULT;
277 // We don't check source/target consistency for CLDC, as J2ME
278 // profiles are not aligned with J2SE targets; moreover, a
279 // single CLDC target may have many profiles. In addition,
280 // this is needed for the continued functioning of the JSR14
281 // prototype.
282 if (Character.isDigit(target.name.charAt(0))) {
283 if (target.compareTo(source.requiredTarget()) < 0) {
284 if (targetString != null) {
285 if (sourceString == null) {
286 warning("warn.target.default.source.conflict",
287 targetString,
288 source.requiredTarget().name);
289 } else {
290 warning("warn.source.target.conflict",
291 sourceString,
292 source.requiredTarget().name);
293 }
294 return null;
295 } else {
296 target = source.requiredTarget();
297 options.put("-target", target.name);
298 }
299 } else {
300 if (targetString == null && !source.allowGenerics()) {
301 target = Target.JDK1_4;
302 options.put("-target", target.name);
303 }
304 }
305 }
307 // handle this here so it works even if no other options given
308 String showClass = options.get("showClass");
309 if (showClass != null) {
310 if (showClass.equals("showClass")) // no value given for option
311 showClass = "com.sun.tools.javac.Main";
312 showClass(showClass);
313 }
315 options.notifyListeners();
317 return filenames;
318 }
319 // where
320 private boolean checkDirectory(OptionName optName) {
321 String value = options.get(optName);
322 if (value == null)
323 return true;
324 File file = new File(value);
325 if (!file.exists()) {
326 error("err.dir.not.found", value);
327 return false;
328 }
329 if (!file.isDirectory()) {
330 error("err.file.not.directory", value);
331 return false;
332 }
333 return true;
334 }
336 /** Programmatic interface for main function.
337 * @param args The command line parameters.
338 */
339 public Result compile(String[] args) {
340 Context context = new Context();
341 JavacFileManager.preRegister(context); // can't create it until Log has been set up
342 Result result = compile(args, context);
343 if (fileManager instanceof JavacFileManager) {
344 // A fresh context was created above, so jfm must be a JavacFileManager
345 ((JavacFileManager)fileManager).close();
346 }
347 return result;
348 }
350 public Result compile(String[] args, Context context) {
351 return compile(args, context, List.<JavaFileObject>nil(), null);
352 }
354 /** Programmatic interface for main function.
355 * @param args The command line parameters.
356 */
357 public Result compile(String[] args,
358 Context context,
359 List<JavaFileObject> fileObjects,
360 Iterable<? extends Processor> processors)
361 {
362 context.put(Log.outKey, out);
363 log = Log.instance(context);
365 if (options == null)
366 options = Options.instance(context); // creates a new one
368 filenames = new LinkedHashSet<File>();
369 classnames = new ListBuffer<String>();
370 JavaCompiler comp = null;
371 /*
372 * TODO: Logic below about what is an acceptable command line
373 * should be updated to take annotation processing semantics
374 * into account.
375 */
376 try {
377 if (args.length == 0 && fileObjects.isEmpty()) {
378 help();
379 return Result.CMDERR;
380 }
382 Collection<File> files;
383 try {
384 files = processArgs(CommandLine.parse(args));
385 if (files == null) {
386 // null signals an error in options, abort
387 return Result.CMDERR;
388 } else if (files.isEmpty() && fileObjects.isEmpty() && classnames.isEmpty()) {
389 // it is allowed to compile nothing if just asking for help or version info
390 if (options.isSet(HELP)
391 || options.isSet(X)
392 || options.isSet(VERSION)
393 || options.isSet(FULLVERSION))
394 return Result.OK;
395 if (JavaCompiler.explicitAnnotationProcessingRequested(options)) {
396 error("err.no.source.files.classes");
397 } else {
398 error("err.no.source.files");
399 }
400 return Result.CMDERR;
401 }
402 } catch (java.io.FileNotFoundException e) {
403 warning("err.file.not.found", e.getMessage());
404 return Result.SYSERR;
405 }
407 boolean forceStdOut = options.isSet("stdout");
408 if (forceStdOut) {
409 log.flush();
410 out = new PrintWriter(System.out, true);
411 log.setWriters(out);
412 }
414 // allow System property in following line as a Mustang legacy
415 boolean batchMode = (options.isUnset("nonBatchMode")
416 && System.getProperty("nonBatchMode") == null);
417 if (batchMode)
418 CacheFSInfo.preRegister(context);
420 fileManager = context.get(JavaFileManager.class);
422 comp = JavaCompiler.instance(context);
423 if (comp == null) return Result.SYSERR;
425 if (!files.isEmpty()) {
426 // add filenames to fileObjects
427 comp = JavaCompiler.instance(context);
428 List<JavaFileObject> otherFiles = List.nil();
429 JavacFileManager dfm = (JavacFileManager)fileManager;
430 for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files))
431 otherFiles = otherFiles.prepend(fo);
432 for (JavaFileObject fo : otherFiles)
433 fileObjects = fileObjects.prepend(fo);
434 }
435 comp.compile(fileObjects,
436 classnames.toList(),
437 processors);
439 if (log.expectDiagKeys != null) {
440 if (log.expectDiagKeys.isEmpty()) {
441 log.printRawLines("all expected diagnostics found");
442 return Result.OK;
443 } else {
444 log.printRawLines("expected diagnostic keys not found: " + log.expectDiagKeys);
445 return Result.ERROR;
446 }
447 }
449 if (comp.errorCount() != 0)
450 return Result.ERROR;
451 } catch (IOException ex) {
452 ioMessage(ex);
453 return Result.SYSERR;
454 } catch (OutOfMemoryError ex) {
455 resourceMessage(ex);
456 return Result.SYSERR;
457 } catch (StackOverflowError ex) {
458 resourceMessage(ex);
459 return Result.SYSERR;
460 } catch (FatalError ex) {
461 feMessage(ex);
462 return Result.SYSERR;
463 } catch (AnnotationProcessingError ex) {
464 if (apiMode)
465 throw new RuntimeException(ex.getCause());
466 apMessage(ex);
467 return Result.SYSERR;
468 } catch (ClientCodeException ex) {
469 // as specified by javax.tools.JavaCompiler#getTask
470 // and javax.tools.JavaCompiler.CompilationTask#call
471 throw new RuntimeException(ex.getCause());
472 } catch (PropagatedException ex) {
473 throw ex.getCause();
474 } catch (Throwable ex) {
475 // Nasty. If we've already reported an error, compensate
476 // for buggy compiler error recovery by swallowing thrown
477 // exceptions.
478 if (comp == null || comp.errorCount() == 0 ||
479 options == null || options.isSet("dev"))
480 bugMessage(ex);
481 return Result.ABNORMAL;
482 } finally {
483 if (comp != null) {
484 try {
485 comp.close();
486 } catch (ClientCodeException ex) {
487 throw new RuntimeException(ex.getCause());
488 }
489 }
490 filenames = null;
491 options = null;
492 }
493 return Result.OK;
494 }
496 /** Print a message reporting an internal error.
497 */
498 void bugMessage(Throwable ex) {
499 log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version());
500 ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
501 }
503 /** Print a message reporting a fatal error.
504 */
505 void feMessage(Throwable ex) {
506 log.printRawLines(ex.getMessage());
507 if (ex.getCause() != null && options.isSet("dev")) {
508 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE));
509 }
510 }
512 /** Print a message reporting an input/output error.
513 */
514 void ioMessage(Throwable ex) {
515 log.printLines(PrefixKind.JAVAC, "msg.io");
516 ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
517 }
519 /** Print a message reporting an out-of-resources error.
520 */
521 void resourceMessage(Throwable ex) {
522 log.printLines(PrefixKind.JAVAC, "msg.resource");
523 ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
524 }
526 /** Print a message reporting an uncaught exception from an
527 * annotation processor.
528 */
529 void apMessage(AnnotationProcessingError ex) {
530 log.printLines("msg.proc.annotation.uncaught.exception");
531 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE));
532 }
534 /** Display the location and checksum of a class. */
535 void showClass(String className) {
536 PrintWriter pw = log.getWriter(WriterKind.NOTICE);
537 pw.println("javac: show class: " + className);
538 URL url = getClass().getResource('/' + className.replace('.', '/') + ".class");
539 if (url == null)
540 pw.println(" class not found");
541 else {
542 pw.println(" " + url);
543 try {
544 final String algorithm = "MD5";
545 byte[] digest;
546 MessageDigest md = MessageDigest.getInstance(algorithm);
547 DigestInputStream in = new DigestInputStream(url.openStream(), md);
548 try {
549 byte[] buf = new byte[8192];
550 int n;
551 do { n = in.read(buf); } while (n > 0);
552 digest = md.digest();
553 } finally {
554 in.close();
555 }
556 StringBuilder sb = new StringBuilder();
557 for (byte b: digest)
558 sb.append(String.format("%02x", b));
559 pw.println(" " + algorithm + " checksum: " + sb);
560 } catch (Exception e) {
561 pw.println(" cannot compute digest: " + e);
562 }
563 }
564 }
566 private JavaFileManager fileManager;
568 /* ************************************************************************
569 * Internationalization
570 *************************************************************************/
572 // /** Find a localized string in the resource bundle.
573 // * @param key The key for the localized string.
574 // */
575 // public static String getLocalizedString(String key, Object... args) { // FIXME sb private
576 // try {
577 // if (messages == null)
578 // messages = new JavacMessages(javacBundleName);
579 // return messages.getLocalizedString("javac." + key, args);
580 // }
581 // catch (MissingResourceException e) {
582 // throw new Error("Fatal Error: Resource for javac is missing", e);
583 // }
584 // }
585 //
586 // public static void useRawMessages(boolean enable) {
587 // if (enable) {
588 // messages = new JavacMessages(javacBundleName) {
589 // @Override
590 // public String getLocalizedString(String key, Object... args) {
591 // return key;
592 // }
593 // };
594 // } else {
595 // messages = new JavacMessages(javacBundleName);
596 // }
597 // }
599 public static final String javacBundleName =
600 "com.sun.tools.javac.resources.javac";
601 //
602 // private static JavacMessages messages;
603 }