src/share/classes/com/sun/tools/javah/JavahTask.java

Thu, 25 Feb 2010 13:32:08 -0800

author
jjg
date
Thu, 25 Feb 2010 13:32:08 -0800
changeset 508
af75fd6155de
parent 416
c287d51c57da
child 529
3058880c0b8d
permissions
-rw-r--r--

6893943: exit code from javah with no args is 0
Reviewed-by: darcy

     1 /*
     2  * Copyright 2002-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.javah;
    28 import java.io.File;
    29 import java.io.IOException;
    30 import java.io.OutputStream;
    31 import java.io.PrintWriter;
    32 import java.io.Writer;
    33 import java.text.MessageFormat;
    34 import java.util.ArrayList;
    35 import java.util.Arrays;
    36 import java.util.Collections;
    37 import java.util.HashMap;
    38 import java.util.Iterator;
    39 import java.util.LinkedHashSet;
    40 import java.util.List;
    41 import java.util.Locale;
    42 import java.util.Map;
    43 import java.util.MissingResourceException;
    44 import java.util.ResourceBundle;
    45 import java.util.Set;
    47 import javax.annotation.processing.AbstractProcessor;
    48 import javax.annotation.processing.Messager;
    49 import javax.annotation.processing.RoundEnvironment;
    50 import javax.annotation.processing.SupportedAnnotationTypes;
    51 import javax.annotation.processing.SupportedSourceVersion;
    53 import javax.lang.model.SourceVersion;
    54 import javax.lang.model.element.ExecutableElement;
    55 import javax.lang.model.element.TypeElement;
    56 import javax.lang.model.element.VariableElement;
    57 import javax.lang.model.type.ArrayType;
    58 import javax.lang.model.type.DeclaredType;
    59 import javax.lang.model.type.TypeMirror;
    60 import javax.lang.model.type.TypeVisitor;
    61 import javax.lang.model.util.ElementFilter;
    62 import javax.lang.model.util.SimpleTypeVisitor6;
    63 import javax.lang.model.util.Types;
    65 import javax.tools.Diagnostic;
    66 import javax.tools.DiagnosticListener;
    67 import javax.tools.JavaCompiler;
    68 import javax.tools.JavaCompiler.CompilationTask;
    69 import javax.tools.JavaFileManager;
    70 import javax.tools.JavaFileObject;
    71 import javax.tools.StandardJavaFileManager;
    72 import javax.tools.StandardLocation;
    73 import javax.tools.ToolProvider;
    75 /**
    76  * Javah generates support files for native methods.
    77  * Parse commandline options & Invokes javadoc to execute those commands.
    78  *
    79  * <p><b>This is NOT part of any API supported by Sun Microsystems.
    80  * If you write code that depends on this, you do so at your own
    81  * risk.  This code and its internal interfaces are subject to change
    82  * or deletion without notice.</b></p>
    83  *
    84  * @author Sucheta Dambalkar
    85  * @author Jonathan Gibbons
    86  */
    87 public class JavahTask implements NativeHeaderTool.NativeHeaderTask {
    88     public class BadArgs extends Exception {
    89         private static final long serialVersionUID = 1479361270874789045L;
    90         BadArgs(String key, Object... args) {
    91             super(JavahTask.this.getMessage(key, args));
    92             this.key = key;
    93             this.args = args;
    94         }
    96         BadArgs showUsage(boolean b) {
    97             showUsage = b;
    98             return this;
    99         }
   101         final String key;
   102         final Object[] args;
   103         boolean showUsage;
   104     }
   106     static abstract class Option {
   107         Option(boolean hasArg, String... aliases) {
   108             this.hasArg = hasArg;
   109             this.aliases = aliases;
   110         }
   112         boolean isHidden() {
   113             return false;
   114         }
   116         boolean matches(String opt) {
   117             for (String a: aliases) {
   118                 if (a.equals(opt))
   119                     return true;
   120             }
   121             return false;
   122         }
   124         boolean ignoreRest() {
   125             return false;
   126         }
   128         abstract void process(JavahTask task, String opt, String arg) throws BadArgs;
   130         final boolean hasArg;
   131         final String[] aliases;
   132     }
   134     static abstract class HiddenOption extends Option {
   135         HiddenOption(boolean hasArg, String... aliases) {
   136             super(hasArg, aliases);
   137         }
   139         @Override
   140         boolean isHidden() {
   141             return true;
   142         }
   143     }
   145     static Option[] recognizedOptions = {
   146         new Option(true, "-o") {
   147             void process(JavahTask task, String opt, String arg) {
   148                 task.ofile = new File(arg);
   149             }
   150         },
   152         new Option(true, "-d") {
   153             void process(JavahTask task, String opt, String arg) {
   154                 task.odir = new File(arg);
   155             }
   156         },
   158         new HiddenOption(true, "-td") {
   159             void process(JavahTask task, String opt, String arg) {
   160                  // ignored; for backwards compatibility
   161             }
   162         },
   164         new HiddenOption(false, "-stubs") {
   165             void process(JavahTask task, String opt, String arg) {
   166                  // ignored; for backwards compatibility
   167             }
   168         },
   170         new Option(false, "-v", "-verbose") {
   171             void process(JavahTask task, String opt, String arg) {
   172                 task.verbose = true;
   173             }
   174         },
   176         new Option(false, "-help", "--help", "-?") {
   177             void process(JavahTask task, String opt, String arg) {
   178                 task.help = true;
   179             }
   180         },
   182         new HiddenOption(false, "-trace") {
   183             void process(JavahTask task, String opt, String arg) {
   184                 task.trace = true;
   185             }
   186         },
   188         new Option(false, "-version") {
   189             void process(JavahTask task, String opt, String arg) {
   190                 task.version = true;
   191             }
   192         },
   194         new HiddenOption(false, "-fullversion") {
   195             void process(JavahTask task, String opt, String arg) {
   196                 task.fullVersion = true;
   197             }
   198         },
   200         new Option(false, "-jni") {
   201             void process(JavahTask task, String opt, String arg) {
   202                 task.jni = true;
   203             }
   204         },
   206         new Option(false, "-force") {
   207             void process(JavahTask task, String opt, String arg) {
   208                 task.force = true;
   209             }
   210         },
   212         new HiddenOption(false, "-Xnew") {
   213             void process(JavahTask task, String opt, String arg) {
   214                 // we're already using the new javah
   215             }
   216         },
   218         new HiddenOption(false, "-old") {
   219             void process(JavahTask task, String opt, String arg) {
   220                 task.old = true;
   221             }
   222         },
   224         new HiddenOption(false, "-llni", "-Xllni") {
   225             void process(JavahTask task, String opt, String arg) {
   226                 task.llni = true;
   227             }
   228         },
   230         new HiddenOption(false, "-llnidouble") {
   231             void process(JavahTask task, String opt, String arg) {
   232                 task.llni = true;
   233                 task.doubleAlign = true;
   234             }
   235         },
   236     };
   238     JavahTask() {
   239     }
   241     JavahTask(Writer out,
   242             JavaFileManager fileManager,
   243             DiagnosticListener<? super JavaFileObject> diagnosticListener,
   244             Iterable<String> options,
   245             Iterable<String> classes) {
   246         this();
   247         this.log = getPrintWriterForWriter(out);
   248         this.fileManager = fileManager;
   249         this.diagnosticListener = diagnosticListener;
   251         try {
   252             handleOptions(options, false);
   253         } catch (BadArgs e) {
   254             throw new IllegalArgumentException(e.getMessage());
   255         }
   257         this.classes = new ArrayList<String>();
   258         if (classes != null) {
   259             for (String classname: classes) {
   260                 classname.getClass(); // null-check
   261                 this.classes.add(classname);
   262             }
   263         }
   264     }
   266     public void setLocale(Locale locale) {
   267         if (locale == null)
   268             locale = Locale.getDefault();
   269         task_locale = locale;
   270     }
   272     public void setLog(PrintWriter log) {
   273         this.log = log;
   274     }
   276     public void setLog(OutputStream s) {
   277         setLog(getPrintWriterForStream(s));
   278     }
   280     static PrintWriter getPrintWriterForStream(OutputStream s) {
   281         return new PrintWriter(s, true);
   282     }
   284     static PrintWriter getPrintWriterForWriter(Writer w) {
   285         if (w == null)
   286             return getPrintWriterForStream(null);
   287         else if (w instanceof PrintWriter)
   288             return (PrintWriter) w;
   289         else
   290             return new PrintWriter(w, true);
   291     }
   293     public void setDiagnosticListener(DiagnosticListener<? super JavaFileObject> dl) {
   294         diagnosticListener = dl;
   295     }
   297     public void setDiagnosticListener(OutputStream s) {
   298         setDiagnosticListener(getDiagnosticListenerForStream(s));
   299     }
   301     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForStream(OutputStream s) {
   302         return getDiagnosticListenerForWriter(getPrintWriterForStream(s));
   303     }
   305     private DiagnosticListener<JavaFileObject> getDiagnosticListenerForWriter(Writer w) {
   306         final PrintWriter pw = getPrintWriterForWriter(w);
   307         return new DiagnosticListener<JavaFileObject> () {
   308             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   309                 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   310                     pw.print(getMessage("err.prefix"));
   311                     pw.print(" ");
   312                 }
   313                 pw.println(diagnostic.getMessage(null));
   314             }
   315         };
   316     }
   318     int run(String[] args) {
   319         try {
   320             handleOptions(args);
   321             if (classes == null || classes.size() == 0) {
   322                 if (help || version || fullVersion)
   323                     return 0;
   324                 else
   325                     return 1;
   326             }
   327             boolean ok = run();
   328             return ok ? 0 : 1;
   329         } catch (BadArgs e) {
   330             diagnosticListener.report(createDiagnostic(e.key, e.args));
   331             return 1;
   332         } catch (InternalError e) {
   333             diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage()));
   334             return 1;
   335         } finally {
   336             log.flush();
   337         }
   338     }
   340     public void handleOptions(String[] args) throws BadArgs {
   341         handleOptions(Arrays.asList(args), true);
   342     }
   344     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
   345         if (log == null) {
   346             log = getPrintWriterForStream(System.out);
   347             if (diagnosticListener == null)
   348               diagnosticListener = getDiagnosticListenerForStream(System.err);
   349         } else {
   350             if (diagnosticListener == null)
   351               diagnosticListener = getDiagnosticListenerForWriter(log);
   352         }
   354         if (fileManager == null)
   355             fileManager = getDefaultFileManager(diagnosticListener, log);
   357         Iterator<String> iter = args.iterator();
   358         boolean noArgs = !iter.hasNext();
   360         while (iter.hasNext()) {
   361             String arg = iter.next();
   362             if (arg.startsWith("-"))
   363                 handleOption(arg, iter);
   364             else if (allowClasses) {
   365                 if (classes == null)
   366                     classes = new ArrayList<String>();
   367                 classes.add(arg);
   368                 while (iter.hasNext())
   369                     classes.add(iter.next());
   370             } else
   371                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
   372         }
   374         if ((classes == null || classes.size() == 0) &&
   375                 !(noArgs || help || version || fullVersion)) {
   376             throw new BadArgs("err.no.classes.specified");
   377         }
   379         if (jni && llni)
   380             throw new BadArgs("jni.llni.mixed");
   382         if (odir != null && ofile != null)
   383             throw new BadArgs("dir.file.mixed");
   384     }
   386     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
   387         for (Option o: recognizedOptions) {
   388             if (o.matches(name)) {
   389                 if (o.hasArg) {
   390                     if (rest.hasNext())
   391                         o.process(this, name, rest.next());
   392                     else
   393                         throw new BadArgs("err.missing.arg", name).showUsage(true);
   394                 } else
   395                     o.process(this, name, null);
   397                 if (o.ignoreRest()) {
   398                     while (rest.hasNext())
   399                         rest.next();
   400                 }
   401                 return;
   402             }
   403         }
   405         if (fileManager.handleOption(name, rest))
   406             return;
   408         throw new BadArgs("err.unknown.option", name).showUsage(true);
   409     }
   411     public Boolean call() {
   412         return run();
   413     }
   415     public boolean run() throws Util.Exit {
   417         Util util = new Util(log, diagnosticListener);
   419         if (help) {
   420             showHelp();
   421             return true;
   422         }
   424         if (version || fullVersion) {
   425             showVersion(fullVersion);
   426             return true;
   427         }
   429         util.verbose = verbose;
   431         Gen g;
   433         if (llni)
   434             g = new LLNI(doubleAlign, util);
   435         else {
   436 //            if (stubs)
   437 //                throw new BadArgs("jni.no.stubs");
   438             g = new JNI(util);
   439         }
   441         if (ofile != null) {
   442             if (!(fileManager instanceof StandardJavaFileManager)) {
   443                 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o"));
   444                 return false;
   445             }
   446             Iterable<? extends JavaFileObject> iter =
   447                     ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile));
   448             JavaFileObject fo = iter.iterator().next();
   449             g.setOutFile(fo);
   450         } else {
   451             if (odir != null) {
   452                 if (!(fileManager instanceof StandardJavaFileManager)) {
   453                     diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d"));
   454                     return false;
   455                 }
   457                 if (!odir.exists())
   458                     if (!odir.mkdirs())
   459                         util.error("cant.create.dir", odir.toString());
   460                 try {
   461                     ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir));
   462                 } catch (IOException e) {
   463                     Object msg = e.getLocalizedMessage();
   464                     if (msg == null) {
   465                         msg = e;
   466                     }
   467                     diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg));
   468                     return false;
   469                 }
   470             }
   471             g.setFileManager(fileManager);
   472         }
   474         /*
   475          * Force set to false will turn off smarts about checking file
   476          * content before writing.
   477          */
   478         g.setForce(force);
   480         if (fileManager instanceof JavahFileManager)
   481             ((JavahFileManager) fileManager).setIgnoreSymbolFile(true);
   483         JavaCompiler c = ToolProvider.getSystemJavaCompiler();
   484         List<String> opts = Arrays.asList("-proc:only");
   485         CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, internalize(classes), null);
   486         JavahProcessor p = new JavahProcessor(g);
   487         t.setProcessors(Collections.singleton(p));
   489         boolean ok = t.call();
   490         if (p.exit != null)
   491             throw new Util.Exit(p.exit);
   492         return ok;
   493     }
   495     private List<String> internalize(List<String> classes) {
   496         List<String> l = new ArrayList<String>();
   497         for (String c: classes) {
   498             l.add(c.replace('$', '.'));
   499         }
   500         return l;
   501     }
   503     private List<File> pathToFiles(String path) {
   504         List<File> files = new ArrayList<File>();
   505         for (String f: path.split(File.pathSeparator)) {
   506             if (f.length() > 0)
   507                 files.add(new File(f));
   508         }
   509         return files;
   510     }
   512     static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
   513         return JavahFileManager.create(dl, log);
   514     }
   516     private void showHelp() {
   517         log.println(getMessage("main.usage", progname));
   518         for (Option o: recognizedOptions) {
   519             if (o.isHidden())
   520                 continue;
   521             String name = o.aliases[0].substring(1); // there must always be at least one name
   522             log.println(getMessage("main.opt." + name));
   523         }
   524         String[] fmOptions = { "-classpath", "-bootclasspath" };
   525         for (String o: fmOptions) {
   526             if (fileManager.isSupportedOption(o) == -1)
   527                 continue;
   528             String name = o.substring(1);
   529             log.println(getMessage("main.opt." + name));
   530         }
   531         log.println(getMessage("main.usage.foot"));
   532     }
   534     private void showVersion(boolean full) {
   535         log.println(version(full ? "full" : "release"));
   536     }
   538     private static final String versionRBName = "com.sun.tools.javah.resources.version";
   539     private static ResourceBundle versionRB;
   541     private String version(String key) {
   542         // key=version:  mm.nn.oo[-milestone]
   543         // key=full:     mm.mm.oo[-milestone]-build
   544         if (versionRB == null) {
   545             try {
   546                 versionRB = ResourceBundle.getBundle(versionRBName);
   547             } catch (MissingResourceException e) {
   548                 return getMessage("version.resource.missing", System.getProperty("java.version"));
   549             }
   550         }
   551         try {
   552             return versionRB.getString(key);
   553         }
   554         catch (MissingResourceException e) {
   555             return getMessage("version.unknown", System.getProperty("java.version"));
   556         }
   557     }
   559     private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
   560         return new Diagnostic<JavaFileObject>() {
   561             public Kind getKind() {
   562                 return Diagnostic.Kind.ERROR;
   563             }
   565             public JavaFileObject getSource() {
   566                 return null;
   567             }
   569             public long getPosition() {
   570                 return Diagnostic.NOPOS;
   571             }
   573             public long getStartPosition() {
   574                 return Diagnostic.NOPOS;
   575             }
   577             public long getEndPosition() {
   578                 return Diagnostic.NOPOS;
   579             }
   581             public long getLineNumber() {
   582                 return Diagnostic.NOPOS;
   583             }
   585             public long getColumnNumber() {
   586                 return Diagnostic.NOPOS;
   587             }
   589             public String getCode() {
   590                 return key;
   591             }
   593             public String getMessage(Locale locale) {
   594                 return JavahTask.this.getMessage(locale, key, args);
   595             }
   597         };
   599     }
   600     private String getMessage(String key, Object... args) {
   601         return getMessage(task_locale, key, args);
   602     }
   604     private String getMessage(Locale locale, String key, Object... args) {
   605         if (bundles == null) {
   606             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
   607             // and for efficiency, keep a hard reference to the bundle for the task
   608             // locale
   609             bundles = new HashMap<Locale, ResourceBundle>();
   610         }
   612         if (locale == null)
   613             locale = Locale.getDefault();
   615         ResourceBundle b = bundles.get(locale);
   616         if (b == null) {
   617             try {
   618                 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale);
   619                 bundles.put(locale, b);
   620             } catch (MissingResourceException e) {
   621                 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e);
   622             }
   623         }
   625         try {
   626             return MessageFormat.format(b.getString(key), args);
   627         } catch (MissingResourceException e) {
   628             return key;
   629             //throw new InternalError(e, key);
   630         }
   631     }
   633     File ofile;
   634     File odir;
   635     String bootcp;
   636     String usercp;
   637     List<String> classes;
   638     boolean verbose;
   639     boolean help;
   640     boolean trace;
   641     boolean version;
   642     boolean fullVersion;
   643     boolean jni;
   644     boolean llni;
   645     boolean doubleAlign;
   646     boolean force;
   647     boolean old;
   649     PrintWriter log;
   650     JavaFileManager fileManager;
   651     DiagnosticListener<? super JavaFileObject> diagnosticListener;
   652     Locale task_locale;
   653     Map<Locale, ResourceBundle> bundles;
   655     private static final String progname = "javah";
   657     @SupportedAnnotationTypes("*")
   658     @SupportedSourceVersion(SourceVersion.RELEASE_7)
   659     class JavahProcessor extends AbstractProcessor {
   660         JavahProcessor(Gen g) {
   661             this.g = g;
   662         }
   664         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   665             Messager messager  = processingEnv.getMessager();
   666             Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements()));
   667             if (classes.size() > 0) {
   668                 checkMethodParameters(classes);
   669                 g.setProcessingEnvironment(processingEnv);
   670                 g.setClasses(classes);
   672                 try {
   673                     g.run();
   674                 } catch (ClassNotFoundException cnfe) {
   675                     messager.printMessage(Diagnostic.Kind.ERROR, getMessage("class.not.found", cnfe.getMessage()));
   676                 } catch (IOException ioe) {
   677                     messager.printMessage(Diagnostic.Kind.ERROR, getMessage("io.exception", ioe.getMessage()));
   678                 } catch (Util.Exit e) {
   679                     exit = e;
   680                 }
   681             }
   682             return true;
   683         }
   685         private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
   686             Set<TypeElement> allClasses = new LinkedHashSet<TypeElement>();
   687             getAllClasses0(classes, allClasses);
   688             return allClasses;
   689         }
   691         private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) {
   692             for (TypeElement c: classes) {
   693                 allClasses.add(c);
   694                 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses);
   695             }
   696         }
   698         // 4942232:
   699         // check that classes exist for all the parameters of native methods
   700         private void checkMethodParameters(Set<TypeElement> classes) {
   701             Types types = processingEnv.getTypeUtils();
   702             for (TypeElement te: classes) {
   703                 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) {
   704                     for (VariableElement ve: ee.getParameters()) {
   705                         TypeMirror tm = ve.asType();
   706                         checkMethodParametersVisitor.visit(tm, types);
   707                     }
   708                 }
   709             }
   710         }
   712         private TypeVisitor<Void,Types> checkMethodParametersVisitor =
   713                 new SimpleTypeVisitor6<Void,Types>() {
   714             @Override
   715             public Void visitArray(ArrayType t, Types types) {
   716                 visit(t.getComponentType(), types);
   717                 return null;
   718             }
   719             @Override
   720             public Void visitDeclared(DeclaredType t, Types types) {
   721                 t.asElement().getKind(); // ensure class exists
   722                 for (TypeMirror st: types.directSupertypes(t))
   723                     visit(st, types);
   724                 return null;
   725             }
   726         };
   728         private Gen g;
   729         private Util.Exit exit;
   730     }
   731 }

mercurial