TestSearchPaths.java revision 2455:01c43036a26e
1/* 2 * Copyright (c) 2014, 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/* 25 * @test 26 * @bug 7026941 27 * @summary path options ignored when reusing filemanager across tasks 28 */ 29 30import java.io.File; 31import java.io.FileWriter; 32import java.io.IOException; 33import java.io.PrintWriter; 34import java.net.URI; 35import java.nio.file.Files; 36import java.util.ArrayList; 37import java.util.Arrays; 38import java.util.Collections; 39import java.util.EnumSet; 40import java.util.List; 41import java.util.Objects; 42import java.util.jar.JarEntry; 43import java.util.jar.JarOutputStream; 44import java.util.regex.Matcher; 45import java.util.regex.Pattern; 46 47import javax.tools.JavaCompiler; 48import javax.tools.JavaCompiler.CompilationTask; 49import javax.tools.JavaFileObject; 50import javax.tools.SimpleJavaFileObject; 51import javax.tools.StandardJavaFileManager; 52import javax.tools.StandardLocation; 53import javax.tools.ToolProvider; 54 55import static javax.tools.StandardLocation.*; 56 57/** 58 * Test for combinations of using javac command-line options and fileManager setLocation 59 * calls to affect the locations available in the fileManager. 60 * 61 * Using a single Java compiler and file manager, for each of the standard locations, 62 * a series of operations is performed, using either compiler options or setLocation 63 * calls. Each operation includes a compilation, and then a check for the value of 64 * the standard location available in the file manager. 65 * 66 * The operations generate and use unique files to minimize the possibility of false 67 * positive results. 68 */ 69public class TestSearchPaths { 70 71 public static void main(String... args) throws Exception { 72 TestSearchPaths t = new TestSearchPaths(); 73 t.run(); 74 } 75 76 void run() throws Exception { 77 compiler = ToolProvider.getSystemJavaCompiler(); 78 fileManager = compiler.getStandardFileManager(null, null, null); 79 80 // basic output path 81 testClassOutput(); 82 83 // basic search paths 84 testClassPath(); 85 testSourcePath(); 86 testPlatformClassPath(); 87 88 // annotation processing 89 testAnnotationProcessorPath(); 90 testSourceOutput(); 91 92 // javah equivalent 93 testNativeHeaderOutput(); 94 95 // future-proof: guard against new StandardLocations being added 96 if (!tested.equals(EnumSet.allOf(StandardLocation.class))) { 97 error("not all standard locations have been tested"); 98 out.println("not yet tested: " + EnumSet.complementOf(tested)); 99 } 100 101 if (errors > 0) { 102 throw new Exception(errors + " errors occurred"); 103 } 104 } 105 106 void testClassOutput() throws IOException { 107 String test = "testClassOutput"; 108 109 for (int i = 1; i <= 5; i++) { 110 File classes = createDir(test + "/" + i + "/classes"); 111 List<String> options; 112 switch (i) { 113 default: 114 options = getOptions("-d", classes.getPath()); 115 break; 116 117 case 3: 118 setLocation(CLASS_OUTPUT, classes); 119 options = null; 120 break; 121 } 122 List<JavaFileObject> sources = getSources("class C" + i + " { }"); 123 callTask(options, sources); 124 checkPath(CLASS_OUTPUT, Mode.EQUALS, classes); 125 checkFile(CLASS_OUTPUT, "C" + i + ".class"); 126 } 127 128 tested.add(CLASS_OUTPUT); 129 } 130 131 void testClassPath() throws IOException { 132 String test = "testClassPath"; 133 134 for (int i = 1; i <= 5; i++) { 135 File classes = createDir(test + "/" + i + "/classes"); 136 File classpath = new File("testClassOutput/" + i + "/classes"); 137 List<String> options; 138 switch (i) { 139 default: 140 options = getOptions("-d", classes.getPath(), "-classpath", classpath.getPath()); 141 break; 142 143 case 3: 144 setLocation(CLASS_PATH, classpath); 145 options = getOptions("-d", classes.getPath()); 146 break; 147 148 case 4: 149 options = getOptions("-d", classes.getPath(), "-cp", classpath.getPath()); 150 break; 151 } 152 List<JavaFileObject> sources = getSources("class D" + i + " { C" + i + " c; }"); 153 callTask(options, sources); 154 checkPath(CLASS_PATH, Mode.EQUALS, classpath); 155 checkFile(CLASS_OUTPUT, "D" + i + ".class"); 156 } 157 158 tested.add(CLASS_PATH); 159 } 160 161 void testSourcePath() throws IOException { 162 String test = "testSourcePath"; 163 setLocation(CLASS_PATH); // empty 164 165 for (int i = 1; i <= 5; i++) { 166 File src = createDir(test + "/" + i + "/src"); 167 writeFile(src, "C" + i + ".java", "class C" + i + "{ }"); 168 File classes = createDir(test + "/" + i + "/classes"); 169 File srcpath = src; 170 List<String> options; 171 switch (i) { 172 default: 173 options = getOptions("-d", classes.getPath(), "-sourcepath", srcpath.getPath()); 174 break; 175 176 case 3: 177 setLocation(SOURCE_PATH, srcpath); 178 options = getOptions("-d", classes.getPath()); 179 break; 180 } 181 List<JavaFileObject> sources = getSources("class D" + i + " { C" + i + " c; }"); 182 callTask(options, sources); 183 checkPath(SOURCE_PATH, Mode.EQUALS, srcpath); 184 checkFile(CLASS_OUTPUT, "D" + i + ".class"); 185 } 186 187 tested.add(SOURCE_PATH); 188 } 189 190 void testPlatformClassPath() throws IOException { 191 String test = "testPlatformClassPath"; 192 193 List<File> defaultPath = getLocation(PLATFORM_CLASS_PATH); 194 StringBuilder sb = new StringBuilder(); 195 for (File f: defaultPath) { 196 if (sb.length() > 0) 197 sb.append(File.pathSeparator); 198 sb.append(f); 199 } 200 String defaultPathString = sb.toString(); 201 202 setLocation(CLASS_PATH); // empty 203 setLocation(SOURCE_PATH); // empty 204 205 for (int i = 1; i <= 10; i++) { 206 File classes = createDir(test + "/" + i + "/classes"); 207 File testJars = createDir(test + "/" + i + "/testJars"); 208 File testClasses = createDir(test + "/" + i + "/testClasses"); 209 callTask(getOptions("-d", testClasses.getPath()), getSources("class C" + i + " { }")); 210 211 List<String> options; 212 Mode mode; 213 List<File> match; 214 String reference = "C" + i + " c;"; 215 216 File jar; 217 218 switch (i) { 219 case 1: 220 options = getOptions("-d", classes.getPath(), "-Xbootclasspath/p:" + testClasses); 221 mode = Mode.STARTS_WITH; 222 match = Arrays.asList(testClasses); 223 break; 224 225 case 2: 226 // the default values for -extdirs and -endorseddirs come after the bootclasspath; 227 // so to check -Xbootclasspath/a: we specify empty values for those options. 228 options = getOptions("-d", classes.getPath(), 229 "-Xbootclasspath/a:" + testClasses, 230 "-extdirs", "", 231 "-endorseddirs", ""); 232 mode = Mode.ENDS_WITH; 233 match = Arrays.asList(testClasses); 234 break; 235 236 case 3: 237 options = getOptions("-d", classes.getPath(), "-Xbootclasspath:" + defaultPathString); 238 mode = Mode.EQUALS; 239 match = defaultPath; 240 reference = ""; 241 break; 242 243 case 4: 244 fileManager.setLocation(PLATFORM_CLASS_PATH, null); 245 jar = new File(testJars, "j" + i + ".jar"); 246 writeJar(jar, testClasses, "C" + i + ".class"); 247 options = getOptions("-d", classes.getPath(), "-endorseddirs", testJars.getPath()); 248 mode = Mode.CONTAINS; 249 match = Arrays.asList(jar); 250 break; 251 252 case 5: 253 fileManager.setLocation(PLATFORM_CLASS_PATH, null); 254 jar = new File(testJars, "j" + i + ".jar"); 255 writeJar(jar, testClasses, "C" + i + ".class"); 256 options = getOptions("-d", classes.getPath(), "-Djava.endorsed.dirs=" + testJars.getPath()); 257 mode = Mode.CONTAINS; 258 match = Arrays.asList(jar); 259 break; 260 261 case 6: 262 fileManager.setLocation(PLATFORM_CLASS_PATH, null); 263 jar = new File(testJars, "j" + i + ".jar"); 264 writeJar(jar, testClasses, "C" + i + ".class"); 265 options = getOptions("-d", classes.getPath(), "-extdirs", testJars.getPath()); 266 mode = Mode.CONTAINS; 267 match = Arrays.asList(jar); 268 break; 269 270 case 7: 271 fileManager.setLocation(PLATFORM_CLASS_PATH, null); 272 jar = new File(testJars, "j" + i + ".jar"); 273 writeJar(jar, testClasses, "C" + i + ".class"); 274 options = getOptions("-d", classes.getPath(), "-Djava.ext.dirs=" + testJars.getPath()); 275 mode = Mode.CONTAINS; 276 match = Arrays.asList(jar); 277 break; 278 279 case 8: 280 setLocation(PLATFORM_CLASS_PATH, defaultPath); 281 options = getOptions("-d", classes.getPath()); 282 mode = Mode.EQUALS; 283 match = defaultPath; 284 reference = ""; 285 break; 286 287 default: 288 options = getOptions("-d", classes.getPath(), "-bootclasspath", defaultPathString); 289 mode = Mode.EQUALS; 290 match = defaultPath; 291 reference = ""; 292 break; 293 } 294 List<JavaFileObject> sources = getSources("class D" + i + " { " + reference + " }"); 295 296 callTask(options, sources); 297 checkPath(PLATFORM_CLASS_PATH, mode, match); 298 checkFile(CLASS_OUTPUT, "D" + i + ".class"); 299 } 300 301 tested.add(PLATFORM_CLASS_PATH); 302 } 303 304 void testAnnotationProcessorPath() throws IOException { 305 String test = "testAnnotationProcessorPath"; 306 307 String template = 308 "import java.util.*;\n" 309 + "import javax.annotation.processing.*;\n" 310 + "import javax.lang.model.*;\n" 311 + "import javax.lang.model.element.*;\n" 312 + "@SupportedAnnotationTypes(\"*\")\n" 313 + "public class A%d extends AbstractProcessor {\n" 314 + " public boolean process(Set<? extends TypeElement> annos, RoundEnvironment rEnv) {\n" 315 + " return true;\n" 316 + " }\n" 317 + " public SourceVersion getSupportedSourceVersion() {\n" 318 + " return SourceVersion.latest();\n" 319 + " }\n" 320 + "}"; 321 322 for (int i = 1; i <= 5; i++) { 323 File classes = createDir(test + "/" + i + "/classes"); 324 File annodir = createDir(test + "/" + i + "/processors"); 325 callTask(getOptions("-d", annodir.getPath()), getSources(String.format(template, i))); 326 File annopath = annodir; 327 List<String> options; 328 switch (i) { 329 default: 330 options = getOptions("-d", classes.getPath(), 331 "-processorpath", annopath.getPath(), 332 "-processor", "A" + i); 333 break; 334 335 case 3: 336 setLocation(ANNOTATION_PROCESSOR_PATH, annopath); 337 options = getOptions("-d", classes.getPath(), 338 "-processor", "A" + i); 339 break; 340 } 341 List<JavaFileObject> sources = getSources("class D" + i + " { }"); 342 callTask(options, sources); 343 checkPath(ANNOTATION_PROCESSOR_PATH, Mode.EQUALS, annopath); 344 checkFile(CLASS_OUTPUT, "D" + i + ".class"); 345 } 346 347 tested.add(ANNOTATION_PROCESSOR_PATH); 348 } 349 350 void testSourceOutput() throws IOException { 351 String test = "testAnnotationProcessorPath"; 352 353 String source = 354 "import java.io.*;\n" 355 + "import java.util.*;\n" 356 + "import javax.annotation.processing.*;\n" 357 + "import javax.lang.model.*;\n" 358 + "import javax.lang.model.element.*;\n" 359 + "import javax.tools.*;\n" 360 + "@SupportedOptions(\"name\")\n" 361 + "@SupportedAnnotationTypes(\"*\")\n" 362 + "public class A extends AbstractProcessor {\n" 363 + " int round = 0;\n" 364 + " public boolean process(Set<? extends TypeElement> annos, RoundEnvironment rEnv) {\n" 365 + " if (round++ == 0) try {\n" 366 + " String name = processingEnv.getOptions().get(\"name\");\n" 367 + " JavaFileObject fo = processingEnv.getFiler().createSourceFile(name);\n" 368 + " try (Writer out = fo.openWriter()) {\n" 369 + " out.write(\"class \" + name + \" { }\");\n" 370 + " }\n" 371 + " } catch (IOException e) { throw new Error(e); }\n" 372 + " return true;\n" 373 + " }\n" 374 + " public SourceVersion getSupportedSourceVersion() {\n" 375 + " return SourceVersion.latest();\n" 376 + " }\n" 377 + "}"; 378 379 File annodir = createDir(test + "/processors"); 380 callTask(getOptions("-d", annodir.getPath()), getSources(source)); 381 setLocation(ANNOTATION_PROCESSOR_PATH, annodir); 382 383 for (int i = 1; i <= 5; i++) { 384 File classes = createDir(test + "/" + i + "/classes"); 385 File genSrc = createDir(test + "/" + "/genSrc"); 386 List<String> options; 387 switch (i) { 388 default: 389 options = getOptions("-d", classes.getPath(), 390 "-processor", "A", "-Aname=G" + i, 391 "-s", genSrc.getPath()); 392 break; 393 394 case 3: 395 setLocation(SOURCE_OUTPUT, genSrc); 396 options = getOptions("-d", classes.getPath(), 397 "-processor", "A", "-Aname=G" + i); 398 break; 399 } 400 List<JavaFileObject> sources = getSources("class D" + i + " { }"); 401 callTask(options, sources); 402 checkPath(SOURCE_OUTPUT, Mode.EQUALS, genSrc); 403 checkFile(CLASS_OUTPUT, "D" + i + ".class"); 404 checkFile(CLASS_OUTPUT, "G" + i + ".class"); 405 } 406 tested.add(SOURCE_OUTPUT); 407 } 408 409 void testNativeHeaderOutput() throws IOException { 410 String test = "testNativeHeaderOutput"; 411 412 for (int i = 1; i <= 5; i++) { 413 File classes = createDir(test + "/" + i + "/classes"); 414 File headers = createDir(test + "/" + i + "/hdrs"); 415 List<String> options; 416 switch (i) { 417 default: 418 options = getOptions("-d", classes.getPath(), "-h", headers.getPath()); 419 break; 420 421 case 3: 422 setLocation(NATIVE_HEADER_OUTPUT, headers); 423 options = getOptions("-d", classes.getPath()); 424 break; 425 } 426 List<JavaFileObject> sources = getSources("class C" + i + " { native void m(); }"); 427 callTask(options, sources); 428 checkPath(NATIVE_HEADER_OUTPUT, Mode.EQUALS, headers); 429 checkFile(NATIVE_HEADER_OUTPUT, "C" + i + ".h"); 430 } 431 432 tested.add(StandardLocation.NATIVE_HEADER_OUTPUT); 433 } 434 435 List<String> getOptions(String... args) { 436 return Arrays.asList(args); 437 } 438 439 List<JavaFileObject> getSources(String... sources) { 440 List<JavaFileObject> list = new ArrayList<>(); 441 for (String s: sources) 442 list.add(getSource(s)); 443 return list; 444 } 445 446 JavaFileObject getSource(final String source) { 447 return new SimpleJavaFileObject(getURIFromSource(source), JavaFileObject.Kind.SOURCE) { 448 @Override 449 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 450 return source; 451 } 452 }; 453 } 454 455 void callTask(List<String> options, List<JavaFileObject> files) { 456 out.print("compile: "); 457 if (options != null) { 458 for (String o: options) { 459 if (o.length() > 64) { 460 o = o.substring(0, 32) + "..." + o.substring(o.length() - 32); 461 } 462 out.print(" " + o); 463 } 464 } 465 for (JavaFileObject f: files) 466 out.print(" " + f.getName()); 467 out.println(); 468 CompilationTask t = compiler.getTask(out, fileManager, null, options, null, files); 469 boolean ok = t.call(); 470 if (!ok) 471 error("compilation failed"); 472 } 473 474 enum Mode { EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH }; 475 476 void checkFile(StandardLocation l, String path) { 477 if (!l.isOutputLocation()) { 478 error("Not an output location: " + l); 479 return; 480 } 481 482 List<File> files = getLocation(l); 483 if (files == null) { 484 error("location is unset: " + l); 485 return; 486 } 487 488 if (files.size() != 1) 489 error("unexpected number of entries on " + l + ": " + files.size()); 490 491 File f = new File(files.get(0), path); 492 if (!f.exists()) 493 error("file not found: " + f); 494 } 495 496 void checkPath(StandardLocation l, Mode m, File expect) { 497 checkPath(l, m, Arrays.asList(expect)); 498 } 499 500 void checkPath(StandardLocation l, Mode m, List<File> expect) { 501 List<File> files = getLocation(l); 502 if (files == null) { 503 error("location is unset: " + l); 504 return; 505 } 506 507 switch (m) { 508 case EQUALS: 509 if (!Objects.equals(files, expect)) { 510 error("location does not match the expected files: " + l); 511 out.println("found: " + files); 512 out.println("expect: " + expect); 513 } 514 break; 515 516 case CONTAINS: 517 int containsIndex = Collections.indexOfSubList(files, expect); 518 if (containsIndex == -1) { 519 error("location does not contain the expected files: " + l); 520 out.println("found: " + files); 521 out.println("expect: " + expect); 522 } 523 break; 524 525 case STARTS_WITH: 526 int startsIndex = Collections.indexOfSubList(files, expect); 527 if (startsIndex != 0) { 528 error("location does not start with the expected files: " + l); 529 out.println("found: " + files); 530 out.println("expect: " + expect); 531 } 532 break; 533 534 case ENDS_WITH: 535 int endsIndex = Collections.lastIndexOfSubList(files, expect); 536 if (endsIndex != files.size() - expect.size()) { 537 error("location does not end with the expected files: " + l); 538 out.println("found: " + files); 539 out.println("expect: " + expect); 540 } 541 break; 542 543 } 544 } 545 546 List<File> getLocation(StandardLocation l) { 547 Iterable<? extends File> iter = fileManager.getLocation(l); 548 if (iter == null) 549 return null; 550 List<File> files = new ArrayList<>(); 551 for (File f: iter) 552 files.add(f); 553 return files; 554 } 555 556 void setLocation(StandardLocation l, File... files) throws IOException { 557 fileManager.setLocation(l, Arrays.asList(files)); 558 } 559 560 void setLocation(StandardLocation l, List<File> files) throws IOException { 561 fileManager.setLocation(l, files); 562 } 563 564 void writeFile(File dir, String path, String body) throws IOException { 565 try (FileWriter w = new FileWriter(new File(dir, path))) { 566 w.write(body); 567 } 568 } 569 570 void writeJar(File jar, File dir, String... entries) throws IOException { 571 try (JarOutputStream j = new JarOutputStream(Files.newOutputStream(jar.toPath()))) { 572 for (String entry: entries) { 573 j.putNextEntry(new JarEntry(entry)); 574 j.write(Files.readAllBytes(dir.toPath().resolve(entry))); 575 } 576 } 577 } 578 579 private static final Pattern packagePattern 580 = Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))"); 581 private static final Pattern classPattern 582 = Pattern.compile("(?:public\\s+)?(?:class|enum|interface)\\s+(\\w+)"); 583 584 585 private static URI getURIFromSource(String source) { 586 String packageName = null; 587 588 Matcher matcher = packagePattern.matcher(source); 589 if (matcher.find()) { 590 packageName = matcher.group(1).replace(".", "/"); 591 } 592 593 matcher = classPattern.matcher(source); 594 if (matcher.find()) { 595 String className = matcher.group(1); 596 String path = ((packageName == null) ? "" : packageName + "/") + className + ".java"; 597 return URI.create("myfo:///" + path); 598 } else { 599 throw new Error("Could not extract the java class " 600 + "name from the provided source"); 601 } 602 } 603 604 File createDir(String path) { 605 File dir = new File(path); 606 dir.mkdirs(); 607 return dir; 608 } 609 610 JavaCompiler compiler; 611 StandardJavaFileManager fileManager; 612 613 /** 614 * Map for recording which standard locations have been tested. 615 */ 616 EnumSet<StandardLocation> tested = EnumSet.noneOf(StandardLocation.class); 617 618 /** 619 * Logging stream. Used directly with test and for getTask calls. 620 */ 621 final PrintWriter out = new PrintWriter(System.err, true); 622 623 /** 624 * Count of errors so far. 625 */ 626 int errors; 627 628 void error(String message) { 629 errors++; 630 out.println("Error: " + message); 631 } 632} 633