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

Mon, 27 Sep 2010 14:05:33 -0700

author
jjg
date
Mon, 27 Sep 2010 14:05:33 -0700
changeset 694
f6fe12839a8a
parent 581
f2fdd52e4e87
child 707
33603a5fa84d
permissions
-rw-r--r--

6890226: javah -version is broken
Reviewed-by: darcy

     1 /*
     2  * Copyright (c) 2002, 2010, 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.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.SimpleTypeVisitor7;
    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 supported API.
    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             boolean ok = run();
   322             return ok ? 0 : 1;
   323         } catch (BadArgs e) {
   324             diagnosticListener.report(createDiagnostic(e.key, e.args));
   325             return 1;
   326         } catch (InternalError e) {
   327             diagnosticListener.report(createDiagnostic("err.internal.error", e.getMessage()));
   328             return 1;
   329         } finally {
   330             log.flush();
   331         }
   332     }
   334     public void handleOptions(String[] args) throws BadArgs {
   335         handleOptions(Arrays.asList(args), true);
   336     }
   338     private void handleOptions(Iterable<String> args, boolean allowClasses) throws BadArgs {
   339         if (log == null) {
   340             log = getPrintWriterForStream(System.out);
   341             if (diagnosticListener == null)
   342               diagnosticListener = getDiagnosticListenerForStream(System.err);
   343         } else {
   344             if (diagnosticListener == null)
   345               diagnosticListener = getDiagnosticListenerForWriter(log);
   346         }
   348         if (fileManager == null)
   349             fileManager = getDefaultFileManager(diagnosticListener, log);
   351         Iterator<String> iter = args.iterator();
   352         noArgs = !iter.hasNext();
   354         while (iter.hasNext()) {
   355             String arg = iter.next();
   356             if (arg.startsWith("-"))
   357                 handleOption(arg, iter);
   358             else if (allowClasses) {
   359                 if (classes == null)
   360                     classes = new ArrayList<String>();
   361                 classes.add(arg);
   362                 while (iter.hasNext())
   363                     classes.add(iter.next());
   364             } else
   365                 throw new BadArgs("err.unknown.option", arg).showUsage(true);
   366         }
   368         if ((classes == null || classes.size() == 0) &&
   369                 !(noArgs || help || version || fullVersion)) {
   370             throw new BadArgs("err.no.classes.specified");
   371         }
   373         if (jni && llni)
   374             throw new BadArgs("jni.llni.mixed");
   376         if (odir != null && ofile != null)
   377             throw new BadArgs("dir.file.mixed");
   378     }
   380     private void handleOption(String name, Iterator<String> rest) throws BadArgs {
   381         for (Option o: recognizedOptions) {
   382             if (o.matches(name)) {
   383                 if (o.hasArg) {
   384                     if (rest.hasNext())
   385                         o.process(this, name, rest.next());
   386                     else
   387                         throw new BadArgs("err.missing.arg", name).showUsage(true);
   388                 } else
   389                     o.process(this, name, null);
   391                 if (o.ignoreRest()) {
   392                     while (rest.hasNext())
   393                         rest.next();
   394                 }
   395                 return;
   396             }
   397         }
   399         if (fileManager.handleOption(name, rest))
   400             return;
   402         throw new BadArgs("err.unknown.option", name).showUsage(true);
   403     }
   405     public Boolean call() {
   406         return run();
   407     }
   409     public boolean run() throws Util.Exit {
   411         Util util = new Util(log, diagnosticListener);
   413         if (noArgs || help) {
   414             showHelp();
   415             return help; // treat noArgs as an error for purposes of exit code
   416         }
   418         if (version || fullVersion) {
   419             showVersion(fullVersion);
   420             return true;
   421         }
   423         util.verbose = verbose;
   425         Gen g;
   427         if (llni)
   428             g = new LLNI(doubleAlign, util);
   429         else {
   430 //            if (stubs)
   431 //                throw new BadArgs("jni.no.stubs");
   432             g = new JNI(util);
   433         }
   435         if (ofile != null) {
   436             if (!(fileManager instanceof StandardJavaFileManager)) {
   437                 diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-o"));
   438                 return false;
   439             }
   440             Iterable<? extends JavaFileObject> iter =
   441                     ((StandardJavaFileManager) fileManager).getJavaFileObjectsFromFiles(Collections.singleton(ofile));
   442             JavaFileObject fo = iter.iterator().next();
   443             g.setOutFile(fo);
   444         } else {
   445             if (odir != null) {
   446                 if (!(fileManager instanceof StandardJavaFileManager)) {
   447                     diagnosticListener.report(createDiagnostic("err.cant.use.option.for.fm", "-d"));
   448                     return false;
   449                 }
   451                 if (!odir.exists())
   452                     if (!odir.mkdirs())
   453                         util.error("cant.create.dir", odir.toString());
   454                 try {
   455                     ((StandardJavaFileManager) fileManager).setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(odir));
   456                 } catch (IOException e) {
   457                     Object msg = e.getLocalizedMessage();
   458                     if (msg == null) {
   459                         msg = e;
   460                     }
   461                     diagnosticListener.report(createDiagnostic("err.ioerror", odir, msg));
   462                     return false;
   463                 }
   464             }
   465             g.setFileManager(fileManager);
   466         }
   468         /*
   469          * Force set to false will turn off smarts about checking file
   470          * content before writing.
   471          */
   472         g.setForce(force);
   474         if (fileManager instanceof JavahFileManager)
   475             ((JavahFileManager) fileManager).setIgnoreSymbolFile(true);
   477         JavaCompiler c = ToolProvider.getSystemJavaCompiler();
   478         List<String> opts = Arrays.asList("-proc:only");
   479         CompilationTask t = c.getTask(log, fileManager, diagnosticListener, opts, internalize(classes), null);
   480         JavahProcessor p = new JavahProcessor(g);
   481         t.setProcessors(Collections.singleton(p));
   483         boolean ok = t.call();
   484         if (p.exit != null)
   485             throw new Util.Exit(p.exit);
   486         return ok;
   487     }
   489     private List<String> internalize(List<String> classes) {
   490         List<String> l = new ArrayList<String>();
   491         for (String c: classes) {
   492             l.add(c.replace('$', '.'));
   493         }
   494         return l;
   495     }
   497     private List<File> pathToFiles(String path) {
   498         List<File> files = new ArrayList<File>();
   499         for (String f: path.split(File.pathSeparator)) {
   500             if (f.length() > 0)
   501                 files.add(new File(f));
   502         }
   503         return files;
   504     }
   506     static StandardJavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
   507         return JavahFileManager.create(dl, log);
   508     }
   510     private void showHelp() {
   511         log.println(getMessage("main.usage", progname));
   512         for (Option o: recognizedOptions) {
   513             if (o.isHidden())
   514                 continue;
   515             String name = o.aliases[0].substring(1); // there must always be at least one name
   516             log.println(getMessage("main.opt." + name));
   517         }
   518         String[] fmOptions = { "-classpath", "-bootclasspath" };
   519         for (String o: fmOptions) {
   520             if (fileManager.isSupportedOption(o) == -1)
   521                 continue;
   522             String name = o.substring(1);
   523             log.println(getMessage("main.opt." + name));
   524         }
   525         log.println(getMessage("main.usage.foot"));
   526     }
   528     private void showVersion(boolean full) {
   529         log.println(version(full));
   530     }
   532     private static final String versionRBName = "com.sun.tools.javah.resources.version";
   533     private static ResourceBundle versionRB;
   535     private String version(boolean full) {
   536         String msgKey = (full ? "javah.fullVersion" : "javah.version");
   537         String versionKey = (full ? "full" : "release");
   538         // versionKey=product:  mm.nn.oo[-milestone]
   539         // versionKey=full:     mm.mm.oo[-milestone]-build
   540         if (versionRB == null) {
   541             try {
   542                 versionRB = ResourceBundle.getBundle(versionRBName);
   543             } catch (MissingResourceException e) {
   544                 return getMessage("version.resource.missing", System.getProperty("java.version"));
   545             }
   546         }
   547         try {
   548             return getMessage(msgKey, "javah", versionRB.getString(versionKey));
   549         }
   550         catch (MissingResourceException e) {
   551             return getMessage("version.unknown", System.getProperty("java.version"));
   552         }
   553     }
   555     private Diagnostic<JavaFileObject> createDiagnostic(final String key, final Object... args) {
   556         return new Diagnostic<JavaFileObject>() {
   557             public Kind getKind() {
   558                 return Diagnostic.Kind.ERROR;
   559             }
   561             public JavaFileObject getSource() {
   562                 return null;
   563             }
   565             public long getPosition() {
   566                 return Diagnostic.NOPOS;
   567             }
   569             public long getStartPosition() {
   570                 return Diagnostic.NOPOS;
   571             }
   573             public long getEndPosition() {
   574                 return Diagnostic.NOPOS;
   575             }
   577             public long getLineNumber() {
   578                 return Diagnostic.NOPOS;
   579             }
   581             public long getColumnNumber() {
   582                 return Diagnostic.NOPOS;
   583             }
   585             public String getCode() {
   586                 return key;
   587             }
   589             public String getMessage(Locale locale) {
   590                 return JavahTask.this.getMessage(locale, key, args);
   591             }
   593         };
   595     }
   596     private String getMessage(String key, Object... args) {
   597         return getMessage(task_locale, key, args);
   598     }
   600     private String getMessage(Locale locale, String key, Object... args) {
   601         if (bundles == null) {
   602             // could make this a HashMap<Locale,SoftReference<ResourceBundle>>
   603             // and for efficiency, keep a hard reference to the bundle for the task
   604             // locale
   605             bundles = new HashMap<Locale, ResourceBundle>();
   606         }
   608         if (locale == null)
   609             locale = Locale.getDefault();
   611         ResourceBundle b = bundles.get(locale);
   612         if (b == null) {
   613             try {
   614                 b = ResourceBundle.getBundle("com.sun.tools.javah.resources.l10n", locale);
   615                 bundles.put(locale, b);
   616             } catch (MissingResourceException e) {
   617                 throw new InternalError("Cannot find javah resource bundle for locale " + locale, e);
   618             }
   619         }
   621         try {
   622             return MessageFormat.format(b.getString(key), args);
   623         } catch (MissingResourceException e) {
   624             return key;
   625             //throw new InternalError(e, key);
   626         }
   627     }
   629     File ofile;
   630     File odir;
   631     String bootcp;
   632     String usercp;
   633     List<String> classes;
   634     boolean verbose;
   635     boolean noArgs;
   636     boolean help;
   637     boolean trace;
   638     boolean version;
   639     boolean fullVersion;
   640     boolean jni;
   641     boolean llni;
   642     boolean doubleAlign;
   643     boolean force;
   644     boolean old;
   646     PrintWriter log;
   647     JavaFileManager fileManager;
   648     DiagnosticListener<? super JavaFileObject> diagnosticListener;
   649     Locale task_locale;
   650     Map<Locale, ResourceBundle> bundles;
   652     private static final String progname = "javah";
   654     @SupportedAnnotationTypes("*")
   655     @SupportedSourceVersion(SourceVersion.RELEASE_7)
   656     class JavahProcessor extends AbstractProcessor {
   657         JavahProcessor(Gen g) {
   658             this.g = g;
   659         }
   661         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   662             Messager messager  = processingEnv.getMessager();
   663             Set<TypeElement> classes = getAllClasses(ElementFilter.typesIn(roundEnv.getRootElements()));
   664             if (classes.size() > 0) {
   665                 checkMethodParameters(classes);
   666                 g.setProcessingEnvironment(processingEnv);
   667                 g.setClasses(classes);
   669                 try {
   670                     g.run();
   671                 } catch (ClassNotFoundException cnfe) {
   672                     messager.printMessage(Diagnostic.Kind.ERROR, getMessage("class.not.found", cnfe.getMessage()));
   673                 } catch (IOException ioe) {
   674                     messager.printMessage(Diagnostic.Kind.ERROR, getMessage("io.exception", ioe.getMessage()));
   675                 } catch (Util.Exit e) {
   676                     exit = e;
   677                 }
   678             }
   679             return true;
   680         }
   682         private Set<TypeElement> getAllClasses(Set<? extends TypeElement> classes) {
   683             Set<TypeElement> allClasses = new LinkedHashSet<TypeElement>();
   684             getAllClasses0(classes, allClasses);
   685             return allClasses;
   686         }
   688         private void getAllClasses0(Iterable<? extends TypeElement> classes, Set<TypeElement> allClasses) {
   689             for (TypeElement c: classes) {
   690                 allClasses.add(c);
   691                 getAllClasses0(ElementFilter.typesIn(c.getEnclosedElements()), allClasses);
   692             }
   693         }
   695         // 4942232:
   696         // check that classes exist for all the parameters of native methods
   697         private void checkMethodParameters(Set<TypeElement> classes) {
   698             Types types = processingEnv.getTypeUtils();
   699             for (TypeElement te: classes) {
   700                 for (ExecutableElement ee: ElementFilter.methodsIn(te.getEnclosedElements())) {
   701                     for (VariableElement ve: ee.getParameters()) {
   702                         TypeMirror tm = ve.asType();
   703                         checkMethodParametersVisitor.visit(tm, types);
   704                     }
   705                 }
   706             }
   707         }
   709         private TypeVisitor<Void,Types> checkMethodParametersVisitor =
   710                 new SimpleTypeVisitor7<Void,Types>() {
   711             @Override
   712             public Void visitArray(ArrayType t, Types types) {
   713                 visit(t.getComponentType(), types);
   714                 return null;
   715             }
   716             @Override
   717             public Void visitDeclared(DeclaredType t, Types types) {
   718                 t.asElement().getKind(); // ensure class exists
   719                 for (TypeMirror st: types.directSupertypes(t))
   720                     visit(st, types);
   721                 return null;
   722             }
   723         };
   725         private Gen g;
   726         private Util.Exit exit;
   727     }
   728 }

mercurial