Main.java revision 3294:9adfb22ff08f
1/* 2 * Copyright (c) 1999, 2015, 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 */ 25 26package com.sun.tools.javac.main; 27 28import java.io.FileNotFoundException; 29import java.io.IOException; 30import java.io.InputStream; 31import java.io.PrintWriter; 32import java.net.URL; 33import java.nio.file.NoSuchFileException; 34import java.security.DigestInputStream; 35import java.security.MessageDigest; 36import java.security.NoSuchAlgorithmException; 37import java.util.Set; 38 39import javax.tools.JavaFileManager; 40 41import com.sun.tools.javac.api.BasicJavacTask; 42import com.sun.tools.javac.file.CacheFSInfo; 43import com.sun.tools.javac.file.BaseFileManager; 44import com.sun.tools.javac.file.JavacFileManager; 45import com.sun.tools.javac.platform.PlatformDescription; 46import com.sun.tools.javac.processing.AnnotationProcessingError; 47import com.sun.tools.javac.util.*; 48import com.sun.tools.javac.util.Log.PrefixKind; 49import com.sun.tools.javac.util.Log.WriterKind; 50 51/** This class provides a command line interface to the javac compiler. 52 * 53 * <p><b>This is NOT part of any supported API. 54 * If you write code that depends on this, you do so at your own risk. 55 * This code and its internal interfaces are subject to change or 56 * deletion without notice.</b> 57 */ 58public class Main { 59 60 /** The name of the compiler, for use in diagnostics. 61 */ 62 String ownName; 63 64 /** The writer to use for diagnostic output. 65 */ 66 PrintWriter out; 67 68 /** The log to use for diagnostic output. 69 */ 70 public Log log; 71 72 /** 73 * If true, certain errors will cause an exception, such as command line 74 * arg errors, or exceptions in user provided code. 75 */ 76 boolean apiMode; 77 78 79 /** Result codes. 80 */ 81 public enum Result { 82 OK(0), // Compilation completed with no errors. 83 ERROR(1), // Completed but reported errors. 84 CMDERR(2), // Bad command-line arguments 85 SYSERR(3), // System error or resource exhaustion. 86 ABNORMAL(4); // Compiler terminated abnormally 87 88 Result(int exitCode) { 89 this.exitCode = exitCode; 90 } 91 92 public boolean isOK() { 93 return (exitCode == 0); 94 } 95 96 public final int exitCode; 97 } 98 99 /** 100 * Construct a compiler instance. 101 * @param name the name of this tool 102 */ 103 public Main(String name) { 104 this(name, new PrintWriter(System.err, true)); 105 } 106 107 /** 108 * Construct a compiler instance. 109 * @param name the name of this tool 110 * @param out a stream to which to write messages 111 */ 112 public Main(String name, PrintWriter out) { 113 this.ownName = name; 114 this.out = out; 115 } 116 117 /** Report a usage error. 118 */ 119 void error(String key, Object... args) { 120 if (apiMode) { 121 String msg = log.localize(PrefixKind.JAVAC, key, args); 122 throw new PropagatedException(new IllegalStateException(msg)); 123 } 124 warning(key, args); 125 log.printLines(PrefixKind.JAVAC, "msg.usage", ownName); 126 } 127 128 /** Report a warning. 129 */ 130 void warning(String key, Object... args) { 131 log.printRawLines(ownName + ": " + log.localize(PrefixKind.JAVAC, key, args)); 132 } 133 134 135 /** 136 * Programmatic interface for main function. 137 * @param args the command line parameters 138 * @return the result of the compilation 139 */ 140 public Result compile(String[] args) { 141 Context context = new Context(); 142 JavacFileManager.preRegister(context); // can't create it until Log has been set up 143 Result result = compile(args, context); 144 if (fileManager instanceof JavacFileManager) { 145 try { 146 // A fresh context was created above, so jfm must be a JavacFileManager 147 ((JavacFileManager)fileManager).close(); 148 } catch (IOException ex) { 149 bugMessage(ex); 150 } 151 } 152 return result; 153 } 154 155 /** 156 * Internal version of compile, allowing context to be provided. 157 * Note that the context needs to have a file manager set up. 158 * @param argv the command line parameters 159 * @param context the context 160 * @return the result of the compilation 161 */ 162 public Result compile(String[] argv, Context context) { 163 context.put(Log.outKey, out); 164 log = Log.instance(context); 165 166 if (argv.length == 0) { 167 Option.HELP.process(new OptionHelper.GrumpyHelper(log) { 168 @Override 169 public String getOwnName() { return ownName; } 170 @Override 171 public void put(String name, String value) { } 172 }, "-help"); 173 return Result.CMDERR; 174 } 175 176 try { 177 argv = CommandLine.parse(argv); 178 } catch (FileNotFoundException | NoSuchFileException e) { 179 warning("err.file.not.found", e.getMessage()); 180 return Result.SYSERR; 181 } catch (IOException ex) { 182 log.printLines(PrefixKind.JAVAC, "msg.io"); 183 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 184 return Result.SYSERR; 185 } 186 187 Arguments args = Arguments.instance(context); 188 args.init(ownName, argv); 189 190 if (log.nerrors > 0) 191 return Result.CMDERR; 192 193 Options options = Options.instance(context); 194 195 // init Log 196 boolean forceStdOut = options.isSet("stdout"); 197 if (forceStdOut) { 198 log.flush(); 199 log.setWriters(new PrintWriter(System.out, true)); 200 } 201 202 // init CacheFSInfo 203 // allow System property in following line as a Mustang legacy 204 boolean batchMode = (options.isUnset("nonBatchMode") 205 && System.getProperty("nonBatchMode") == null); 206 if (batchMode) 207 CacheFSInfo.preRegister(context); 208 209 boolean ok = true; 210 211 // init file manager 212 fileManager = context.get(JavaFileManager.class); 213 if (fileManager instanceof BaseFileManager) { 214 ((BaseFileManager) fileManager).setContext(context); // reinit with options 215 ok &= ((BaseFileManager) fileManager).handleOptions(args.getDeferredFileManagerOptions()); 216 } 217 218 // handle this here so it works even if no other options given 219 String showClass = options.get("showClass"); 220 if (showClass != null) { 221 if (showClass.equals("showClass")) // no value given for option 222 showClass = "com.sun.tools.javac.Main"; 223 showClass(showClass); 224 } 225 226 ok &= args.validate(); 227 if (!ok || log.nerrors > 0) 228 return Result.CMDERR; 229 230 if (args.isEmpty()) 231 return Result.OK; 232 233 // init Depeendencies 234 if (options.isSet("completionDeps")) { 235 Dependencies.GraphDependencies.preRegister(context); 236 } 237 238 // init plugins 239 Set<List<String>> pluginOpts = args.getPluginOpts(); 240 if (!pluginOpts.isEmpty() || context.get(PlatformDescription.class) != null) { 241 BasicJavacTask t = (BasicJavacTask) BasicJavacTask.instance(context); 242 t.initPlugins(pluginOpts); 243 } 244 245 // init JavaCompiler 246 JavaCompiler comp = JavaCompiler.instance(context); 247 248 // init doclint 249 List<String> docLintOpts = args.getDocLintOpts(); 250 if (!docLintOpts.isEmpty()) { 251 BasicJavacTask t = (BasicJavacTask) BasicJavacTask.instance(context); 252 t.initDocLint(docLintOpts); 253 } 254 255 if (options.get(Option.XSTDOUT) != null) { 256 // Stdout reassigned - ask compiler to close it when it is done 257 comp.closeables = comp.closeables.prepend(log.getWriter(WriterKind.NOTICE)); 258 } 259 260 try { 261 comp.compile(args.getFileObjects(), args.getClassNames(), null); 262 263 if (log.expectDiagKeys != null) { 264 if (log.expectDiagKeys.isEmpty()) { 265 log.printRawLines("all expected diagnostics found"); 266 return Result.OK; 267 } else { 268 log.printRawLines("expected diagnostic keys not found: " + log.expectDiagKeys); 269 return Result.ERROR; 270 } 271 } 272 273 return (comp.errorCount() == 0) ? Result.OK : Result.ERROR; 274 275 } catch (OutOfMemoryError | StackOverflowError ex) { 276 resourceMessage(ex); 277 return Result.SYSERR; 278 } catch (FatalError ex) { 279 feMessage(ex, options); 280 return Result.SYSERR; 281 } catch (AnnotationProcessingError ex) { 282 apMessage(ex); 283 return Result.SYSERR; 284 } catch (PropagatedException ex) { 285 // TODO: what about errors from plugins? should not simply rethrow the error here 286 throw ex.getCause(); 287 } catch (Throwable ex) { 288 // Nasty. If we've already reported an error, compensate 289 // for buggy compiler error recovery by swallowing thrown 290 // exceptions. 291 if (comp == null || comp.errorCount() == 0 || options.isSet("dev")) 292 bugMessage(ex); 293 return Result.ABNORMAL; 294 } finally { 295 if (comp != null) { 296 try { 297 comp.close(); 298 } catch (ClientCodeException ex) { 299 throw new RuntimeException(ex.getCause()); 300 } 301 } 302 } 303 } 304 305 /** Print a message reporting an internal error. 306 */ 307 void bugMessage(Throwable ex) { 308 log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version()); 309 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 310 } 311 312 /** Print a message reporting a fatal error. 313 */ 314 void feMessage(Throwable ex, Options options) { 315 log.printRawLines(ex.getMessage()); 316 if (ex.getCause() != null && options.isSet("dev")) { 317 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE)); 318 } 319 } 320 321 /** Print a message reporting an input/output error. 322 */ 323 void ioMessage(Throwable ex) { 324 log.printLines(PrefixKind.JAVAC, "msg.io"); 325 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 326 } 327 328 /** Print a message reporting an out-of-resources error. 329 */ 330 void resourceMessage(Throwable ex) { 331 log.printLines(PrefixKind.JAVAC, "msg.resource"); 332 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 333 } 334 335 /** Print a message reporting an uncaught exception from an 336 * annotation processor. 337 */ 338 void apMessage(AnnotationProcessingError ex) { 339 log.printLines(PrefixKind.JAVAC, "msg.proc.annotation.uncaught.exception"); 340 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE)); 341 } 342 343 /** Print a message reporting an uncaught exception from an 344 * annotation processor. 345 */ 346 void pluginMessage(Throwable ex) { 347 log.printLines(PrefixKind.JAVAC, "msg.plugin.uncaught.exception"); 348 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 349 } 350 351 /** Display the location and checksum of a class. */ 352 void showClass(String className) { 353 PrintWriter pw = log.getWriter(WriterKind.NOTICE); 354 pw.println("javac: show class: " + className); 355 356 URL url = getClass().getResource('/' + className.replace('.', '/') + ".class"); 357 if (url != null) { 358 pw.println(" " + url); 359 } 360 361 try (InputStream in = getClass().getResourceAsStream('/' + className.replace('.', '/') + ".class")) { 362 final String algorithm = "MD5"; 363 byte[] digest; 364 MessageDigest md = MessageDigest.getInstance(algorithm); 365 try (DigestInputStream din = new DigestInputStream(in, md)) { 366 byte[] buf = new byte[8192]; 367 int n; 368 do { n = din.read(buf); } while (n > 0); 369 digest = md.digest(); 370 } 371 StringBuilder sb = new StringBuilder(); 372 for (byte b: digest) 373 sb.append(String.format("%02x", b)); 374 pw.println(" " + algorithm + " checksum: " + sb); 375 } catch (NoSuchAlgorithmException | IOException e) { 376 pw.println(" cannot compute digest: " + e); 377 } 378 } 379 380 // TODO: update this to JavacFileManager 381 private JavaFileManager fileManager; 382 383 /* ************************************************************************ 384 * Internationalization 385 *************************************************************************/ 386 387 public static final String javacBundleName = 388 "com.sun.tools.javac.resources.javac"; 389} 390