make/tools/GenStubs/GenStubs.java

Fri, 20 Nov 2009 11:18:43 -0800

author
jjg
date
Fri, 20 Nov 2009 11:18:43 -0800
changeset 441
4325b440eb3e
child 468
51011e02c02f
permissions
-rw-r--r--

6902337: fix langtools build to allow forward refs into jdk/ repo
Reviewed-by: ohair, mcimadamore

     1 /*
     2  * Copyright 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  */
    25 import java.io.*;
    26 import java.util.*;
    27 import javax.tools.JavaFileObject;
    28 import javax.tools.StandardJavaFileManager;
    29 import javax.tools.StandardLocation;
    31 import org.apache.tools.ant.BuildException;
    32 import org.apache.tools.ant.DirectoryScanner;
    33 import org.apache.tools.ant.taskdefs.MatchingTask;
    34 import org.apache.tools.ant.types.Path;
    35 import org.apache.tools.ant.types.Reference;
    38 import com.sun.source.tree.CompilationUnitTree;
    39 import com.sun.source.util.JavacTask;
    40 import com.sun.tools.javac.api.JavacTool;
    41 import com.sun.tools.javac.code.Flags;
    42 import com.sun.tools.javac.code.TypeTags;
    43 import com.sun.tools.javac.tree.JCTree;
    44 import com.sun.tools.javac.tree.JCTree.JCBlock;
    45 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
    46 import com.sun.tools.javac.tree.JCTree.JCLiteral;
    47 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    48 import com.sun.tools.javac.tree.JCTree.JCModifiers;
    49 import com.sun.tools.javac.tree.JCTree.JCStatement;
    50 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
    51 import com.sun.tools.javac.tree.Pretty;
    52 import com.sun.tools.javac.tree.TreeTranslator;
    54 /**
    55  * Generate stub source files by removing implementation details from input files.
    56  *
    57  * This is a special purpose stub generator, specific to the needs of generating
    58  * stub files for JDK 7 API that are needed to compile langtools files that depend
    59  * on that API. The stub generator works by removing as much of the API source code
    60  * as possible without affecting the public signature, in order to reduce the
    61  * transitive closure of the API being referenced. The resulting stubs can be
    62  * put on the langtools sourcepath with -implicit:none to compile the langtools
    63  * files that depend on the JDK 7 API.
    64  *
    65  * Usage:
    66  *  genstubs -s <outdir> -sourcepath <path> <classnames>
    67  *
    68  * The specified class names are looked up on the sourcepath, and corresponding
    69  * stubs are written to the source output directory.
    70  *
    71  * Classes are parsed into javac ASTs, then processed with a javac TreeTranslator
    72  * to remove implementation details, and written out in the source output directory.
    73  * Documentation comments and annotations are removed. Method bodies are removed
    74  * and methods are marked native. Private and package-private field definitions
    75  * have their initializers replace with 0, 0.0, false, null as appropriate.
    76  *
    77  * An Ant task, Main$Ant is also provided. Files are specified with an implicit
    78  * fileset, using srcdir as a base directory. The set of files to be included
    79  * is specified with an includes attribute or nested <includes> set. However,
    80  * unlike a normal fileset, an empty includes attribute means "no files" instead
    81  * of "all files".  The Ant task also accepts "fork=true" and classpath attribute
    82  * or nested <classpath> element to run GenStubs in a separate VM with the specified
    83  * path. This is likely necessary if a JDK 7 parser is required to read the
    84  * JDK 7 input files.
    85  */
    87 public class GenStubs {
    88     static class Fault extends Exception {
    89         private static final long serialVersionUID = 0;
    90         Fault(String message) {
    91             super(message);
    92         }
    93         Fault(String message, Throwable cause) {
    94             super(message);
    95             initCause(cause);
    96         }
    97     }
    99     public static void main(String[] args) {
   100         boolean ok = new GenStubs().run(args);
   101         if (!ok)
   102             System.exit(1);
   103     }
   105     boolean run(String... args) {
   106         File outdir = null;
   107         String sourcepath = null;
   108         List<String> classes = new ArrayList<String>();
   109         for (ListIterator<String> iter = Arrays.asList(args).listIterator(); iter.hasNext(); ) {
   110             String arg = iter.next();
   111             if (arg.equals("-s") && iter.hasNext())
   112                 outdir = new File(iter.next());
   113             else if (arg.equals("-sourcepath") && iter.hasNext())
   114                 sourcepath = iter.next();
   115             else if (arg.startsWith("-"))
   116                 throw new IllegalArgumentException(arg);
   117             else {
   118                 classes.add(arg);
   119                 while (iter.hasNext())
   120                     classes.add(iter.next());
   121             }
   122         }
   124         return run(sourcepath, outdir, classes);
   125     }
   127     boolean run(String sourcepath, File outdir, List<String> classes) {
   128         //System.err.println("run: sourcepath:" + sourcepath + " outdir:" + outdir + " classes:" + classes);
   129         if (sourcepath == null)
   130             throw new IllegalArgumentException("sourcepath not set");
   131         if (outdir == null)
   132             throw new IllegalArgumentException("source output dir not set");
   134         JavacTool tool = JavacTool.create();
   135         StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
   137         try {
   138             fm.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outdir));
   139             fm.setLocation(StandardLocation.SOURCE_PATH, splitPath(sourcepath));
   140             List<JavaFileObject> files = new ArrayList<JavaFileObject>();
   141             for (String c: classes) {
   142                 JavaFileObject fo = fm.getJavaFileForInput(
   143                         StandardLocation.SOURCE_PATH, c, JavaFileObject.Kind.SOURCE);
   144                 if (fo == null)
   145                     error("class not found: " + c);
   146                 else
   147                     files.add(fo);
   148             }
   150             JavacTask t = tool.getTask(null, fm, null, null, null, files);
   151             Iterable<? extends CompilationUnitTree> trees = t.parse();
   152             for (CompilationUnitTree tree: trees) {
   153                 makeStub(fm, tree);
   154             }
   155         } catch (IOException e) {
   156             error("IO error " + e, e);
   157         }
   159         return (errors == 0);
   160     }
   162     void makeStub(StandardJavaFileManager fm, CompilationUnitTree tree) throws IOException {
   163         CompilationUnitTree tree2 = new StubMaker().translate(tree);
   165         String className = fm.inferBinaryName(StandardLocation.SOURCE_PATH, tree.getSourceFile());
   166         JavaFileObject fo = fm.getJavaFileForOutput(StandardLocation.SOURCE_OUTPUT,
   167                 className, JavaFileObject.Kind.SOURCE, null);
   168         // System.err.println("Writing " + className + " to " + fo.getName());
   169         Writer out = fo.openWriter();
   170         try {
   171             new Pretty(out, true).printExpr((JCTree) tree2);
   172         } finally {
   173             out.close();
   174         }
   175     }
   177     List<File> splitPath(String path) {
   178         List<File> list = new ArrayList<File>();
   179         for (String p: path.split(File.pathSeparator)) {
   180             if (p.length() > 0)
   181                 list.add(new File(p));
   182         }
   183         return list;
   184     }
   186     void error(String message) {
   187         System.err.println(message);
   188         errors++;
   189     }
   191     void error(String message, Throwable cause) {
   192         error(message);
   193     }
   195     int errors;
   197     class StubMaker extends TreeTranslator {
   198         CompilationUnitTree translate(CompilationUnitTree tree) {
   199             return super.translate((JCCompilationUnit) tree);
   200         }
   202         /**
   203          * compilation units: remove javadoc comments
   204          * -- required, in order to remove @deprecated tags, since we
   205          * (separately) remove all annotations, including @Deprecated
   206          */
   207         public void visitTopLevel(JCCompilationUnit tree) {
   208             super.visitTopLevel(tree);
   209             tree.docComments = Collections.emptyMap();
   210         }
   212         /**
   213          * methods: remove method bodies, make methods native
   214          */
   215         @Override
   216         public void visitMethodDef(JCMethodDecl tree) {
   217             tree.mods = translate(tree.mods);
   218             tree.restype = translate(tree.restype);
   219             tree.typarams = translateTypeParams(tree.typarams);
   220             tree.params = translateVarDefs(tree.params);
   221             tree.thrown = translate(tree.thrown);
   222             if (tree.restype != null && tree.body != null) {
   223                 tree.mods.flags |= Flags.NATIVE;
   224                 tree.body = null;
   225             }
   226             result = tree;
   227         }
   229         /**
   230          * modifiers: remove annotations
   231          */
   232         @Override
   233         public void visitModifiers(JCModifiers tree) {
   234             tree.annotations = com.sun.tools.javac.util.List.nil();
   235             result = tree;
   236         }
   238         /**
   239          * field definitions: replace initializers with 0, 0.0, false etc
   240          * when possible -- i.e. leave public, protected initializers alone
   241          */
   242         @Override
   243         public void visitVarDef(JCVariableDecl tree) {
   244             tree.mods = translate(tree.mods);
   245             tree.vartype = translate(tree.vartype);
   246             if (tree.init != null) {
   247                 if ((tree.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0)
   248                     tree.init = translate(tree.init);
   249                 else {
   250                     String t = tree.vartype.toString();
   251                     if (t.equals("boolean"))
   252                         tree.init = new JCLiteral(TypeTags.BOOLEAN, 0) { };
   253                     else if (t.equals("byte"))
   254                         tree.init = new JCLiteral(TypeTags.BYTE, 0) { };
   255                     else if (t.equals("char"))
   256                         tree.init = new JCLiteral(TypeTags.CHAR, 0) { };
   257                     else if (t.equals("double"))
   258                         tree.init = new JCLiteral(TypeTags.DOUBLE, 0.d) { };
   259                     else if (t.equals("float"))
   260                         tree.init = new JCLiteral(TypeTags.FLOAT, 0.f) { };
   261                     else if (t.equals("int"))
   262                         tree.init = new JCLiteral(TypeTags.INT, 0) { };
   263                     else if (t.equals("long"))
   264                         tree.init = new JCLiteral(TypeTags.LONG, 0) { };
   265                     else if (t.equals("short"))
   266                         tree.init = new JCLiteral(TypeTags.SHORT, 0) { };
   267                     else
   268                         tree.init = new JCLiteral(TypeTags.BOT, null) { };
   269                 }
   270             }
   271             result = tree;
   272         }
   273     }
   275     //---------- Ant Invocation ------------------------------------------------
   277     public static class Ant extends MatchingTask {
   278         private File srcDir;
   279         private File destDir;
   280         private boolean fork;
   281         private Path classpath;
   282         private String includes;
   284         public void setSrcDir(File dir) {
   285             this.srcDir = dir;
   286         }
   288         public void setDestDir(File dir) {
   289             this.destDir = dir;
   290         }
   292         public void setFork(boolean v) {
   293             this.fork = v;
   294         }
   296         public void setClasspath(Path cp) {
   297             if (classpath == null)
   298                 classpath = cp;
   299             else
   300                 classpath.append(cp);
   301         }
   303         public Path createClasspath() {
   304             if (classpath == null) {
   305                 classpath = new Path(getProject());
   306             }
   307             return classpath.createPath();
   308         }
   310         public void setClasspathRef(Reference r) {
   311             createClasspath().setRefid(r);
   312         }
   314         public void setIncludes(String includes) {
   315             super.setIncludes(includes);
   316             this.includes = includes;
   317         }
   319         @Override
   320         public void execute() {
   321             if (includes != null && includes.trim().isEmpty())
   322                 return;
   324             DirectoryScanner s = getDirectoryScanner(srcDir);
   325             String[] files = s.getIncludedFiles();
   326 //            System.err.println("Ant.execute: srcDir " + srcDir);
   327 //            System.err.println("Ant.execute: destDir " + destDir);
   328 //            System.err.println("Ant.execute: files " + Arrays.asList(files));
   330             files = filter(srcDir, destDir, files);
   331             if (files.length == 0)
   332                 return;
   333             System.out.println("Generating " + files.length + " stub files to " + destDir);
   335             List<String> classNames = new ArrayList<String>();
   336             for (String file: files) {
   337                 classNames.add(file.replaceAll(".java$", "").replace('/', '.'));
   338             }
   340             if (!fork) {
   341                 GenStubs m = new GenStubs();
   342                 boolean ok = m.run(srcDir.getPath(), destDir, classNames);
   343                 if (!ok)
   344                     throw new BuildException("genstubs failed");
   345             } else {
   346                 List<String> cmd = new ArrayList<String>();
   347                 String java_home = System.getProperty("java.home");
   348                 cmd.add(new File(new File(java_home, "bin"), "java").getPath());
   349                 if (classpath != null)
   350                     cmd.add("-Xbootclasspath/p:" + classpath);
   351                 cmd.add(GenStubs.class.getName());
   352                 cmd.add("-sourcepath");
   353                 cmd.add(srcDir.getPath());
   354                 cmd.add("-s");
   355                 cmd.add(destDir.getPath());
   356                 cmd.addAll(classNames);
   357                 //System.err.println("GenStubs exec " + cmd);
   358                 ProcessBuilder pb = new ProcessBuilder(cmd);
   359                 pb.redirectErrorStream(true);
   360                 try {
   361                     Process p = pb.start();
   362                     BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
   363                     try {
   364                         String line;
   365                         while ((line = in.readLine()) != null)
   366                             System.out.println(line);
   367                     } finally {
   368                         in.close();
   369                     }
   370                     int rc = p.waitFor();
   371                     if (rc != 0)
   372                         throw new BuildException("genstubs failed");
   373                 } catch (IOException e) {
   374                     throw new BuildException("genstubs failed", e);
   375                 } catch (InterruptedException e) {
   376                     throw new BuildException("genstubs failed", e);
   377                 }
   378             }
   379         }
   381         String[] filter(File srcDir, File destDir, String[] files) {
   382             List<String> results = new ArrayList<String>();
   383             for (String f: files) {
   384                 long srcTime = new File(srcDir, f).lastModified();
   385                 long destTime = new File(destDir, f).lastModified();
   386                 if (srcTime > destTime)
   387                     results.add(f);
   388             }
   389             return results.toArray(new String[results.size()]);
   390         }
   391     }
   392 }

mercurial