test/tools/javac/diags/Example.java

changeset 610
3640b60bd0f6
child 616
e79e8efe1b3e
equal deleted inserted replaced
609:13354e1abba7 610:3640b60bd0f6
1 /*
2 * Copyright (c) 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 import com.sun.tools.javac.file.JavacFileManager;
25 import java.io.*;
26 import java.util.*;
27 import java.util.regex.*;
28 import javax.tools.Diagnostic;
29 import javax.tools.DiagnosticCollector;
30 import javax.tools.JavaCompiler;
31 import javax.tools.JavaCompiler.CompilationTask;
32 import javax.tools.JavaFileObject;
33 import javax.tools.StandardJavaFileManager;
34 import javax.tools.ToolProvider;
35
36 // The following two classes are both used, but cannot be imported directly
37 // import com.sun.tools.javac.Main
38 // import com.sun.tools.javac.main.Main
39
40 import com.sun.tools.javac.util.Context;
41 import com.sun.tools.javac.util.JavacMessages;
42 import com.sun.tools.javac.util.JCDiagnostic;
43 import java.net.URL;
44 import java.net.URLClassLoader;
45 import javax.annotation.processing.Processor;
46
47 /**
48 * Class to handle example code designed to illustrate javac diagnostic messages.
49 */
50 class Example implements Comparable<Example> {
51 /* Create an Example from the files found at path.
52 * The head of the file, up to the first Java code, is scanned
53 * for information about the test, such as what resource keys it
54 * generates when run, what options are required to run it, and so on.
55 */
56 Example(File file) {
57 this.file = file;
58 declaredKeys = new TreeSet<String>();
59 srcFiles = new ArrayList<File>();
60 procFiles = new ArrayList<File>();
61 supportFiles = new ArrayList<File>();
62 srcPathFiles = new ArrayList<File>();
63
64 findFiles(file, srcFiles);
65 for (File f: srcFiles) {
66 parse(f);
67 }
68
69 if (infoFile == null)
70 throw new Error("Example " + file + " has no info file");
71 }
72
73 private void findFiles(File f, List<File> files) {
74 if (f.isDirectory()) {
75 for (File c: f.listFiles()) {
76 if (files == srcFiles && c.getName().equals("processors"))
77 findFiles(c, procFiles);
78 else if (files == srcFiles && c.getName().equals("sourcepath")) {
79 srcPathDir = c;
80 findFiles(c, srcPathFiles);
81 } else if (files == srcFiles && c.getName().equals("support"))
82 findFiles(c, supportFiles);
83 else
84 findFiles(c, files);
85 }
86 } else if (f.isFile() && f.getName().endsWith(".java")) {
87 files.add(f);
88 }
89 }
90
91 private void parse(File f) {
92 Pattern keyPat = Pattern.compile(" *// *key: *([^ ]+) *");
93 Pattern optPat = Pattern.compile(" *// *options: *(.*)");
94 Pattern runPat = Pattern.compile(" *// *run: *(.*)");
95 Pattern javaPat = Pattern.compile(" *@?[A-Za-z].*");
96 try {
97 String[] lines = read(f).split("[\r\n]+");
98 for (String line: lines) {
99 Matcher keyMatch = keyPat.matcher(line);
100 if (keyMatch.matches()) {
101 foundInfo(f);
102 declaredKeys.add(keyMatch.group(1));
103 continue;
104 }
105 Matcher optMatch = optPat.matcher(line);
106 if (optMatch.matches()) {
107 foundInfo(f);
108 options = Arrays.asList(optMatch.group(1).trim().split(" +"));
109 continue;
110 }
111 Matcher runMatch = runPat.matcher(line);
112 if (runMatch.matches()) {
113 foundInfo(f);
114 runOpts = Arrays.asList(runMatch.group(1).trim().split(" +"));
115 }
116 if (javaPat.matcher(line).matches())
117 break;
118 }
119 } catch (IOException e) {
120 throw new Error(e);
121 }
122 }
123
124 private void foundInfo(File file) {
125 if (infoFile != null && !infoFile.equals(file))
126 throw new Error("multiple info files found: " + infoFile + ", " + file);
127 infoFile = file;
128 }
129
130 String getName() {
131 return file.getName();
132 }
133
134 /**
135 * Get the set of resource keys that this test declares it will generate
136 * when it is run.
137 */
138 Set<String> getDeclaredKeys() {
139 return declaredKeys;
140 }
141
142 /**
143 * Get the set of resource keys that this test generates when it is run.
144 * The test will be run if it has not already been run.
145 */
146 Set<String> getActualKeys() {
147 if (actualKeys == null)
148 actualKeys = run(false);
149 return actualKeys;
150 }
151
152 /**
153 * Run the test. Information in the test header is used to determine
154 * how to run the test.
155 */
156 void run(PrintWriter out, boolean raw, boolean verbose) {
157 if (out == null)
158 throw new NullPointerException();
159 try {
160 run(out, null, raw, verbose);
161 } catch (IOException e) {
162 e.printStackTrace(out);
163 }
164 }
165
166 Set<String> run(boolean verbose) {
167 Set<String> keys = new TreeSet<String>();
168 try {
169 run(null, keys, true, verbose);
170 } catch (IOException e) {
171 e.printStackTrace();
172 }
173 return keys;
174 }
175
176 /**
177 * Run the test. Information in the test header is used to determine
178 * how to run the test.
179 */
180 private void run(PrintWriter out, Set<String> keys, boolean raw, boolean verbose)
181 throws IOException {
182 ClassLoader loader = getClass().getClassLoader();
183 if (supportFiles.size() > 0) {
184 File supportDir = new File(tempDir, "support");
185 supportDir.mkdirs();
186 clean(supportDir);
187 List<String> sOpts = Arrays.asList("-d", supportDir.getPath());
188 new Jsr199Compiler(verbose).run(null, null, false, sOpts, procFiles);
189 URLClassLoader ucl =
190 new URLClassLoader(new URL[] { supportDir.toURI().toURL() }, loader);
191 loader = ucl;
192 }
193
194 File classesDir = new File(tempDir, "classes");
195 classesDir.mkdirs();
196 clean(classesDir);
197
198 List<String> opts = new ArrayList<String>();
199 opts.add("-d");
200 opts.add(classesDir.getPath());
201 if (options != null)
202 opts.addAll(options);
203
204 if (procFiles.size() > 0) {
205 List<String> pOpts = Arrays.asList("-d", classesDir.getPath());
206 new Jsr199Compiler(verbose).run(null, null, false, pOpts, procFiles);
207 opts.add("-classpath"); // avoid using -processorpath for now
208 opts.add(classesDir.getPath());
209 createAnnotationServicesFile(classesDir, procFiles);
210 }
211
212 if (srcPathDir != null) {
213 opts.add("-sourcepath");
214 opts.add(srcPathDir.getPath());
215 }
216
217 try {
218 Compiler c = Compiler.getCompiler(runOpts, verbose);
219 c.run(out, keys, raw, opts, srcFiles);
220 } catch (IllegalArgumentException e) {
221 if (out != null) {
222 out.println("Invalid value for run tag: " + runOpts);
223 }
224 }
225 }
226
227 void createAnnotationServicesFile(File dir, List<File> procFiles) throws IOException {
228 File servicesDir = new File(new File(dir, "META-INF"), "services");
229 servicesDir.mkdirs();
230 File annoServices = new File(servicesDir, Processor.class.getName());
231 Writer out = new FileWriter(annoServices);
232 try {
233 for (File f: procFiles) {
234 out.write(f.getName().toString().replace(".java", ""));
235 }
236 } finally {
237 out.close();
238 }
239 }
240
241 @Override
242 public int compareTo(Example e) {
243 return file.compareTo(e.file);
244 }
245
246 @Override
247 public String toString() {
248 return file.getPath();
249 }
250
251 /**
252 * Read the contents of a file.
253 */
254 private String read(File f) throws IOException {
255 byte[] bytes = new byte[(int) f.length()];
256 DataInputStream in = new DataInputStream(new FileInputStream(f));
257 try {
258 in.readFully(bytes);
259 } finally {
260 in.close();
261 }
262 return new String(bytes);
263 }
264
265 /**
266 * Clean the contents of a directory.
267 */
268 boolean clean(File dir) {
269 boolean ok = true;
270 for (File f: dir.listFiles()) {
271 if (f.isDirectory())
272 ok &= clean(f);
273 ok &= f.delete();
274 }
275 return ok;
276 }
277
278 File file;
279 List<File> srcFiles;
280 List<File> procFiles;
281 File srcPathDir;
282 List<File> srcPathFiles;
283 List<File> supportFiles;
284 File infoFile;
285 private List<String> runOpts;
286 private List<String> options;
287 private Set<String> actualKeys;
288 private Set<String> declaredKeys;
289
290 static File tempDir = new File(System.getProperty("java.io.tmpdir"));
291 static void setTempDir(File tempDir) {
292 Example.tempDir = tempDir;
293 }
294
295 abstract static class Compiler {
296 static Compiler getCompiler(List<String> opts, boolean verbose) {
297 String first;
298 String[] rest;
299 if (opts == null || opts.size() == 0) {
300 first = null;
301 rest = new String[0];
302 } else {
303 first = opts.get(0);
304 rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]);
305 }
306 if (first == null || first.equals("jsr199"))
307 return new Jsr199Compiler(verbose, rest);
308 else if (first.equals("simple"))
309 return new SimpleCompiler(verbose);
310 else if (first.equals("backdoor"))
311 return new BackdoorCompiler(verbose);
312 else
313 throw new IllegalArgumentException(first);
314 }
315
316 protected Compiler(boolean verbose) {
317 this.verbose = verbose;
318 }
319
320 abstract boolean run(PrintWriter out, Set<String> keys, boolean raw,
321 List<String> opts, List<File> files);
322
323 void setSupportClassLoader(ClassLoader cl) {
324 loader = cl;
325 }
326
327 protected ClassLoader loader;
328 protected boolean verbose;
329 }
330
331 /**
332 * Compile using the JSR 199 API. The diagnostics generated are
333 * scanned for resource keys. Not all diagnostic keys are generated
334 * via the JSR 199 API -- for example, rich diagnostics are not directly
335 * accessible, and some diagnostics generated by the file manager may
336 * not be generated (for example, the JSR 199 file manager does not see
337 * -Xlint:path).
338 */
339 static class Jsr199Compiler extends Compiler {
340 List<String> fmOpts;
341
342 Jsr199Compiler(boolean verbose, String... args) {
343 super(verbose);
344 for (int i = 0; i < args.length; i++) {
345 String arg = args[i];
346 if (arg.equals("-filemanager") && (i + 1 < args.length)) {
347 fmOpts = Arrays.asList(args[++i].split(","));
348 } else
349 throw new IllegalArgumentException(arg);
350 }
351 }
352
353 @Override
354 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
355 if (out != null && keys != null)
356 throw new IllegalArgumentException();
357
358 if (verbose)
359 System.err.println("run_jsr199: " + opts + " " + files);
360
361 DiagnosticCollector<JavaFileObject> dc = null;
362 if (keys != null)
363 dc = new DiagnosticCollector<JavaFileObject>();
364
365 if (raw) {
366 List<String> newOpts = new ArrayList<String>();
367 newOpts.add("-XDrawDiagnostics");
368 newOpts.addAll(opts);
369 opts = newOpts;
370 }
371
372 JavaCompiler c = ToolProvider.getSystemJavaCompiler();
373
374 StandardJavaFileManager fm = c.getStandardFileManager(dc, null, null);
375 if (fmOpts != null)
376 fm = new FileManager(fm, fmOpts);
377
378 Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
379
380 CompilationTask t = c.getTask(out, fm, dc, opts, null, fos);
381 Boolean ok = t.call();
382
383 if (keys != null) {
384 for (Diagnostic<? extends JavaFileObject> d: dc.getDiagnostics()) {
385 scanForKeys((JCDiagnostic) d, keys);
386 }
387 }
388
389 return ok;
390 }
391
392 /**
393 * Scan a diagnostic for resource keys. This will not detect additional
394 * sub diagnostics that might be generated by a rich diagnostic formatter.
395 */
396 private static void scanForKeys(JCDiagnostic d, Set<String> keys) {
397 keys.add(d.getCode());
398 for (Object o: d.getArgs()) {
399 if (o instanceof JCDiagnostic) {
400 scanForKeys((JCDiagnostic) o, keys);
401 }
402 }
403 for (JCDiagnostic sd: d.getSubdiagnostics())
404 scanForKeys(d, keys);
405 }
406 }
407
408 /**
409 * Run the test using the standard simple entry point.
410 */
411 static class SimpleCompiler extends Compiler {
412 SimpleCompiler(boolean verbose) {
413 super(verbose);
414 }
415
416 @Override
417 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
418 if (out != null && keys != null)
419 throw new IllegalArgumentException();
420
421 if (verbose)
422 System.err.println("run_simple: " + opts + " " + files);
423
424 List<String> args = new ArrayList<String>(opts);
425
426 if (keys != null || raw)
427 args.add("-XDrawDiagnostics");
428
429 args.addAll(opts);
430 for (File f: files)
431 args.add(f.getPath());
432
433 StringWriter sw = null;
434 PrintWriter pw;
435 if (keys != null) {
436 sw = new StringWriter();
437 pw = new PrintWriter(sw);
438 } else
439 pw = out;
440
441 int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
442
443 if (keys != null) {
444 pw.close();
445 scanForKeys(sw.toString(), keys);
446 }
447
448 return (rc == 0);
449 }
450
451 private static void scanForKeys(String text, Set<String> keys) {
452 StringTokenizer st = new StringTokenizer(text, " ,\r\n():");
453 while (st.hasMoreElements()) {
454 String t = st.nextToken();
455 if (t.startsWith("compiler."))
456 keys.add(t);
457 }
458 }
459 }
460
461 static class BackdoorCompiler extends Compiler {
462 BackdoorCompiler(boolean verbose) {
463 super(verbose);
464 }
465
466 @Override
467 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
468 if (out != null && keys != null)
469 throw new IllegalArgumentException();
470
471 if (verbose)
472 System.err.println("run_simple: " + opts + " " + files);
473
474 List<String> args = new ArrayList<String>(opts);
475
476 if (out != null && raw)
477 args.add("-XDrawDiagnostics");
478
479 args.addAll(opts);
480 for (File f: files)
481 args.add(f.getPath());
482
483 StringWriter sw = null;
484 PrintWriter pw;
485 if (keys != null) {
486 sw = new StringWriter();
487 pw = new PrintWriter(sw);
488 } else
489 pw = out;
490
491 Context c = new Context();
492 JavacFileManager.preRegister(c); // can't create it until Log has been set up
493 MessageTracker.preRegister(c, keys);
494 com.sun.tools.javac.main.Main m = new com.sun.tools.javac.main.Main("javac", pw);
495 int rc = m.compile(args.toArray(new String[args.size()]), c);
496
497 if (keys != null) {
498 pw.close();
499 }
500
501 return (rc == 0);
502 }
503
504 static class MessageTracker extends JavacMessages {
505 static void preRegister(Context c, final Set<String> keys) {
506 if (keys != null) {
507 c.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() {
508 public JavacMessages make() {
509 return new MessageTracker() {
510 @Override
511 public String getLocalizedString(Locale l, String key, Object... args) {
512 keys.add(key);
513 return super.getLocalizedString(l, key, args);
514 }
515 };
516 }
517 });
518 }
519 }
520 }
521
522 }
523 }

mercurial