Add proper keep list generation.
Change-Id: I904336e64316318a1a7808c593fc9dfd10019820
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 56acf26..9e138ca 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -119,6 +119,7 @@
public static boolean start(RootDoc r) {
long startTime = System.nanoTime();
String keepListFile = null;
+ String proguardFile = null;
String proofreadFile = null;
String todoFile = null;
String sdkValuePath = null;
@@ -170,6 +171,8 @@
}
} else if (a[0].equals("-keeplist")) {
keepListFile = a[1];
+ } else if (a[0].equals("-proguard")) {
+ proguardFile = a[1];
} else if (a[0].equals("-proofread")) {
proofreadFile = a[1];
} else if (a[0].equals("-todo")) {
@@ -303,8 +306,8 @@
}
// Stubs
- if (stubsDir != null || apiFile != null) {
- Stubs.writeStubsAndApi(stubsDir, apiFile, stubPackages);
+ if (stubsDir != null || apiFile != null || proguardFile != null) {
+ Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, stubPackages);
}
Errors.printErrors();
@@ -478,6 +481,9 @@
if (option.equals("-keeplist")) {
return 2;
}
+ if (option.equals("-proguard")) {
+ return 2;
+ }
if (option.equals("-proofread")) {
return 2;
}
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index b493aa0..3c63acd 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -31,12 +31,13 @@
import java.util.Set;
public class Stubs {
- public static void writeStubsAndApi(String stubsDir, String apiFile,
+ public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
HashSet<String> stubPackages) {
// figure out which classes we need
final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
ClassInfo[] all = Converter.allClasses();
PrintStream apiWriter = null;
+ PrintStream keepListWriter = null;
if (apiFile != null) {
try {
File xml = new File(apiFile);
@@ -47,6 +48,16 @@
"Cannot open file for write.");
}
}
+ if (keepListFile != null) {
+ try {
+ File keepList = new File(keepListFile);
+ keepList.getParentFile().mkdirs();
+ keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList)));
+ } catch (FileNotFoundException e) {
+ Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0),
+ "Cannot open file for write.");
+ }
+ }
// If a class is public or protected, not hidden, and marked as included,
// then we can't strip it
for (ClassInfo cl : all) {
@@ -125,8 +136,8 @@
if (stubsDir != null) {
writeClassFile(stubsDir, notStrippable, cl);
}
- // build class list for xml file
- if (apiWriter != null) {
+ // build class list for api file or keep list file
+ if (apiWriter != null || keepListWriter != null) {
if (packages.containsKey(cl.containingPackage())) {
packages.get(cl.containingPackage()).add(cl);
} else {
@@ -144,6 +155,12 @@
writeApi(apiWriter, packages, notStrippable);
apiWriter.close();
}
+
+ // write out the keep list
+ if (keepListWriter != null) {
+ writeKeepList(keepListWriter, packages, notStrippable);
+ keepListWriter.close();
+ }
}
public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
@@ -1235,6 +1252,148 @@
apiWriter.print("\n");
}
+ static void writeKeepList(PrintStream keepListWriter,
+ HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) {
+ // extract the set of packages, sort them by name, and write them out in that order
+ Set<PackageInfo> allClassKeys = allClasses.keySet();
+ PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
+ Arrays.sort(allPackages, PackageInfo.comparator);
+
+ for (PackageInfo pack : allPackages) {
+ writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable);
+ }
+ }
+
+ static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack,
+ Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
+ // Work around the bogus "Array" class we invent for
+ // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
+ if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
+ return;
+ }
+
+ ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
+ Arrays.sort(classes, ClassInfo.comparator);
+ for (ClassInfo cl : classes) {
+ writeClassKeepList(keepListWriter, cl, notStrippable);
+ }
+ }
+
+ static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl,
+ HashSet<ClassInfo> notStrippable) {
+ keepListWriter.print("-keep class ");
+ keepListWriter.print(to$Class(cl.qualifiedName()));
+
+ keepListWriter.print(" {\n");
+
+ ArrayList<MethodInfo> constructors = cl.constructors();
+ Collections.sort(constructors, MethodInfo.comparator);
+ for (MethodInfo mi : constructors) {
+ writeConstructorKeepList(keepListWriter, mi);
+ }
+
+ keepListWriter.print("\n");
+
+ ArrayList<MethodInfo> methods = cl.allSelfMethods();
+ Collections.sort(methods, MethodInfo.comparator);
+ for (MethodInfo mi : methods) {
+ if (!methodIsOverride(notStrippable, mi)) {
+ writeMethodKeepList(keepListWriter, mi);
+ }
+ }
+
+ keepListWriter.print("\n");
+
+ ArrayList<FieldInfo> enums = cl.enumConstants();
+ Collections.sort(enums, FieldInfo.comparator);
+ for (FieldInfo fi : enums) {
+ writeFieldKeepList(keepListWriter, fi);
+ }
+
+ keepListWriter.print("\n");
+
+ ArrayList<FieldInfo> fields = cl.allSelfFields();
+ Collections.sort(fields, FieldInfo.comparator);
+ for (FieldInfo fi : fields) {
+ writeFieldKeepList(keepListWriter, fi);
+ }
+
+ keepListWriter.print("}\n\n");
+ }
+
+ static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
+ keepListWriter.print(" ");
+ String name = mi.name();
+ name = name.replace(".", "$");
+ keepListWriter.print(name);
+
+ writeParametersKeepList(keepListWriter, mi, mi.parameters());
+ keepListWriter.print(";\n");
+ }
+
+ static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) {
+ keepListWriter.print(" ");
+ keepListWriter.print(mi.scope());
+ if (mi.isStatic()) {
+ keepListWriter.print(" static");
+ }
+ if (mi.isAbstract()) {
+ keepListWriter.print(" abstract");
+ }
+ if (mi.isSynchronized()) {
+ keepListWriter.print(" synchronized");
+ }
+ keepListWriter.print(" ");
+ if (mi.returnType() == null) {
+ keepListWriter.print("void");
+ } else {
+ keepListWriter.print(getCleanTypeName(mi.returnType()));
+ }
+ keepListWriter.print(" ");
+ keepListWriter.print(mi.name());
+
+ writeParametersKeepList(keepListWriter, mi, mi.parameters());
+
+ keepListWriter.print(";\n");
+ }
+
+ static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method,
+ ArrayList<ParameterInfo> params) {
+ keepListWriter.print("(");
+
+ for (ParameterInfo pi : params) {
+ if (pi != params.get(0)) {
+ keepListWriter.print(", ");
+ }
+ keepListWriter.print(fullParameterTypeNameNoGenerics(method, pi.type(),
+ pi == params.get(params.size() - 1)));
+ }
+
+ keepListWriter.print(")");
+ }
+
+ static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) {
+ keepListWriter.print(" ");
+ keepListWriter.print(fi.scope());
+ if (fi.isStatic()) {
+ keepListWriter.print(" static");
+ }
+ if (fi.isTransient()) {
+ keepListWriter.print(" transient");
+ }
+ if (fi.isVolatile()) {
+ keepListWriter.print(" volatile");
+ }
+
+ keepListWriter.print(" ");
+ keepListWriter.print(getCleanTypeName(fi.type()) + fi.type().dimension());
+
+ keepListWriter.print(" ");
+ keepListWriter.print(fi.name());
+
+ keepListWriter.print(";\n");
+ }
+
static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
String fullTypeName = type.fullName(method.typeVariables());
if (isLast && method.isVarArgs()) {
@@ -1245,4 +1404,31 @@
}
return fullTypeName;
}
+
+ static String fullParameterTypeNameNoGenerics(MethodInfo method, TypeInfo type, boolean isLast) {
+ String fullTypeName = getCleanTypeName(type);
+ if (isLast && method.isVarArgs()) {
+ // TODO: note that this does not attempt to handle hypothetical
+ // vararg methods whose last parameter is a list of arrays, e.g.
+ // "Object[]...".
+ fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
+ }
+ return fullTypeName;
+ }
+
+ static String to$Class(String name) {
+ int pos = 0;
+ while ((pos = name.indexOf('.', pos)) > 0) {
+ String n = name.substring(0, pos);
+ if (Converter.obtainClass(n) != null) {
+ return n + (name.substring(pos).replace('.', '$'));
+ }
+ pos = pos + 1;
+ }
+ return name;
+ }
+
+ static String getCleanTypeName(TypeInfo t) {
+ return t.isPrimitive() ? t.simpleTypeName() : to$Class(t.asClassInfo().qualifiedName());
+ }
}