make/tools/GenStubs/GenStubs.java

changeset 441
4325b440eb3e
child 468
51011e02c02f
equal deleted inserted replaced
440:243d0be1ba99 441:4325b440eb3e
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;
30
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;
36
37
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;
53
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 */
86
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 }
98
99 public static void main(String[] args) {
100 boolean ok = new GenStubs().run(args);
101 if (!ok)
102 System.exit(1);
103 }
104
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 }
123
124 return run(sourcepath, outdir, classes);
125 }
126
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");
133
134 JavacTool tool = JavacTool.create();
135 StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
136
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 }
149
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 }
158
159 return (errors == 0);
160 }
161
162 void makeStub(StandardJavaFileManager fm, CompilationUnitTree tree) throws IOException {
163 CompilationUnitTree tree2 = new StubMaker().translate(tree);
164
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 }
176
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 }
185
186 void error(String message) {
187 System.err.println(message);
188 errors++;
189 }
190
191 void error(String message, Throwable cause) {
192 error(message);
193 }
194
195 int errors;
196
197 class StubMaker extends TreeTranslator {
198 CompilationUnitTree translate(CompilationUnitTree tree) {
199 return super.translate((JCCompilationUnit) tree);
200 }
201
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 }
211
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 }
228
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 }
237
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 }
274
275 //---------- Ant Invocation ------------------------------------------------
276
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;
283
284 public void setSrcDir(File dir) {
285 this.srcDir = dir;
286 }
287
288 public void setDestDir(File dir) {
289 this.destDir = dir;
290 }
291
292 public void setFork(boolean v) {
293 this.fork = v;
294 }
295
296 public void setClasspath(Path cp) {
297 if (classpath == null)
298 classpath = cp;
299 else
300 classpath.append(cp);
301 }
302
303 public Path createClasspath() {
304 if (classpath == null) {
305 classpath = new Path(getProject());
306 }
307 return classpath.createPath();
308 }
309
310 public void setClasspathRef(Reference r) {
311 createClasspath().setRefid(r);
312 }
313
314 public void setIncludes(String includes) {
315 super.setIncludes(includes);
316 this.includes = includes;
317 }
318
319 @Override
320 public void execute() {
321 if (includes != null && includes.trim().isEmpty())
322 return;
323
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));
329
330 files = filter(srcDir, destDir, files);
331 if (files.length == 0)
332 return;
333 System.out.println("Generating " + files.length + " stub files to " + destDir);
334
335 List<String> classNames = new ArrayList<String>();
336 for (String file: files) {
337 classNames.add(file.replaceAll(".java$", "").replace('/', '.'));
338 }
339
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 }
380
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