Fri, 29 Jan 2010 16:54:52 -0800
6499119: Created package-info class file modeled improperly
6920317: package-info.java file has to be specified on the javac cmdline, else it will not be avail.
Reviewed-by: darcy
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Jan 29 16:06:51 2010 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Jan 29 16:54:52 2010 -0800 1.3 @@ -657,6 +657,11 @@ 1.4 1.5 public List<Attribute.Compound> getAnnotationMirrors() { 1.6 if (completer != null) complete(); 1.7 + if (package_info != null && package_info.completer != null) { 1.8 + package_info.complete(); 1.9 + if (attributes_field.isEmpty()) 1.10 + attributes_field = package_info.attributes_field; 1.11 + } 1.12 assert attributes_field != null; 1.13 return attributes_field; 1.14 }
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Enter.java Fri Jan 29 16:06:51 2010 -0800 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Enter.java Fri Jan 29 16:54:52 2010 -0800 2.3 @@ -100,6 +100,7 @@ 2.4 MemberEnter memberEnter; 2.5 Types types; 2.6 Lint lint; 2.7 + Names names; 2.8 JavaFileManager fileManager; 2.9 2.10 private final Todo todo; 2.11 @@ -123,6 +124,7 @@ 2.12 types = Types.instance(context); 2.13 annotate = Annotate.instance(context); 2.14 lint = Lint.instance(context); 2.15 + names = Names.instance(context); 2.16 2.17 predefClassDef = make.ClassDef( 2.18 make.Modifiers(PUBLIC), 2.19 @@ -308,6 +310,17 @@ 2.20 } 2.21 } 2.22 } 2.23 + 2.24 + for (Symbol q = tree.packge; q != null && q.kind == PCK; q = q.owner) 2.25 + q.flags_field |= EXISTS; 2.26 + 2.27 + Name name = names.package_info; 2.28 + ClassSymbol c = reader.enterClass(name, tree.packge); 2.29 + c.flatname = names.fromString(tree.packge + "." + name); 2.30 + c.sourcefile = tree.sourcefile; 2.31 + c.completer = null; 2.32 + c.members_field = new Scope(c); 2.33 + tree.packge.package_info = c; 2.34 } 2.35 classEnter(tree.defs, env); 2.36 if (addEnv) {
3.1 --- a/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Jan 29 16:06:51 2010 -0800 3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Jan 29 16:54:52 2010 -0800 3.3 @@ -1994,19 +1994,14 @@ 3.4 tree.packageAnnotations), 3.5 name, List.<JCTypeParameter>nil(), 3.6 null, List.<JCExpression>nil(), List.<JCTree>nil()); 3.7 - ClassSymbol c = reader.enterClass(name, tree.packge); 3.8 - c.flatname = names.fromString(tree.packge + "." + name); 3.9 - c.sourcefile = tree.sourcefile; 3.10 - c.completer = null; 3.11 - c.members_field = new Scope(c); 3.12 - c.flags_field = flags; 3.13 + ClassSymbol c = tree.packge.package_info; 3.14 + c.flags_field |= flags; 3.15 c.attributes_field = tree.packge.attributes_field; 3.16 ClassType ctype = (ClassType) c.type; 3.17 ctype.supertype_field = syms.objectType; 3.18 ctype.interfaces_field = List.nil(); 3.19 packageAnnotationsClass.sym = c; 3.20 3.21 - 3.22 translated.append(packageAnnotationsClass); 3.23 } 3.24 }
4.1 --- a/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Jan 29 16:06:51 2010 -0800 4.2 +++ b/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Jan 29 16:54:52 2010 -0800 4.3 @@ -67,6 +67,7 @@ 4.4 import com.sun.tools.javac.tree.JCTree.*; 4.5 import com.sun.tools.javac.util.Abort; 4.6 import com.sun.tools.javac.util.Context; 4.7 +import com.sun.tools.javac.util.Convert; 4.8 import com.sun.tools.javac.util.List; 4.9 import com.sun.tools.javac.util.ListBuffer; 4.10 import com.sun.tools.javac.util.Log; 4.11 @@ -893,14 +894,20 @@ 4.12 errorStatus = true; 4.13 break runAround; 4.14 } else { 4.15 - ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext); 4.16 + List<ClassSymbol> newClasses = enterNewClassFiles(currentContext); 4.17 compiler.enterTrees(roots); 4.18 4.19 // annotationsPresentInSource = 4.20 // collector.findAnnotations(parsedFiles); 4.21 - classes.appendList(getTopLevelClasses(parsedFiles)); 4.22 - topLevelClasses = classes.toList(); 4.23 - packageInfoFiles = getPackageInfoFiles(parsedFiles); 4.24 + ListBuffer<ClassSymbol> tlc = new ListBuffer<ClassSymbol>(); 4.25 + tlc.appendList(getTopLevelClasses(parsedFiles)); 4.26 + tlc.appendList(getTopLevelClassesFromClasses(newClasses)); 4.27 + topLevelClasses = tlc.toList(); 4.28 + 4.29 + ListBuffer<PackageSymbol> pif = new ListBuffer<PackageSymbol>(); 4.30 + pif.appendList(getPackageInfoFiles(parsedFiles)); 4.31 + pif.appendList(getPackageInfoFilesFromClasses(newClasses)); 4.32 + packageInfoFiles = pif.toList(); 4.33 4.34 annotationsPresent = new LinkedHashSet<TypeElement>(); 4.35 for (ClassSymbol classSym : topLevelClasses) 4.36 @@ -1026,20 +1033,30 @@ 4.37 } 4.38 } 4.39 4.40 - private ListBuffer<ClassSymbol> enterNewClassFiles(Context currentContext) { 4.41 + private List<ClassSymbol> enterNewClassFiles(Context currentContext) { 4.42 ClassReader reader = ClassReader.instance(currentContext); 4.43 Names names = Names.instance(currentContext); 4.44 - ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>(); 4.45 + List<ClassSymbol> list = List.nil(); 4.46 4.47 for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) { 4.48 Name name = names.fromString(entry.getKey()); 4.49 JavaFileObject file = entry.getValue(); 4.50 if (file.getKind() != JavaFileObject.Kind.CLASS) 4.51 throw new AssertionError(file); 4.52 - ClassSymbol cs = reader.enterClass(name, file); 4.53 - list.append(cs); 4.54 + ClassSymbol cs; 4.55 + if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) { 4.56 + Name packageName = Convert.packagePart(name); 4.57 + PackageSymbol p = reader.enterPackage(packageName); 4.58 + if (p.package_info == null) 4.59 + p.package_info = reader.enterClass(Convert.shortName(name), p); 4.60 + cs = p.package_info; 4.61 + if (cs.classfile == null) 4.62 + cs.classfile = file; 4.63 + } else 4.64 + cs = reader.enterClass(name, file); 4.65 + list = list.prepend(cs); 4.66 } 4.67 - return list; 4.68 + return list.reverse(); 4.69 } 4.70 4.71 /** 4.72 @@ -1066,18 +1083,44 @@ 4.73 return classes.reverse(); 4.74 } 4.75 4.76 + private List<ClassSymbol> getTopLevelClassesFromClasses(List<? extends ClassSymbol> syms) { 4.77 + List<ClassSymbol> classes = List.nil(); 4.78 + for (ClassSymbol sym : syms) { 4.79 + if (!isPkgInfo(sym)) { 4.80 + classes = classes.prepend(sym); 4.81 + } 4.82 + } 4.83 + return classes.reverse(); 4.84 + } 4.85 + 4.86 private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) { 4.87 List<PackageSymbol> packages = List.nil(); 4.88 for (JCCompilationUnit unit : units) { 4.89 - boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info", 4.90 - JavaFileObject.Kind.SOURCE); 4.91 - if (isPkgInfo) { 4.92 + if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) { 4.93 packages = packages.prepend(unit.packge); 4.94 } 4.95 } 4.96 return packages.reverse(); 4.97 } 4.98 4.99 + private List<PackageSymbol> getPackageInfoFilesFromClasses(List<? extends ClassSymbol> syms) { 4.100 + List<PackageSymbol> packages = List.nil(); 4.101 + for (ClassSymbol sym : syms) { 4.102 + if (isPkgInfo(sym)) { 4.103 + packages = packages.prepend((PackageSymbol) sym.owner); 4.104 + } 4.105 + } 4.106 + return packages.reverse(); 4.107 + } 4.108 + 4.109 + private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) { 4.110 + return fo.isNameCompatible("package-info", kind); 4.111 + } 4.112 + 4.113 + private boolean isPkgInfo(ClassSymbol sym) { 4.114 + return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym); 4.115 + } 4.116 + 4.117 private Context contextForNextRound(Context context, boolean shareNames) 4.118 throws IOException 4.119 {
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/processing/6499119/ClassProcessor.java Fri Jan 29 16:54:52 2010 -0800 5.3 @@ -0,0 +1,132 @@ 5.4 +/* 5.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 5.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 5.24 + * have any questions. 5.25 + */ 5.26 + 5.27 +import java.io.*; 5.28 +import java.util.*; 5.29 +import javax.annotation.processing.*; 5.30 +import javax.lang.model.element.*; 5.31 +import javax.lang.model.SourceVersion; 5.32 +import javax.tools.Diagnostic.Kind; 5.33 + 5.34 +/* 5.35 + * @test 5.36 + * @bug 6499119 5.37 + * @summary Created package-info class file modeled improperly 5.38 + * @compile ClassProcessor.java package-info.java 5.39 + * @compile/process -cp . -processor ClassProcessor -Akind=java java.lang.Object 5.40 + * @compile/process -cp . -processor ClassProcessor -Akind=class java.lang.Object 5.41 + */ 5.42 + 5.43 +@SupportedOptions({ "gen", "expect" }) 5.44 +@SupportedAnnotationTypes({"*"}) 5.45 +public class ClassProcessor extends AbstractProcessor { 5.46 + int round = 1; 5.47 + 5.48 + public SourceVersion getSupportedSourceVersion() { 5.49 + return SourceVersion.latest(); 5.50 + } 5.51 + 5.52 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 5.53 + if (round == 1) { 5.54 + System.out.println("-- Round 1 --"); 5.55 + createPackageFile(); 5.56 + } else if (round == 2) { 5.57 + boolean found_foo_A = false; 5.58 + System.out.println("-- Round 2 --"); 5.59 + for(Element e: roundEnv.getRootElements()) { 5.60 + System.out.println("ElementKind: " + e.getKind()); 5.61 + System.out.println("Modifiers: " + e.getModifiers()); 5.62 + System.out.println("Annotations: " + e.getAnnotationMirrors()); 5.63 + if (e.getAnnotationMirrors().toString().equals("@foo.A")) { 5.64 + found_foo_A = true; 5.65 + checkEqual("ElementKind", e.getKind().toString(), "PACKAGE"); 5.66 + checkEqual("Modifiers", e.getModifiers().toString(), "[]"); 5.67 + } 5.68 + } 5.69 + if (!found_foo_A) 5.70 + error("did not find @foo.A"); 5.71 + } 5.72 + round++; 5.73 + return true; 5.74 + } 5.75 + 5.76 + private void createPackageFile() { 5.77 + Filer filer = processingEnv.getFiler(); 5.78 + 5.79 + String kind = processingEnv.getOptions().get("kind"); 5.80 + 5.81 + File pkgInfo; 5.82 + if (kind.equals("java")) 5.83 + pkgInfo = new File(System.getProperty("test.src"), "package-info.java"); 5.84 + else 5.85 + pkgInfo = new File(System.getProperty("test.classes"), "foo/package-info.class"); 5.86 + 5.87 + byte[] bytes = new byte[(int) pkgInfo.length()]; 5.88 + DataInputStream in = null; 5.89 + try { 5.90 + in = new DataInputStream(new FileInputStream(pkgInfo)); 5.91 + in.readFully(bytes); 5.92 + } catch (IOException ioe) { 5.93 + error("Couldn't read package info file: " + ioe); 5.94 + } finally { 5.95 + if(in != null) { 5.96 + try { 5.97 + in.close(); 5.98 + } catch (IOException e) { 5.99 + error("InputStream closing failed: " + e); 5.100 + } 5.101 + } 5.102 + } 5.103 + 5.104 + OutputStream out = null; 5.105 + try { 5.106 + if (kind.equals("java")) 5.107 + out = filer.createSourceFile("foo.package-info").openOutputStream(); 5.108 + else 5.109 + out = filer.createClassFile("foo.package-info").openOutputStream(); 5.110 + out.write(bytes, 0, bytes.length); 5.111 + } catch (IOException ioe) { 5.112 + error("Couldn't create package info file: " + ioe); 5.113 + } finally { 5.114 + if(out != null) { 5.115 + try { 5.116 + out.close(); 5.117 + } catch (IOException e) { 5.118 + error("OutputStream closing failed: " + e); 5.119 + } 5.120 + } 5.121 + } 5.122 + } 5.123 + 5.124 + private void checkEqual(String label, String actual, String expect) { 5.125 + if (!actual.equals(expect)) { 5.126 + error("Unexpected value for " + label + "; actual=" + actual + ", expected=" + expect); 5.127 + } 5.128 + } 5.129 + 5.130 + private void error(String msg) { 5.131 + Messager messager = processingEnv.getMessager(); 5.132 + messager.printMessage(Kind.ERROR, msg); 5.133 + } 5.134 +} 5.135 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/tools/javac/processing/6499119/package-info.java Fri Jan 29 16:54:52 2010 -0800 6.3 @@ -0,0 +1,27 @@ 6.4 +/* 6.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. 6.11 + * 6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.15 + * version 2 for more details (a copy is included in the LICENSE file that 6.16 + * accompanied this code). 6.17 + * 6.18 + * You should have received a copy of the GNU General Public License version 6.19 + * 2 along with this work; if not, write to the Free Software Foundation, 6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.21 + * 6.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 6.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 6.24 + * have any questions. 6.25 + */ 6.26 + 6.27 +@A package foo; 6.28 + 6.29 +@interface A {} 6.30 +
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/tools/javac/processing/T6920317.java Fri Jan 29 16:54:52 2010 -0800 7.3 @@ -0,0 +1,462 @@ 7.4 +/* 7.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.7 + * 7.8 + * This code is free software; you can redistribute it and/or modify it 7.9 + * under the terms of the GNU General Public License version 2 only, as 7.10 + * published by the Free Software Foundation. 7.11 + * 7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 7.15 + * version 2 for more details (a copy is included in the LICENSE file that 7.16 + * accompanied this code). 7.17 + * 7.18 + * You should have received a copy of the GNU General Public License version 7.19 + * 2 along with this work; if not, write to the Free Software Foundation, 7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 7.21 + * 7.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 7.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 7.24 + * have any questions. 7.25 + */ 7.26 + 7.27 +/* 7.28 + * @test 7.29 + * @bug 6920317 7.30 + * @summary package-info.java file has to be specified on the javac cmdline, else it will not be avail 7.31 + */ 7.32 + 7.33 +import java.io.*; 7.34 +import java.util.*; 7.35 +import javax.annotation.processing.*; 7.36 +import javax.lang.model.*; 7.37 +import javax.lang.model.element.*; 7.38 +import javax.lang.model.util.*; 7.39 +import javax.tools.*; 7.40 + 7.41 +/** 7.42 + * The test exercises different ways of providing annotations for a package. 7.43 + * Each way provides an annotation with a unique argument. For each test 7.44 + * case, the test verifies that the annotation with the correct argument is 7.45 + * found by the compiler. 7.46 + */ 7.47 +public class T6920317 { 7.48 + public static void main(String... args) throws Exception { 7.49 + new T6920317().run(args); 7.50 + } 7.51 + 7.52 + // Used to describe properties of files to be put on command line, source path, class path 7.53 + enum Kind { 7.54 + /** File is not used. */ 7.55 + NONE, 7.56 + /** File is used. */ 7.57 + OLD, 7.58 + /** Only applies to files on classpath/sourcepath, when there is another file on the 7.59 + * other path of type OLD, in which case, this file must be newer than the other one. */ 7.60 + NEW, 7.61 + /** Only applies to files on classpath/sourcepath, when there is no file in any other 7.62 + * location, in which case, this file will be generated by the annotation processor. */ 7.63 + GEN 7.64 + } 7.65 + 7.66 + void run(String... args) throws Exception { 7.67 + // if no args given, all test cases are run 7.68 + // if args given, they indicate the test cases to be run 7.69 + for (int i = 0; i < args.length; i++) { 7.70 + tests.add(Integer.valueOf(args[i])); 7.71 + } 7.72 + 7.73 + setup(); 7.74 + 7.75 + // Run tests for all combinations of files on command line, source path and class path. 7.76 + // Invalid combinations are skipped in the test method 7.77 + for (Kind cmdLine: EnumSet.of(Kind.NONE, Kind.OLD)) { 7.78 + for (Kind srcPath: Kind.values()) { 7.79 + for (Kind clsPath: Kind.values()) { 7.80 + try { 7.81 + test(cmdLine, srcPath, clsPath); 7.82 + } catch (Exception e) { 7.83 + e.printStackTrace(); 7.84 + error("Exception " + e); 7.85 + // uncomment to stop on first failed test case 7.86 + // throw e; 7.87 + } 7.88 + } 7.89 + } 7.90 + } 7.91 + 7.92 + if (errors > 0) 7.93 + throw new Exception(errors + " errors occurred"); 7.94 + } 7.95 + 7.96 + /** One time setup for files and directories to be used in the various test cases. */ 7.97 + void setup() throws Exception { 7.98 + // Annotation used in test cases to annotate package. This file is 7.99 + // given on the command line in test cases. 7.100 + test_java = writeFile("Test.java", "package p; @interface Test { String value(); }"); 7.101 + // Compile the annotation for use later in setup 7.102 + File tmpClasses = new File("tmp.classes"); 7.103 + compile(tmpClasses, new String[] { }, test_java); 7.104 + 7.105 + // package-info file to use on the command line when requied 7.106 + cl_pkgInfo_java = writeFile("cl/p/package-info.java", "@Test(\"CL\") package p;"); 7.107 + 7.108 + // source path containing package-info 7.109 + sp_old = new File("src.old"); 7.110 + writeFile("src.old/p/package-info.java", "@Test(\"SP_OLD\") package p;"); 7.111 + 7.112 + // class path containing package-info 7.113 + cp_old = new File("classes.old"); 7.114 + compile(cp_old, new String[] { "-classpath", tmpClasses.getPath() }, 7.115 + writeFile("tmp.old/p/package-info.java", "@Test(\"CP_OLD\") package p;")); 7.116 + 7.117 + // source path containing package-info which is newer than the one in cp-old 7.118 + sp_new = new File("src.new"); 7.119 + File old_class = new File(cp_old, "p/package-info.class"); 7.120 + writeFile("src.new/p/package-info.java", "@Test(\"SP_NEW\") package p;", old_class); 7.121 + 7.122 + // class path containing package-info which is newer than the one in sp-old 7.123 + cp_new = new File("classes.new"); 7.124 + File old_java = new File(sp_old, "p/package-info.java"); 7.125 + compile(cp_new, new String[] { "-classpath", tmpClasses.getPath() }, 7.126 + writeFile("tmp.new/p/package-info.java", "@Test(\"CP_NEW\") package p;", old_java)); 7.127 + 7.128 + // directory containing package-info.java to be "generated" later by annotation processor 7.129 + sp_gen = new File("src.gen"); 7.130 + writeFile("src.gen/p/package-info.java", "@Test(\"SP_GEN\") package p;"); 7.131 + 7.132 + // directory containing package-info.class to be "generated" later by annotation processor 7.133 + cp_gen = new File("classes.gen"); 7.134 + compile(cp_gen, new String[] { "-classpath", tmpClasses.getPath() }, 7.135 + writeFile("tmp.gen/p/package-info.java", "@Test(\"CP_GEN\") package p;")); 7.136 + } 7.137 + 7.138 + void test(Kind cl, Kind sp, Kind cp) throws Exception { 7.139 + if (skip(cl, sp, cp)) 7.140 + return; 7.141 + 7.142 + ++count; 7.143 + // if test cases specified, skip this test case if not selected 7.144 + if (tests.size() > 0 && !tests.contains(count)) 7.145 + return; 7.146 + 7.147 + System.err.println("Test " + count + " cl:" + cl + " sp:" + sp + " cp:" + cp); 7.148 + 7.149 + // test specific tmp directory 7.150 + File test_tmp = new File("tmp.test" + count); 7.151 + test_tmp.mkdirs(); 7.152 + 7.153 + // build up list of options and files to be compiled 7.154 + List<String> opts = new ArrayList<String>(); 7.155 + List<File> files = new ArrayList<File>(); 7.156 + 7.157 + // expected value for annotation 7.158 + String expect = null; 7.159 + 7.160 + opts.add("-processorpath"); 7.161 + opts.add(System.getProperty("test.classes")); 7.162 + opts.add("-processor"); 7.163 + opts.add(Processor.class.getName()); 7.164 + opts.add("-proc:only"); 7.165 + opts.add("-d"); 7.166 + opts.add(test_tmp.getPath()); 7.167 + //opts.add("-verbose"); 7.168 + files.add(test_java); 7.169 + 7.170 + /* 7.171 + * Analyze each of cl, cp, sp, building up the options and files to 7.172 + * be compiled, and determining the expected outcome fo the test case. 7.173 + */ 7.174 + 7.175 + // command line file: either omitted or given 7.176 + if (cl == Kind.OLD) { 7.177 + files.add(cl_pkgInfo_java); 7.178 + // command line files always supercede files on paths 7.179 + expect = "CL"; 7.180 + } 7.181 + 7.182 + // source path: 7.183 + switch (sp) { 7.184 + case NONE: 7.185 + break; 7.186 + 7.187 + case OLD: 7.188 + opts.add("-sourcepath"); 7.189 + opts.add(sp_old.getPath()); 7.190 + if (expect == null && cp == Kind.NONE) { 7.191 + assert cl == Kind.NONE && cp == Kind.NONE; 7.192 + expect = "SP_OLD"; 7.193 + } 7.194 + break; 7.195 + 7.196 + case NEW: 7.197 + opts.add("-sourcepath"); 7.198 + opts.add(sp_new.getPath()); 7.199 + if (expect == null) { 7.200 + assert cl == Kind.NONE && cp == Kind.OLD; 7.201 + expect = "SP_NEW"; 7.202 + } 7.203 + break; 7.204 + 7.205 + case GEN: 7.206 + opts.add("-Agen=" + new File(sp_gen, "p/package-info.java")); 7.207 + assert cl == Kind.NONE && cp == Kind.NONE; 7.208 + expect = "SP_GEN"; 7.209 + break; 7.210 + } 7.211 + 7.212 + // class path: 7.213 + switch (cp) { 7.214 + case NONE: 7.215 + break; 7.216 + 7.217 + case OLD: 7.218 + opts.add("-classpath"); 7.219 + opts.add(cp_old.getPath()); 7.220 + if (expect == null && sp == Kind.NONE) { 7.221 + assert cl == Kind.NONE && sp == Kind.NONE; 7.222 + expect = "CP_OLD"; 7.223 + } 7.224 + break; 7.225 + 7.226 + case NEW: 7.227 + opts.add("-classpath"); 7.228 + opts.add(cp_new.getPath()); 7.229 + if (expect == null) { 7.230 + assert cl == Kind.NONE && sp == Kind.OLD; 7.231 + expect = "CP_NEW"; 7.232 + } 7.233 + break; 7.234 + 7.235 + case GEN: 7.236 + opts.add("-Agen=" + new File(cp_gen, "p/package-info.class")); 7.237 + assert cl == Kind.NONE && sp == Kind.NONE; 7.238 + expect = "CP_GEN"; 7.239 + break; 7.240 + } 7.241 + 7.242 + // pass expected value to annotation processor 7.243 + assert expect != null; 7.244 + opts.add("-Aexpect=" + expect); 7.245 + 7.246 + // compile the files with the options that have been built up 7.247 + compile(opts, files); 7.248 + } 7.249 + 7.250 + /** 7.251 + * Return true if this combination of parameters does not identify a useful test case. 7.252 + */ 7.253 + boolean skip(Kind cl, Kind sp, Kind cp) { 7.254 + // skip if no package files required 7.255 + if (cl == Kind.NONE && sp == Kind.NONE && cp == Kind.NONE) 7.256 + return true; 7.257 + 7.258 + // skip if both sp and sp are OLD, since results may be indeterminate 7.259 + if (sp == Kind.OLD && cp == Kind.OLD) 7.260 + return true; 7.261 + 7.262 + // skip if sp or cp is NEW but the other is not OLD 7.263 + if ((sp == Kind.NEW && cp != Kind.OLD) || (cp == Kind.NEW && sp != Kind.OLD)) 7.264 + return true; 7.265 + 7.266 + // only use GEN if no other package-info files present 7.267 + if (sp == Kind.GEN && !(cl == Kind.NONE && cp == Kind.NONE) || 7.268 + cp == Kind.GEN && !(cl == Kind.NONE && sp == Kind.NONE)) { 7.269 + return true; 7.270 + } 7.271 + 7.272 + // remaining combinations are valid 7.273 + return false; 7.274 + } 7.275 + 7.276 + /** Write a file with a given body. */ 7.277 + File writeFile(String path, String body) throws Exception { 7.278 + File f = new File(path); 7.279 + if (f.getParentFile() != null) 7.280 + f.getParentFile().mkdirs(); 7.281 + Writer out = new FileWriter(path); 7.282 + try { 7.283 + out.write(body); 7.284 + } finally { 7.285 + out.close(); 7.286 + } 7.287 + return f; 7.288 + } 7.289 + 7.290 + /** Write a file with a given body, ensuring that the file is newer than a reference file. */ 7.291 + File writeFile(String path, String body, File ref) throws Exception { 7.292 + for (int i = 0; i < 5; i++) { 7.293 + File f = writeFile(path, body); 7.294 + if (f.lastModified() > ref.lastModified()) 7.295 + return f; 7.296 + Thread.sleep(2000); 7.297 + } 7.298 + throw new Exception("cannot create file " + path + " newer than " + ref); 7.299 + } 7.300 + 7.301 + /** Compile a file to a given directory, with options provided. */ 7.302 + void compile(File dir, String[] opts, File src) throws Exception { 7.303 + dir.mkdirs(); 7.304 + List<String> opts2 = new ArrayList<String>(); 7.305 + opts2.addAll(Arrays.asList("-d", dir.getPath())); 7.306 + opts2.addAll(Arrays.asList(opts)); 7.307 + compile(opts2, Collections.singletonList(src)); 7.308 + } 7.309 + 7.310 + /** Compile files with options provided. */ 7.311 + void compile(List<String> opts, List<File> files) throws Exception { 7.312 + System.err.println("javac: " + opts + " " + files); 7.313 + List<String> args = new ArrayList<String>(); 7.314 + args.addAll(opts); 7.315 + for (File f: files) 7.316 + args.add(f.getPath()); 7.317 + StringWriter sw = new StringWriter(); 7.318 + PrintWriter pw = new PrintWriter(sw); 7.319 + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); 7.320 + pw.flush(); 7.321 + if (sw.getBuffer().length() > 0) 7.322 + System.err.println(sw.toString()); 7.323 + if (rc != 0) 7.324 + throw new Exception("compilation failed: rc=" + rc); 7.325 + } 7.326 + 7.327 + /** Report an error. */ 7.328 + void error(String msg) { 7.329 + System.err.println("Error: " + msg); 7.330 + errors++; 7.331 + } 7.332 + 7.333 + /** Test case counter. */ 7.334 + int count; 7.335 + 7.336 + /** Number of errors found. */ 7.337 + int errors; 7.338 + 7.339 + /** Optional set of test cases to be run; empty implies all test cases. */ 7.340 + Set<Integer> tests = new HashSet<Integer>(); 7.341 + 7.342 + /* Files created by setup. */ 7.343 + File test_java; 7.344 + File sp_old; 7.345 + File sp_new; 7.346 + File sp_gen; 7.347 + File cp_old; 7.348 + File cp_new; 7.349 + File cp_gen; 7.350 + File cl_pkgInfo_java; 7.351 + 7.352 + /** Annotation processor used to verify the expected value for the 7.353 + package annotations found by javac. */ 7.354 + @SupportedOptions({ "gen", "expect" }) 7.355 + @SupportedAnnotationTypes({"*"}) 7.356 + public static class Processor extends AbstractProcessor { 7.357 + public SourceVersion getSupportedSourceVersion() { 7.358 + return SourceVersion.latest(); 7.359 + } 7.360 + 7.361 + public boolean process(Set<? extends TypeElement> annots, RoundEnvironment renv) { 7.362 + round++; 7.363 + System.err.println("Round " + round + " annots:" + annots + " rootElems:" + renv.getRootElements()); 7.364 + 7.365 + // if this is the first round and the gen option is given, use the filer to create 7.366 + // a copy of the file specified by the gen option. 7.367 + String gen = getOption("gen"); 7.368 + if (round == 1 && gen != null) { 7.369 + try { 7.370 + Filer filer = processingEnv.getFiler(); 7.371 + JavaFileObject f; 7.372 + if (gen.endsWith(".java")) 7.373 + f = filer.createSourceFile("p.package-info"); 7.374 + else 7.375 + f = filer.createClassFile("p.package-info"); 7.376 + System.err.println("copy " + gen + " to " + f.getName()); 7.377 + write(f, read(new File(gen))); 7.378 + } catch (IOException e) { 7.379 + error("Cannot create package-info file: " + e); 7.380 + } 7.381 + } 7.382 + 7.383 + // if annotation processing is complete, verify the package annotation 7.384 + // found by the compiler. 7.385 + if (renv.processingOver()) { 7.386 + System.err.println("final round"); 7.387 + Elements eu = processingEnv.getElementUtils(); 7.388 + TypeElement te = eu.getTypeElement("p.Test"); 7.389 + PackageElement pe = eu.getPackageOf(te); 7.390 + System.err.println("final: te:" + te + " pe:" + pe); 7.391 + List<? extends AnnotationMirror> annos = pe.getAnnotationMirrors(); 7.392 + System.err.println("final: annos:" + annos); 7.393 + if (annos.size() == 1) { 7.394 + String expect = "@" + te + "(\"" + getOption("expect") + "\")"; 7.395 + String actual = annos.get(0).toString(); 7.396 + checkEqual("package annotations", actual, expect); 7.397 + } else { 7.398 + error("Wrong number of annotations found: (" + annos.size() + ") " + annos); 7.399 + } 7.400 + } 7.401 + 7.402 + return true; 7.403 + } 7.404 + 7.405 + /** Get an option given to the annotation processor. */ 7.406 + String getOption(String name) { 7.407 + return processingEnv.getOptions().get(name); 7.408 + } 7.409 + 7.410 + /** Read a file. */ 7.411 + byte[] read(File file) { 7.412 + byte[] bytes = new byte[(int) file.length()]; 7.413 + DataInputStream in = null; 7.414 + try { 7.415 + in = new DataInputStream(new FileInputStream(file)); 7.416 + in.readFully(bytes); 7.417 + } catch (IOException e) { 7.418 + error("Error reading file: " + e); 7.419 + } finally { 7.420 + if (in != null) { 7.421 + try { 7.422 + in.close(); 7.423 + } catch (IOException e) { 7.424 + error("Error closing file: " + e); 7.425 + } 7.426 + } 7.427 + } 7.428 + return bytes; 7.429 + } 7.430 + 7.431 + /** Write a file. */ 7.432 + void write(JavaFileObject file, byte[] bytes) { 7.433 + OutputStream out = null; 7.434 + try { 7.435 + out = file.openOutputStream(); 7.436 + out.write(bytes, 0, bytes.length); 7.437 + } catch (IOException e) { 7.438 + error("Error writing file: " + e); 7.439 + } finally { 7.440 + if (out != null) { 7.441 + try { 7.442 + out.close(); 7.443 + } catch (IOException e) { 7.444 + error("Error closing file: " + e); 7.445 + } 7.446 + } 7.447 + } 7.448 + } 7.449 + 7.450 + /** Check two strings are equal, and report an error if they are not. */ 7.451 + private void checkEqual(String label, String actual, String expect) { 7.452 + if (!actual.equals(expect)) { 7.453 + error("Unexpected value for " + label + "; actual=" + actual + ", expected=" + expect); 7.454 + } 7.455 + } 7.456 + 7.457 + /** Report an error to the annotation processing system. */ 7.458 + void error(String msg) { 7.459 + Messager messager = processingEnv.getMessager(); 7.460 + messager.printMessage(Diagnostic.Kind.ERROR, msg); 7.461 + } 7.462 + 7.463 + int round; 7.464 + } 7.465 +}