Update androidx keep annotation generation
* Match the current source more closely also adding
`@Suppress("KotlinDefaultParameterOrder")` where fields with default
values are before fields without default vaules.
* Update the tool to copy source to the androidx repository
Bug: b/392865072
Change-Id: Ic1839b895cde5898afa24768ea753e7cdc781674diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
index b02671c..b453d72 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
@@ -36,8 +36,8 @@
* @see UsesReflectionToConstruct
* @see UsesReflectionToAccessMethod
*/
-@Repeatable
@Retention(AnnotationRetention.BINARY)
+@Repeatable
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FIELD,
@@ -51,14 +51,14 @@
*
* Mutually exclusive with [className].
*/
- val classConstant: KClass<*> = Unspecified::class,
+ @Suppress("KotlinDefaultParameterOrder") val classConstant: KClass<*> = Unspecified::class,
/**
* Class name (or class name pattern) containing the field accessed by reflection.
*
* Mutually exclusive with [classConstant].
*/
- val className: String = "",
+ @Suppress("KotlinDefaultParameterOrder") val className: String = "",
/** Name (or name pattern) of field accessed by reflection. */
val fieldName: String,
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
index 60b4e71..d750e70 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
@@ -36,8 +36,8 @@
* @see UsesReflectionToConstruct
* @see UsesReflectionToAccessField
*/
-@Repeatable
@Retention(AnnotationRetention.BINARY)
+@Repeatable
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FIELD,
@@ -51,14 +51,14 @@
*
* Mutually exclusive with [className].
*/
- val classConstant: KClass<*> = Unspecified::class,
+ @Suppress("KotlinDefaultParameterOrder") val classConstant: KClass<*> = Unspecified::class,
/**
* Class name (or class name pattern) containing the method accessed by reflection.
*
* Mutually exclusive with [classConstant].
*/
- val className: String = "",
+ @Suppress("KotlinDefaultParameterOrder") val className: String = "",
/** Name (or name pattern) of method accessed by reflection. */
val methodName: String,
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
index 75f4d07..ff39d03 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
@@ -39,8 +39,8 @@
* @see UsesReflectionToAccessMethod
* @see UsesReflectionToAccessField
*/
-@Repeatable
@Retention(AnnotationRetention.BINARY)
+@Repeatable
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FIELD,
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 3ff6678..7121cbd 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -134,6 +134,7 @@
final String kotlinName;
String valueType = null;
String valueDefault = null;
+ boolean suppressKotlinDefaultParameterOrder = false;
GroupMember(String name) {
this(name, name);
@@ -154,6 +155,11 @@
return this;
}
+ public GroupMember setSuppressKotlinDefaultParameterOrder() {
+ this.suppressKotlinDefaultParameterOrder = true;
+ return this;
+ }
+
@Override
public GroupMember self() {
return this;
@@ -165,12 +171,17 @@
generator.println("@Deprecated");
}
if (generator.generateKotlin()) {
- if (kotlinValueDefault() == null) {
- generator.println("val " + kotlinName + ": " + kotlinValueType() + ",");
- } else {
- generator.println(
- "val " + kotlinName + ": " + kotlinValueType() + " = " + kotlinValueDefault() + ",");
+ StringBuilder builder = new StringBuilder();
+ if (suppressKotlinDefaultParameterOrder) {
+ builder.append("@Suppress(\"KotlinDefaultParameterOrder\") ");
}
+ builder.append("val ").append(kotlinName).append(": ").append(kotlinValueType());
+ if (kotlinValueDefault() == null) {
+ builder.append(",");
+ } else {
+ builder.append(" = ").append(kotlinValueDefault()).append(",");
+ }
+ generator.println(builder.toString());
} else {
if (valueDefault == null) {
generator.println(valueType + " " + name + "();");
@@ -688,22 +699,30 @@
}
}
- private void printOpenAnnotationClassTargetingClassFieldMethodCtor(String clazz) {
+ private void printOpenAnnotationClassTargetingClassFieldMethodCtor(ClassReference clazz) {
+ String unqualifiedName = getUnqualifiedName(clazz);
if (generateKotlin()) {
println("@Retention(AnnotationRetention.BINARY)");
+ if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+ println("@Repeatable");
+ }
println("@Target(");
println(" AnnotationTarget.CLASS,");
println(" AnnotationTarget.FIELD,");
println(" AnnotationTarget.FUNCTION,");
println(" AnnotationTarget.CONSTRUCTOR,");
println(")");
- println("public annotation class " + clazz + "(");
+ println("public annotation class " + unqualifiedName + "(");
} else {
+ if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+ throw new RuntimeException(
+ "Repeatable annotations not supported for Java code generation");
+ }
println(
"@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
+ " ElementType.CONSTRUCTOR})");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + clazz + " {");
+ println("public @interface " + unqualifiedName + " {");
}
}
@@ -1745,7 +1764,7 @@
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
- printOpenAnnotationClassTargetingClassFieldMethodCtor("KeepForApi");
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(KEEP_FOR_API);
println();
withIndent(
() -> {
@@ -1834,7 +1853,7 @@
" // unreachable",
" }")
.printDoc(this::println);
- printOpenAnnotationClassTargetingClassFieldMethodCtor(getUnqualifiedName(USES_REFLECTION));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION);
println();
withIndent(
() -> {
@@ -1941,9 +1960,7 @@
.addSection("@see UsesReflectionToAccessMethod")
.addSection("@see UsesReflectionToAccessField")
.printDoc(this::println);
- println("@Repeatable");
- printOpenAnnotationClassTargetingClassFieldMethodCtor(
- getUnqualifiedName(USES_REFLECTION_TO_CONSTRUCT));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_CONSTRUCT);
println();
withIndent(
() -> {
@@ -1990,18 +2007,19 @@
.addSection("@see UsesReflectionToConstruct")
.addSection("@see UsesReflectionToAccessField")
.printDoc(this::println);
- println("@Repeatable");
- printOpenAnnotationClassTargetingClassFieldMethodCtor(
- getUnqualifiedName(USES_REFLECTION_TO_ACCESS_METHOD));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_ACCESS_METHOD);
println();
withIndent(
() -> {
createAndroidXClassSelection(
- g -> g.setDocTitle("Class containing the method accessed by reflection."),
g ->
- g.setDocTitle(
- "Class name (or class name pattern) containing the method accessed by"
- + " reflection."))
+ g.setSuppressKotlinDefaultParameterOrder()
+ .setDocTitle("Class containing the method accessed by reflection."),
+ g ->
+ g.setSuppressKotlinDefaultParameterOrder()
+ .setDocTitle(
+ "Class name (or class name pattern) containing the method accessed"
+ + " by reflection."))
.generate(this);
println();
createMethodNameSelection().generate(this);
@@ -2044,18 +2062,19 @@
+ " preserved if the annotated code is reachable in the final application build.")
.addSection("@see UsesReflectionToConstruct", "@see UsesReflectionToAccessMethod")
.printDoc(this::println);
- println("@Repeatable");
- printOpenAnnotationClassTargetingClassFieldMethodCtor(
- getUnqualifiedName(USES_REFLECTION_TO_ACCESS_FIELD));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(USES_REFLECTION_TO_ACCESS_FIELD);
println();
withIndent(
() -> {
createAndroidXClassSelection(
- g -> g.setDocTitle("Class containing the field accessed by reflection."),
g ->
- g.setDocTitle(
- "Class name (or class name pattern) containing the field accessed by"
- + " reflection."))
+ g.setSuppressKotlinDefaultParameterOrder()
+ .setDocTitle("Class containing the field accessed by reflection."),
+ g ->
+ g.setSuppressKotlinDefaultParameterOrder()
+ .setDocTitle(
+ "Class name (or class name pattern) containing the field accessed"
+ + " by reflection."))
.generate(this);
println();
createAndroidXFieldNameSelection().generate(this);
@@ -2090,8 +2109,7 @@
"@see UsesReflectionToAccessMethod",
"@see UsesReflectionToAccessField")
.printDoc(this::println);
- printOpenAnnotationClassTargetingClassFieldMethodCtor(
- getUnqualifiedName(UNCONDITIONALLY_KEEP));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(UNCONDITIONALLY_KEEP);
println();
withIndent(
() -> {
@@ -2108,7 +2126,7 @@
printCloseAnnotationClass();
}
- private void generateUsedByX(String annotationClassName, String doc) {
+ private void generateUsedByX(ClassReference clazz, String doc) {
printCopyRight(2023);
printPackage();
printAnnotationImports();
@@ -2137,7 +2155,7 @@
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
- printOpenAnnotationClassTargetingClassFieldMethodCtor(annotationClassName);
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(clazz);
println();
withIndent(
() -> {
@@ -2746,15 +2764,16 @@
private static void writeFile(
String pkg,
Function<Generator, ClassReference> f,
- Consumer<Generator> fn,
+ BiConsumer<Generator, ClassReference> fn,
BiConsumer<Path, String> write)
throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteStream);
Generator generator = new Generator(printStream, pkg);
- fn.accept(generator);
+ ClassReference clazz = f.apply(generator);
+ fn.accept(generator, clazz);
String formatted = byteStream.toString();
- Path file = source(f.apply(generator));
+ Path file = source(clazz);
if (file.toString().endsWith(".kt")) {
formatted = CodeGenerationBase.kotlinFormatRawOutput(formatted);
} else if (file.toString().endsWith(".java")) {
@@ -2783,7 +2802,7 @@
writeFile(
ANDROIDX_ANNO_PKG,
generator -> generator.ANNOTATION_CONSTANTS,
- Generator::generateConstants,
+ (generator, clazz) -> generator.generateConstants(),
write);
// Create a copy of the non-generated classes in the androidx namespace.
// This is currently disabled. Will potentially be re-introduced by converting these classes
@@ -2791,61 +2810,86 @@
// copyNonGeneratedMethods();
for (String pkg : new String[] {R8_ANNO_PKG, ANDROIDX_ANNO_PKG}) {
writeFile(
- pkg, generator -> generator.STRING_PATTERN, Generator::generateStringPattern, write);
- writeFile(pkg, generator -> generator.TYPE_PATTERN, Generator::generateTypePattern, write);
+ pkg,
+ generator -> generator.STRING_PATTERN,
+ (generator, clazz) -> generator.generateStringPattern(),
+ write);
+ writeFile(
+ pkg,
+ generator -> generator.TYPE_PATTERN,
+ (generator, clazz) -> generator.generateTypePattern(),
+ write);
writeFile(
pkg,
generator -> generator.CLASS_NAME_PATTERN,
- Generator::generateClassNamePattern,
+ (generator, clazz) -> generator.generateClassNamePattern(),
write);
writeFile(
pkg,
generator -> generator.INSTANCE_OF_PATTERN,
- Generator::generateInstanceOfPattern,
+ (generator, clazz) -> generator.generateInstanceOfPattern(),
write);
writeFile(
pkg,
generator -> generator.ANNOTATION_PATTERN,
- Generator::generateAnnotationPattern,
+ (generator, clazz) -> generator.generateAnnotationPattern(),
write);
- writeFile(pkg, generator -> generator.KEEP_BINDING, Generator::generateKeepBinding, write);
- writeFile(pkg, generator -> generator.KEEP_TARGET, Generator::generateKeepTarget, write);
writeFile(
- pkg, generator -> generator.KEEP_CONDITION, Generator::generateKeepCondition, write);
- writeFile(pkg, generator -> generator.KEEP_FOR_API, Generator::generateKeepForApi, write);
+ pkg,
+ generator -> generator.KEEP_BINDING,
+ (generator, clazz) -> generator.generateKeepBinding(),
+ write);
writeFile(
- pkg, generator -> generator.USES_REFLECTION, Generator::generateUsesReflection, write);
+ pkg,
+ generator -> generator.KEEP_TARGET,
+ (generator, clazz) -> generator.generateKeepTarget(),
+ write);
+ writeFile(
+ pkg,
+ generator -> generator.KEEP_CONDITION,
+ (generator, clazz) -> generator.generateKeepCondition(),
+ write);
+ writeFile(
+ pkg,
+ generator -> generator.KEEP_FOR_API,
+ (generator, clazz) -> generator.generateKeepForApi(),
+ write);
+ writeFile(
+ pkg,
+ generator -> generator.USES_REFLECTION,
+ (generator, clazz) -> generator.generateUsesReflection(),
+ write);
writeFile(
pkg,
generator -> generator.USED_BY_REFLECTION,
- generator -> generator.generateUsedByX("UsedByReflection", "accessed reflectively"),
+ (generator, clazz) -> generator.generateUsedByX(clazz, "accessed reflectively"),
write);
writeFile(
pkg,
generator -> generator.USED_BY_NATIVE,
- generator ->
- generator.generateUsedByX("UsedByNative", "accessed from native code via JNI"),
+ (generator, clazz) ->
+ generator.generateUsedByX(clazz, "accessed from native code via JNI"),
write);
}
writeFile(
ANDROIDX_ANNO_PKG,
generator -> generator.USES_REFLECTION_TO_CONSTRUCT,
- Generator::generateUsesReflectionToConstruct,
+ (generator, clazz) -> generator.generateUsesReflectionToConstruct(),
write);
writeFile(
ANDROIDX_ANNO_PKG,
generator -> generator.USES_REFLECTION_TO_ACCESS_METHOD,
- Generator::generateUsesReflectionToAccessMethod,
+ (generator, clazz) -> generator.generateUsesReflectionToAccessMethod(),
write);
writeFile(
ANDROIDX_ANNO_PKG,
generator -> generator.USES_REFLECTION_TO_ACCESS_FIELD,
- Generator::generateUsesReflectionToAccessField,
+ (generator, clazz) -> generator.generateUsesReflectionToAccessField(),
write);
writeFile(
ANDROIDX_ANNO_PKG,
generator -> generator.UNCONDITIONALLY_KEEP,
- Generator::generateUnconditionallyKeep,
+ (generator, clazz) -> generator.generateUnconditionallyKeep(),
write);
}
}
diff --git a/tools/update-androidx-keep-annotations.py b/tools/update-androidx-keep-annotations.py
index 29bf464..3f5da7c 100755
--- a/tools/update-androidx-keep-annotations.py
+++ b/tools/update-androidx-keep-annotations.py
@@ -15,10 +15,17 @@
def parse_options():
parser = argparse.ArgumentParser(description='Update androidx keep annotations')
- parser.add_argument('--androidx',
- metavar=('<path>'),
- required=True,
- help='Path to the androidx checkout')
+ parser.add_argument(
+ '--androidx',
+ metavar=('<path>'),
+ required=True,
+ help='Path to the androidx checkout')
+ parser.add_argument(
+ '--dry-run',
+ '--dry_run',
+ help="Don't copy, just print what to copy",
+ default=False,
+ action='store_true')
return parser.parse_args()
@@ -59,9 +66,22 @@
.format(filename=os.path.join(root, filename)))
sys.exit(1)
- shutil.copyfile(
- os.path.join(root, filename),
- os.path.join(dest_dir, filename))
+ files = (
+ 'UnconditionallyKeep.kt',
+ 'Unspecified.kt',
+ 'UsesReflectionToAccessField.kt',
+ 'UsesReflectionToAccessMethod.kt',
+ 'UsesReflectionToConstruct.kt',
+ )
+ if not filename in files:
+ print('Skipping {filename}'.format(filename=filename))
+ continue
+
+ src = os.path.join(root, filename)
+ dest = os.path.join(dest_dir, filename)
+ print("Copying '{src}' to '{dest}'".format(src=src, dest=dest))
+ if not args.dry_run:
+ shutil.copyfile(src, dest)
if __name__ == '__main__':