6960424: new option -Xpkginfo for better control of when package-info.class is generated
Reviewed-by: mcimadamore
This commit is contained in:
parent
0e1661c2f1
commit
37fcc133f6
@ -295,4 +295,11 @@ public abstract class Attribute implements AnnotationValue {
|
||||
void visitEnum(Attribute.Enum e);
|
||||
void visitError(Attribute.Error e);
|
||||
}
|
||||
|
||||
/** A mirror of java.lang.annotation.RetentionPolicy. */
|
||||
public static enum RetentionPolicy {
|
||||
SOURCE,
|
||||
CLASS,
|
||||
RUNTIME
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import com.sun.tools.javac.util.*;
|
||||
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 class Types {
|
||||
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>
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import com.sun.tools.javac.util.List;
|
||||
|
||||
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 @@ public class Enter extends JCTree.Visitor {
|
||||
Lint lint;
|
||||
Names names;
|
||||
JavaFileManager fileManager;
|
||||
PkgInfo pkginfoOpt;
|
||||
|
||||
private final Todo todo;
|
||||
|
||||
@ -132,6 +134,9 @@ public class Enter extends JCTree.Visitor {
|
||||
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 @@ public class Enter extends JCTree.Visitor {
|
||||
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 {
|
||||
|
@ -29,6 +29,7 @@ import java.util.*;
|
||||
|
||||
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 @@ public class Lower extends TreeTranslator {
|
||||
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 @@ public class Lower extends TreeTranslator {
|
||||
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 class Lower extends TreeTranslator {
|
||||
}
|
||||
|
||||
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 @@ public class Lower extends TreeTranslator {
|
||||
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;
|
||||
|
@ -34,6 +34,7 @@ import javax.tools.FileObject;
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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 @@ public class ClassWriter extends ClassFile {
|
||||
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.
|
||||
*/
|
||||
|
@ -80,6 +80,7 @@ public enum OptionName {
|
||||
XMAXERRS("-Xmaxerrs"),
|
||||
XMAXWARNS("-Xmaxwarns"),
|
||||
XSTDOUT("-Xstdout"),
|
||||
XPKGINFO("-Xpkginfo:"),
|
||||
XPRINT("-Xprint"),
|
||||
XPRINTROUNDS("-XprintRounds"),
|
||||
XPRINTPROCESSORINFO("-XprintProcessorInfo"),
|
||||
|
@ -160,6 +160,7 @@ public class RecognizedOptions {
|
||||
XMAXERRS,
|
||||
XMAXWARNS,
|
||||
XSTDOUT,
|
||||
XPKGINFO,
|
||||
XPRINT,
|
||||
XPRINTROUNDS,
|
||||
XPRINTPROCESSORINFO,
|
||||
@ -217,6 +218,7 @@ public class RecognizedOptions {
|
||||
XMAXERRS,
|
||||
XMAXWARNS,
|
||||
// XSTDOUT,
|
||||
XPKGINFO,
|
||||
XPRINT,
|
||||
XPRINTROUNDS,
|
||||
XPRINTPROCESSORINFO,
|
||||
@ -532,6 +534,9 @@ public class RecognizedOptions {
|
||||
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 class RecognizedOptions {
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -74,7 +74,9 @@ javac.opt.Werror=\
|
||||
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=\
|
||||
|
||||
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) \
|
||||
|
174
langtools/test/tools/javac/TestPkgInfo.java
Normal file
174
langtools/test/tools/javac/TestPkgInfo.java
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user