6960424: new option -Xpkginfo for better control of when package-info.class is generated
authorjjg
Wed, 25 Aug 2010 11:40:25 -0700
changeset 6575 ae1798028008
parent 6574 7729fe9665a2
child 6576 002cb45af72c
6960424: new option -Xpkginfo for better control of when package-info.class is generated Reviewed-by: mcimadamore
langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java
langtools/src/share/classes/com/sun/tools/javac/code/Types.java
langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java
langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
langtools/src/share/classes/com/sun/tools/javac/main/OptionName.java
langtools/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java
langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties
langtools/test/tools/javac/TestPkgInfo.java
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java	Wed Aug 25 11:40:25 2010 -0700
@@ -295,4 +295,11 @@
         void visitEnum(Attribute.Enum e);
         void visitError(Attribute.Error e);
     }
+
+    /** A mirror of java.lang.annotation.RetentionPolicy. */
+    public static enum RetentionPolicy {
+        SOURCE,
+        CLASS,
+        RUNTIME
+    }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Wed Aug 25 11:40:25 2010 -0700
@@ -32,6 +32,7 @@
 import com.sun.tools.javac.util.List;
 
 import com.sun.tools.javac.jvm.ClassReader;
+import com.sun.tools.javac.code.Attribute.RetentionPolicy;
 import com.sun.tools.javac.comp.Check;
 
 import static com.sun.tools.javac.code.Type.*;
@@ -3554,4 +3555,24 @@
         public Type visitType(Type t, S s) { return t; }
     }
     // </editor-fold>
+
+
+    // <editor-fold defaultstate="collapsed" desc="Annotation support">
+
+    public RetentionPolicy getRetention(Attribute.Compound a) {
+        RetentionPolicy vis = RetentionPolicy.CLASS; // the default
+        Attribute.Compound c = a.type.tsym.attribute(syms.retentionType.tsym);
+        if (c != null) {
+            Attribute value = c.member(names.value);
+            if (value != null && value instanceof Attribute.Enum) {
+                Name levelName = ((Attribute.Enum)value).value.name;
+                if (levelName == names.SOURCE) vis = RetentionPolicy.SOURCE;
+                else if (levelName == names.CLASS) vis = RetentionPolicy.CLASS;
+                else if (levelName == names.RUNTIME) vis = RetentionPolicy.RUNTIME;
+                else ;// /* fail soft */ throw new AssertionError(levelName);
+            }
+        }
+        return vis;
+    }
+    // </editor-fold>
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java	Wed Aug 25 11:40:25 2010 -0700
@@ -38,6 +38,7 @@
 
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.main.RecognizedOptions.PkgInfo;
 import com.sun.tools.javac.tree.JCTree.*;
 
 import static com.sun.tools.javac.code.Flags.*;
@@ -102,6 +103,7 @@
     Lint lint;
     Names names;
     JavaFileManager fileManager;
+    PkgInfo pkginfoOpt;
 
     private final Todo todo;
 
@@ -132,6 +134,9 @@
         predefClassDef.sym = syms.predefClass;
         todo = Todo.instance(context);
         fileManager = context.get(JavaFileManager.class);
+
+        Options options = Options.instance(context);
+        pkginfoOpt = PkgInfo.get(options);
     }
 
     /** A hashtable mapping classes and packages to the environments current
@@ -278,7 +283,7 @@
                                                              JavaFileObject.Kind.SOURCE);
         if (tree.pid != null) {
             tree.packge = reader.enterPackage(TreeInfo.fullName(tree.pid));
-            if (tree.packageAnnotations.nonEmpty()) {
+            if (tree.packageAnnotations.nonEmpty() || pkginfoOpt == PkgInfo.ALWAYS) {
                 if (isPkgInfo) {
                     addEnv = true;
                 } else {
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Aug 25 11:40:25 2010 -0700
@@ -29,6 +29,7 @@
 
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.jvm.*;
+import com.sun.tools.javac.main.RecognizedOptions.PkgInfo;
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@@ -82,6 +83,7 @@
     private final Name classDollar;
     private Types types;
     private boolean debugLower;
+    private PkgInfo pkginfoOpt;
 
     protected Lower(Context context) {
         context.put(lowerKey, this);
@@ -106,6 +108,7 @@
         types = Types.instance(context);
         Options options = Options.instance(context);
         debugLower = options.get("debuglower") != null;
+        pkginfoOpt = PkgInfo.get(options);
     }
 
     /** The currently enclosing class.
@@ -2161,7 +2164,7 @@
     }
 
     public void visitTopLevel(JCCompilationUnit tree) {
-        if (tree.packageAnnotations.nonEmpty()) {
+        if (needPackageInfoClass(tree)) {
             Name name = names.package_info;
             long flags = Flags.ABSTRACT | Flags.INTERFACE;
             if (target.isPackageInfoSynthetic())
@@ -2183,6 +2186,23 @@
             translated.append(packageAnnotationsClass);
         }
     }
+    // where
+    private boolean needPackageInfoClass(JCCompilationUnit tree) {
+        switch (pkginfoOpt) {
+            case ALWAYS:
+                return true;
+            case LEGACY:
+                return tree.packageAnnotations.nonEmpty();
+            case NONEMPTY:
+                for (Attribute.Compound a: tree.packge.attributes_field) {
+                    Attribute.RetentionPolicy p = types.getRetention(a);
+                    if (p != Attribute.RetentionPolicy.SOURCE)
+                        return true;
+                }
+                return false;
+        }
+        throw new AssertionError();
+    }
 
     public void visitClassDef(JCClassDecl tree) {
         ClassSymbol currentClassPrev = currentClass;
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Aug 25 11:40:25 2010 -0700
@@ -34,6 +34,7 @@
 import javax.tools.JavaFileObject;
 
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Attribute.RetentionPolicy;
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.file.BaseFileObject;
@@ -692,7 +693,7 @@
         boolean hasInvisible = false;
         if (m.params != null) for (VarSymbol s : m.params) {
             for (Attribute.Compound a : s.getAnnotationMirrors()) {
-                switch (getRetention(a.type.tsym)) {
+                switch (types.getRetention(a)) {
                 case SOURCE: break;
                 case CLASS: hasInvisible = true; break;
                 case RUNTIME: hasVisible = true; break;
@@ -708,7 +709,7 @@
             for (VarSymbol s : m.params) {
                 ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>();
                 for (Attribute.Compound a : s.getAnnotationMirrors())
-                    if (getRetention(a.type.tsym) == RetentionPolicy.RUNTIME)
+                    if (types.getRetention(a) == RetentionPolicy.RUNTIME)
                         buf.append(a);
                 databuf.appendChar(buf.length());
                 for (Attribute.Compound a : buf)
@@ -723,7 +724,7 @@
             for (VarSymbol s : m.params) {
                 ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>();
                 for (Attribute.Compound a : s.getAnnotationMirrors())
-                    if (getRetention(a.type.tsym) == RetentionPolicy.CLASS)
+                    if (types.getRetention(a) == RetentionPolicy.CLASS)
                         buf.append(a);
                 databuf.appendChar(buf.length());
                 for (Attribute.Compound a : buf)
@@ -747,7 +748,7 @@
         ListBuffer<Attribute.Compound> visibles = new ListBuffer<Attribute.Compound>();
         ListBuffer<Attribute.Compound> invisibles = new ListBuffer<Attribute.Compound>();
         for (Attribute.Compound a : attrs) {
-            switch (getRetention(a.type.tsym)) {
+            switch (types.getRetention(a)) {
             case SOURCE: break;
             case CLASS: invisibles.append(a); break;
             case RUNTIME: visibles.append(a); break;
@@ -785,7 +786,7 @@
             if (tc.position.type == TargetType.UNKNOWN
                 || !tc.position.emitToClassfile())
                 continue;
-            switch (getRetention(tc.type.tsym)) {
+            switch (types.getRetention(tc)) {
             case SOURCE: break;
             case CLASS: invisibles.append(tc); break;
             case RUNTIME: visibles.append(tc); break;
@@ -815,29 +816,6 @@
         return attrCount;
     }
 
-    /** A mirror of java.lang.annotation.RetentionPolicy. */
-    enum RetentionPolicy {
-        SOURCE,
-        CLASS,
-        RUNTIME
-    }
-
-    RetentionPolicy getRetention(TypeSymbol annotationType) {
-        RetentionPolicy vis = RetentionPolicy.CLASS; // the default
-        Attribute.Compound c = annotationType.attribute(syms.retentionType.tsym);
-        if (c != null) {
-            Attribute value = c.member(names.value);
-            if (value != null && value instanceof Attribute.Enum) {
-                Name levelName = ((Attribute.Enum)value).value.name;
-                if (levelName == names.SOURCE) vis = RetentionPolicy.SOURCE;
-                else if (levelName == names.CLASS) vis = RetentionPolicy.CLASS;
-                else if (levelName == names.RUNTIME) vis = RetentionPolicy.RUNTIME;
-                else ;// /* fail soft */ throw new AssertionError(levelName);
-            }
-        }
-        return vis;
-    }
-
     /** A visitor to write an attribute including its leading
      *  single-character marker.
      */
--- a/langtools/src/share/classes/com/sun/tools/javac/main/OptionName.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/OptionName.java	Wed Aug 25 11:40:25 2010 -0700
@@ -80,6 +80,7 @@
     XMAXERRS("-Xmaxerrs"),
     XMAXWARNS("-Xmaxwarns"),
     XSTDOUT("-Xstdout"),
+    XPKGINFO("-Xpkginfo:"),
     XPRINT("-Xprint"),
     XPRINTROUNDS("-XprintRounds"),
     XPRINTPROCESSORINFO("-XprintProcessorInfo"),
--- a/langtools/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java	Wed Aug 25 11:40:25 2010 -0700
@@ -160,6 +160,7 @@
         XMAXERRS,
         XMAXWARNS,
         XSTDOUT,
+        XPKGINFO,
         XPRINT,
         XPRINTROUNDS,
         XPRINTPROCESSORINFO,
@@ -217,6 +218,7 @@
         XMAXERRS,
         XMAXWARNS,
         // XSTDOUT,
+        XPKGINFO,
         XPRINT,
         XPRINTROUNDS,
         XPRINTPROCESSORINFO,
@@ -532,6 +534,9 @@
         new XOption(XPREFER,                                    "opt.prefer",
                 Option.ChoiceKind.ONEOF, "source", "newer"),
 
+        new XOption(XPKGINFO,                                   "opt.pkginfo",
+                Option.ChoiceKind.ONEOF, "always", "legacy", "nonempty"),
+
         /* -O is a no-op, accepted for backward compatibility. */
         new HiddenOption(O),
 
@@ -598,6 +603,16 @@
     };
     }
 
+    public enum PkgInfo {
+        ALWAYS, LEGACY, NONEMPTY;
+        public static PkgInfo get(Options options) {
+            String v = options.get(XPKGINFO);
+            return (v == null
+                    ? PkgInfo.LEGACY
+                    : PkgInfo.valueOf(v.toUpperCase()));
+        }
+    }
+
     private static Map<String,Boolean> getXLintChoices() {
         Map<String,Boolean> choices = new LinkedHashMap<String,Boolean>();
         choices.put("all", false);
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties	Wed Aug 25 11:24:30 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/javac.properties	Wed Aug 25 11:40:25 2010 -0700
@@ -74,7 +74,9 @@
 javac.opt.A=\
     Options to pass to annotation processors
 javac.opt.implicit=\
-    Specify whether or not to generate class files for implicitly referenced files 
+    Specify whether or not to generate class files for implicitly referenced files
+javac.opt.pkginfo=\
+    Specify handling of package-info files
 javac.opt.arg.class=\
     <class>
 javac.opt.arg.class.list=\
@@ -189,7 +191,7 @@
 
 javac.msg.usage.nonstandard.footer=\
 These options are non-standard and subject to change without notice.
-	
+
 javac.msg.bug=\
 An exception has occurred in the compiler ({0}). \
 Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TestPkgInfo.java	Wed Aug 25 11:40:25 2010 -0700
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6960424
+ * @summary new option -Xpkginfo for better control of when package-info.class is generated
+ */
+
+import java.io.*;
+import java.util.*;
+
+public class TestPkgInfo {
+    enum OptKind {
+        NONE(null),
+        ALWAYS("-Xpkginfo:always"),
+        NONEMPTY("-Xpkginfo:nonempty"),
+        LEGACY("-Xpkginfo:legacy");
+        OptKind(String opt) { this.opt = opt; }
+        final String opt;
+    };
+
+    public static void main(String... args) throws Exception {
+        new TestPkgInfo().run(args);
+    }
+
+    public void run(String... args) throws Exception {
+        boolean[] booleanValues = { false, true };
+        for (OptKind ok: OptKind.values()) {
+            for (boolean sr: booleanValues) {
+                for (boolean cr: booleanValues) {
+                    for (boolean rr: booleanValues) {
+                        try {
+                            test(ok, sr, cr, rr);
+                        } catch (Exception e) {
+                            error("Exception: " + e);
+                        }
+                        if (errors > 0) throw new AssertionError();
+                    }
+                }
+            }
+        }
+
+        if (errors > 0)
+            throw new Exception(errors + " errors occurred");
+    }
+
+    void test(OptKind ok, boolean sr, boolean cr, boolean rr) throws Exception {
+        count++;
+        System.err.println("Test " + count + ": ok:" + ok + " sr:" + sr + " cr:" + cr + " rr:" + rr);
+
+        StringBuilder sb = new StringBuilder();
+
+        // create annotated package statement with all combinations of retention policy
+        if (sr) sb.append("@SR\n");
+        if (cr) sb.append("@CR\n");
+        if (rr) sb.append("@RR\n");
+        sb.append("package p;\n");
+        sb.append("\n");
+
+        sb.append("import java.lang.annotation.*;\n");
+        sb.append("@Retention(RetentionPolicy.SOURCE)  @interface SR { }\n");
+        sb.append("@Retention(RetentionPolicy.CLASS)   @interface CR { }\n");
+        sb.append("@Retention(RetentionPolicy.RUNTIME) @interface RR { }\n");
+
+        // test specific tmp directory
+        File tmpDir = new File("tmp.test" + count);
+        File classesDir = new File(tmpDir, "classes");
+        classesDir.mkdirs();
+        File pkginfo_java = new File(new File(tmpDir, "src"), "package-info.java");
+        writeFile(pkginfo_java, sb.toString());
+
+        // build up list of options and files to be compiled
+        List<String> opts = new ArrayList<String>();
+        List<File> files = new ArrayList<File>();
+
+        opts.add("-d");
+        opts.add(classesDir.getPath());
+        if (ok.opt != null)
+            opts.add(ok.opt);
+        //opts.add("-verbose");
+        files.add(pkginfo_java);
+
+        compile(opts, files);
+
+        File pkginfo_class = new File(new File(classesDir, "p"), "package-info.class");
+        boolean exists = pkginfo_class.exists();
+
+        boolean expected;
+        switch (ok) {
+            case ALWAYS:
+                expected = true;
+                break;
+
+            case LEGACY:
+            case NONE:
+                expected = (sr || cr || rr ); // any annotation
+                break;
+
+            case NONEMPTY:
+                expected = (cr || rr ); // any annotation in class file
+                break;
+
+            default:
+                throw new IllegalStateException();
+        }
+
+        if (exists && !expected)
+            error("package-info.class found but not expected");
+        if (!exists && expected)
+            error("package-info.class expected but not found");
+    }
+
+    /** Compile files with options provided. */
+    void compile(List<String> opts, List<File> files) throws Exception {
+        System.err.println("javac: " + opts + " " + files);
+        List<String> args = new ArrayList<String>();
+        args.addAll(opts);
+        for (File f: files)
+            args.add(f.getPath());
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
+        pw.flush();
+        if (sw.getBuffer().length() > 0)
+            System.err.println(sw.toString());
+        if (rc != 0)
+            throw new Exception("compilation failed: rc=" + rc);
+    }
+
+    /** Write a file with a given body. */
+    void writeFile(File f, String body) throws Exception {
+        if (f.getParentFile() != null)
+            f.getParentFile().mkdirs();
+        Writer out = new FileWriter(f);
+        try {
+            out.write(body);
+        } finally {
+            out.close();
+        }
+    }
+
+    /** Report an error. */
+    void error(String msg) {
+        System.err.println("Error: " + msg);
+        errors++;
+    }
+
+    /** Test case counter. */
+    int count;
+
+    /** Number of errors found. */
+    int errors;
+}