src/share/classes/com/sun/tools/javac/main/Main.java

Tue, 29 Mar 2011 16:41:18 +0100

author
mcimadamore
date
Tue, 29 Mar 2011 16:41:18 +0100
changeset 951
de1c65ecfec2
parent 946
31e5cfc5a990
child 1055
7337295434b6
permissions
-rw-r--r--

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

mercurial