Tue, 22 Jan 2013 18:43:22 -0800
8006723: sjavac test fails to compile on clean build
Reviewed-by: ksrini
1 /*
2 * Copyright (c) 2013, 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 */
24 import java.util.*;
25 import java.io.*;
26 import java.net.*;
27 import java.nio.file.*;
28 import java.nio.file.attribute.*;
29 import java.nio.charset.*;
31 import com.sun.tools.sjavac.Main;
33 public
34 class SJavac {
36 public static void main(String... args) throws Exception {
37 SJavac s = new SJavac();
38 s.test();
39 }
41 FileSystem defaultfs = FileSystems.getDefault();
43 // Where to put generated sources that will
44 // test aspects of sjavac, ie JTWork/scratch/gensrc
45 Path gensrc;
46 // More gensrc dirs are used to test merging of serveral source roots.
47 Path gensrc2;
48 Path gensrc3;
50 // Where to put compiled classes.
51 Path bin;
52 // Where to put c-header files.
53 Path headers;
55 // The sjavac compiler.
56 Main main = new Main();
58 // Remember the previous bin and headers state here.
59 Map<String,Long> previous_bin_state;
60 Map<String,Long> previous_headers_state;
62 public void test() throws Exception {
63 gensrc = defaultfs.getPath("gensrc");
64 gensrc2 = defaultfs.getPath("gensrc2");
65 gensrc3 = defaultfs.getPath("gensrc3");
66 bin = defaultfs.getPath("bin");
67 headers = defaultfs.getPath("headers");
69 Files.createDirectory(gensrc);
70 Files.createDirectory(gensrc2);
71 Files.createDirectory(gensrc3);
72 Files.createDirectory(bin);
73 Files.createDirectory(headers);
75 initialCompile();
76 incrementalCompileNoChanges();
77 incrementalCompileDroppingClasses();
78 incrementalCompileWithChange();
79 incrementalCompileDropAllNatives();
80 incrementalCompileAddNative();
81 incrementalCompileChangeNative();
82 compileWithOverrideSource();
83 compileWithInvisibleSources();
84 compileCircularSources();
86 delete(gensrc);
87 delete(gensrc2);
88 delete(gensrc3);
89 delete(bin);
90 }
92 void initialCompile() throws Exception {
93 System.out.println("\nInitial compile of gensrc.");
94 System.out.println("----------------------------");
95 populate(gensrc,
96 "alfa/AINT.java",
97 "package alfa; public interface AINT { void aint(); }",
99 "alfa/A.java",
100 "package alfa; public class A implements AINT { "+
101 "public final static int DEFINITION = 17; public void aint() { } }",
103 "alfa/AA.java",
104 "package alfa;"+
105 "// A package private class, not contributing to the public api.\n"+
106 "class AA {"+
107 " // A properly nested static inner class.\n"+
108 " static class AAA { }\n"+
109 " // A properly nested inner class.\n"+
110 " class AAAA { }\n"+
111 " Runnable foo() {\n"+
112 " // A proper anonymous class.\n"+
113 " return new Runnable() { public void run() { } };\n"+
114 " }\n"+
115 " AAA aaa;\n"+
116 " AAAA aaaa;\n"+
117 " AAAAA aaaaa;\n"+
118 "}\n"+
119 "class AAAAA {\n"+
120 " // A bad auxiliary class, but no one is referencing it\n"+
121 " // from outside of this source file, therefore it is ok.\n"+
122 "}\n",
124 "beta/BINT.java",
125 "package beta;public interface BINT { void foo(); }",
127 "beta/B.java",
128 "package beta; import alfa.A; public class B {"+
129 "private int b() { return A.DEFINITION; } native void foo(); }");
131 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
132 "--server:portfile=testserver,background=false", "--log=debug");
133 previous_bin_state = collectState(bin);
134 previous_headers_state = collectState(headers);
135 }
137 void incrementalCompileNoChanges() throws Exception {
138 System.out.println("\nTesting that no change in sources implies no change in binaries.");
139 System.out.println("------------------------------------------------------------------");
140 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
141 "--server:portfile=testserver,background=false", "--log=debug");
142 Map<String,Long> new_bin_state = collectState(bin);
143 verifyEqual(new_bin_state, previous_bin_state);
144 Map<String,Long> new_headers_state = collectState(headers);
145 verifyEqual(previous_headers_state, new_headers_state);
146 }
148 void incrementalCompileDroppingClasses() throws Exception {
149 System.out.println("\nTesting that deleting AA.java deletes all");
150 System.out.println("generated inner class as well as AA.class");
151 System.out.println("-----------------------------------------");
152 removeFrom(gensrc, "alfa/AA.java");
153 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
154 "--server:portfile=testserver,background=false", "--log=debug");
155 Map<String,Long> new_bin_state = collectState(bin);
156 verifyThatFilesHaveBeenRemoved(previous_bin_state, new_bin_state,
157 "bin/alfa/AA$1.class",
158 "bin/alfa/AA$AAAA.class",
159 "bin/alfa/AA$AAA.class",
160 "bin/alfa/AAAAA.class",
161 "bin/alfa/AA.class");
163 previous_bin_state = new_bin_state;
164 Map<String,Long> new_headers_state = collectState(headers);
165 verifyEqual(previous_headers_state, new_headers_state);
166 }
168 void incrementalCompileWithChange() throws Exception {
169 System.out.println("\nNow update the A.java file with a new timestamps and");
170 System.out.println("new final static definition. This should trigger a recompile,");
171 System.out.println("not only of alfa, but also beta.");
172 System.out.println("But check that the generated native header was not updated!");
173 System.out.println("Since we did not modify the native api of B.");
174 System.out.println("-------------------------------------------------------------");
176 populate(gensrc,"alfa/A.java",
177 "package alfa; public class A implements AINT { "+
178 "public final static int DEFINITION = 18; public void aint() { } private void foo() { } }");
180 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
181 "--server:portfile=testserver,background=false", "--log=debug");
182 Map<String,Long> new_bin_state = collectState(bin);
184 verifyNewerFiles(previous_bin_state, new_bin_state,
185 "bin/alfa/A.class",
186 "bin/alfa/AINT.class",
187 "bin/beta/B.class",
188 "bin/beta/BINT.class",
189 "bin/javac_state");
190 previous_bin_state = new_bin_state;
192 Map<String,Long> new_headers_state = collectState(headers);
193 verifyEqual(new_headers_state, previous_headers_state);
194 }
196 void incrementalCompileDropAllNatives() throws Exception {
197 System.out.println("\nNow update the B.java file with one less native method,");
198 System.out.println("ie it has no longer any methods!");
199 System.out.println("Verify that beta_B.h is removed!");
200 System.out.println("---------------------------------------------------------");
202 populate(gensrc,"beta/B.java",
203 "package beta; import alfa.A; public class B {"+
204 "private int b() { return A.DEFINITION; } }");
206 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
207 "--server:portfile=testserver,background=false", "--log=debug");
208 Map<String,Long> new_bin_state = collectState(bin);
209 verifyNewerFiles(previous_bin_state, new_bin_state,
210 "bin/beta/B.class",
211 "bin/beta/BINT.class",
212 "bin/javac_state");
213 previous_bin_state = new_bin_state;
215 Map<String,Long> new_headers_state = collectState(headers);
216 verifyThatFilesHaveBeenRemoved(previous_headers_state, new_headers_state,
217 "headers/beta_B.h");
218 previous_headers_state = new_headers_state;
219 }
221 void incrementalCompileAddNative() throws Exception {
222 System.out.println("\nNow update the B.java file with a final static annotated with @Native.");
223 System.out.println("Verify that beta_B.h is added again!");
224 System.out.println("------------------------------------------------------------------------");
226 populate(gensrc,"beta/B.java",
227 "package beta; import alfa.A; public class B {"+
228 "private int b() { return A.DEFINITION; } "+
229 "@java.lang.annotation.Native final static int alfa = 42; }");
231 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
232 "--server:portfile=testserver,background=false", "--log=debug");
233 Map<String,Long> new_bin_state = collectState(bin);
234 verifyNewerFiles(previous_bin_state, new_bin_state,
235 "bin/beta/B.class",
236 "bin/beta/BINT.class",
237 "bin/javac_state");
238 previous_bin_state = new_bin_state;
240 Map<String,Long> new_headers_state = collectState(headers);
241 verifyThatFilesHaveBeenAdded(previous_headers_state, new_headers_state,
242 "headers/beta_B.h");
243 previous_headers_state = new_headers_state;
244 }
246 void incrementalCompileChangeNative() throws Exception {
247 System.out.println("\nNow update the B.java file with a new value for the final static"+
248 " annotated with @Native.");
249 System.out.println("Verify that beta_B.h is rewritten again!");
250 System.out.println("-------------------------------------------------------------------");
252 populate(gensrc,"beta/B.java",
253 "package beta; import alfa.A; public class B {"+
254 "private int b() { return A.DEFINITION; } "+
255 "@java.lang.annotation.Native final static int alfa = 43; }");
257 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
258 "--server:portfile=testserver,background=false", "--log=debug");
259 Map<String,Long> new_bin_state = collectState(bin);
260 verifyNewerFiles(previous_bin_state, new_bin_state,
261 "bin/beta/B.class",
262 "bin/beta/BINT.class",
263 "bin/javac_state");
264 previous_bin_state = new_bin_state;
266 Map<String,Long> new_headers_state = collectState(headers);
267 verifyNewerFiles(previous_headers_state, new_headers_state,
268 "headers/beta_B.h");
269 previous_headers_state = new_headers_state;
270 }
272 void compileWithOverrideSource() throws Exception {
273 System.out.println("\nNow verify that we can override sources to be compiled.");
274 System.out.println("Compile gensrc and gensrc2. However do not compile broken beta.B in gensrc,");
275 System.out.println("only compile ok beta.B in gensrc2.");
276 System.out.println("---------------------------------------------------------------------------");
278 delete(gensrc);
279 delete(gensrc2);
280 delete(bin);
281 previous_bin_state = collectState(bin);
283 populate(gensrc,"alfa/A.java",
284 "package alfa; import beta.B; import gamma.C; public class A { B b; C c; }",
285 "beta/B.java",
286 "package beta; public class B { broken",
287 "gamma/C.java",
288 "package gamma; public class C { }");
290 populate(gensrc2,
291 "beta/B.java",
292 "package beta; public class B { }");
294 compile("-x", "beta", "gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
295 "--server:portfile=testserver,background=false");
296 Map<String,Long> new_bin_state = collectState(bin);
297 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
298 "bin/alfa/A.class",
299 "bin/beta/B.class",
300 "bin/gamma/C.class",
301 "bin/javac_state");
303 System.out.println("----- Compile with exluded beta went well!");
304 delete(bin);
305 compileExpectFailure("gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
306 "--server:portfile=testserver,background=false");
308 System.out.println("----- Compile without exluded beta failed, as expected! Good!");
309 delete(bin);
310 }
312 void compileWithInvisibleSources() throws Exception {
313 System.out.println("\nNow verify that we can make sources invisible to linking (sourcepath).");
314 System.out.println("Compile gensrc and link against gensrc2 and gensrc3, however");
315 System.out.println("gensrc2 contains broken code in beta.B, thus we must exclude that package");
316 System.out.println("fortunately gensrc3 contains a proper beta.B.");
317 System.out.println("------------------------------------------------------------------------");
319 // Start with a fresh gensrcs and bin.
320 delete(gensrc);
321 delete(gensrc2);
322 delete(gensrc3);
323 delete(bin);
324 previous_bin_state = collectState(bin);
326 populate(gensrc,"alfa/A.java",
327 "package alfa; import beta.B; import gamma.C; public class A { B b; C c; }");
328 populate(gensrc2,"beta/B.java",
329 "package beta; public class B { broken",
330 "gamma/C.java",
331 "package gamma; public class C { }");
332 populate(gensrc3, "beta/B.java",
333 "package beta; public class B { }");
335 compile("gensrc", "-x", "beta", "-sourcepath", "gensrc2",
336 "-sourcepath", "gensrc3", "-d", "bin", "-h", "headers", "-j", "1",
337 "--server:portfile=testserver,background=false");
339 System.out.println("The first compile went well!");
340 Map<String,Long> new_bin_state = collectState(bin);
341 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
342 "bin/alfa/A.class",
343 "bin/javac_state");
345 System.out.println("----- Compile with exluded beta went well!");
346 delete(bin);
347 compileExpectFailure("gensrc", "-sourcepath", "gensrc2", "-sourcepath", "gensrc3",
348 "-d", "bin", "-h", "headers", "-j", "1",
349 "--server:portfile=testserver,background=false");
351 System.out.println("----- Compile without exluded beta failed, as expected! Good!");
352 delete(bin);
353 }
355 void compileCircularSources() throws Exception {
356 System.out.println("\nNow verify that circular sources split on multiple cores can be compiled.");
357 System.out.println("---------------------------------------------------------------------------");
359 // Start with a fresh gensrcs and bin.
360 delete(gensrc);
361 delete(gensrc2);
362 delete(gensrc3);
363 delete(bin);
364 previous_bin_state = collectState(bin);
366 populate(gensrc,"alfa/A.java",
367 "package alfa; public class A { beta.B b; }",
368 "beta/B.java",
369 "package beta; public class B { gamma.C c; }",
370 "gamma/C.java",
371 "package gamma; public class C { alfa.A a; }");
373 compile("gensrc", "-d", "bin", "-h", "headers", "-j", "3",
374 "--server:portfile=testserver,background=false","--log=debug");
375 Map<String,Long> new_bin_state = collectState(bin);
376 verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
377 "bin/alfa/A.class",
378 "bin/beta/B.class",
379 "bin/gamma/C.class",
380 "bin/javac_state");
381 delete(bin);
382 }
384 void removeFrom(Path dir, String... args) throws IOException {
385 for (String filename : args) {
386 Path p = dir.resolve(filename);
387 Files.delete(p);
388 }
389 }
391 void populate(Path src, String... args) throws IOException {
392 if (!Files.exists(src)) {
393 Files.createDirectory(src);
394 }
395 String[] a = args;
396 for (int i = 0; i<a.length; i+=2) {
397 String filename = a[i];
398 String content = a[i+1];
399 Path p = src.resolve(filename);
400 Files.createDirectories(p.getParent());
401 PrintWriter out = new PrintWriter(Files.newBufferedWriter(p,
402 Charset.defaultCharset()));
403 out.println(content);
404 out.close();
405 }
406 }
408 void delete(Path root) throws IOException {
409 if (!Files.exists(root)) return;
410 Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
411 @Override
412 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
413 {
414 Files.delete(file);
415 return FileVisitResult.CONTINUE;
416 }
418 @Override
419 public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException
420 {
421 if (e == null) {
422 if (!dir.equals(root)) Files.delete(dir);
423 return FileVisitResult.CONTINUE;
424 } else {
425 // directory iteration failed
426 throw e;
427 }
428 }
429 });
430 }
432 void compile(String... args) throws Exception {
433 int rc = main.go(args, System.out, System.err);
434 if (rc != 0) throw new Exception("Error during compile!");
436 // Wait a second, to get around the (temporary) problem with
437 // second resolution in the Java file api. But do not do this
438 // on windows where the timestamps work.
439 long in_a_sec = System.currentTimeMillis()+1000;
440 while (in_a_sec > System.currentTimeMillis()) {
441 try {
442 Thread.sleep(1000);
443 } catch (InterruptedException e) {
444 }
445 }
446 }
448 void compileExpectFailure(String... args) throws Exception {
449 int rc = main.go(args, System.out, System.err);
450 if (rc == 0) throw new Exception("Expected error during compile! Did not fail!");
451 }
453 Map<String,Long> collectState(Path dir) throws IOException
454 {
455 final Map<String,Long> files = new HashMap<>();
456 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
457 @Override
458 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
459 throws IOException
460 {
461 files.put(file.toString(),new Long(Files.getLastModifiedTime(file).toMillis()));
462 return FileVisitResult.CONTINUE;
463 }
464 });
465 return files;
466 }
468 void verifyThatFilesHaveBeenRemoved(Map<String,Long> from,
469 Map<String,Long> to,
470 String... args) throws Exception {
472 Set<String> froms = from.keySet();
473 Set<String> tos = to.keySet();
475 if (froms.equals(tos)) {
476 throw new Exception("Expected new state to have fewer files than previous state!");
477 }
479 for (String t : tos) {
480 if (!froms.contains(t)) {
481 throw new Exception("Expected "+t+" to exist in previous state!");
482 }
483 }
485 for (String f : args) {
486 f = f.replace("/", File.separator);
487 if (!froms.contains(f)) {
488 throw new Exception("Expected "+f+" to exist in previous state!");
489 }
490 if (tos.contains(f)) {
491 throw new Exception("Expected "+f+" to have been removed from the new state!");
492 }
493 }
495 if (froms.size() - args.length != tos.size()) {
496 throw new Exception("There are more removed files than the expected list!");
497 }
498 }
500 void verifyThatFilesHaveBeenAdded(Map<String,Long> from,
501 Map<String,Long> to,
502 String... args) throws Exception {
504 Set<String> froms = from.keySet();
505 Set<String> tos = to.keySet();
507 if (froms.equals(tos)) {
508 throw new Exception("Expected new state to have more files than previous state!");
509 }
511 for (String t : froms) {
512 if (!tos.contains(t)) {
513 throw new Exception("Expected "+t+" to exist in new state!");
514 }
515 }
517 for (String f : args) {
518 f = f.replace("/", File.separator);
519 if (!tos.contains(f)) {
520 throw new Exception("Expected "+f+" to have been added to new state!");
521 }
522 if (froms.contains(f)) {
523 throw new Exception("Expected "+f+" to not exist in previous state!");
524 }
525 }
527 if (froms.size() + args.length != tos.size()) {
528 throw new Exception("There are more added files than the expected list!");
529 }
530 }
532 void verifyNewerFiles(Map<String,Long> from,
533 Map<String,Long> to,
534 String... args) throws Exception {
535 if (!from.keySet().equals(to.keySet())) {
536 throw new Exception("Expected the set of files to be identical!");
537 }
538 Set<String> files = new HashSet<String>();
539 for (String s : args) {
540 files.add(s.replace("/", File.separator));
541 }
542 for (String fn : from.keySet()) {
543 long f = from.get(fn);
544 long t = to.get(fn);
545 if (files.contains(fn)) {
546 if (t <= f) {
547 throw new Exception("Expected "+fn+" to have a more recent timestamp!");
548 }
549 } else {
550 if (t != f) {
551 throw new Exception("Expected "+fn+" to have the same timestamp!");
552 }
553 }
554 }
555 }
557 String print(Map<String,Long> m) {
558 StringBuilder b = new StringBuilder();
559 Set<String> keys = m.keySet();
560 for (String k : keys) {
561 b.append(k+" "+m.get(k)+"\n");
562 }
563 return b.toString();
564 }
566 void verifyEqual(Map<String,Long> from, Map<String,Long> to) throws Exception {
567 if (!from.equals(to)) {
568 System.out.println("FROM---"+print(from));
569 System.out.println("TO-----"+print(to));
570 throw new Exception("The dir should not differ! But it does!");
571 }
572 }
573 }