6960424: new option -Xpkginfo for better control of when package-info.class is generated
Reviewed-by: mcimadamore
--- 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;
+}