[KeepAnno] Fill out more sections in user guide
Change-Id: Ia26dc1cfb4b90462fd0d5b6e1a445d4d2b5c3e20
diff --git a/doc/keepanno-guide.md b/doc/keepanno-guide.md
index 7b16d03..75d6b24 100644
--- a/doc/keepanno-guide.md
+++ b/doc/keepanno-guide.md
@@ -110,14 +110,66 @@
## Annotating code used by reflection (or via JNI)<a id="used-by-reflection"></a>
+TODO
+
## Annotating APIs<a id="apis"></a>
+TODO
+
## Migrating rules to annotations<a id="migrating-rules"></a>
+There is no automatic migration of keep rules. Keep annotations often invert the
+direction and rules have no indication of where the reflection is taking
+place or why. Thus, migrating existing keep rules requires user involvement.
+Keep rules also have a tendency to be very general, matching a large
+number of classes and members. Often the rules are much too broad and are
+keeping more than needed which will have a negative impact on the shrinkers
+ability to reduce size.
+
+First step in converting a rule is to determine the purpose of the rule. Is it
+API surface or is it reflection? Note that a very general rule may be covering
+several use cases and even a mix of both reflection and API usage.
+
+When migrating it is preferable to use [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html) instead of
+[@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html). For very general rules it might not be easy or worth it to
+migrate without completely reevaluating the rule. If one still wants to replace
+it by annotations, the general [@KeepEdge](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepEdge.html) can be used to define a context
+independent keep annotation.
+
+For example, to keep all main methods in the program one could use:
+
+
+```
+@KeepEdge(
+ consequences = {
+ @KeepTarget(
+ kind = KeepItemKind.CLASS_AND_MEMBERS,
+ methodName = "main",
+ methodReturnType = "void",
+ methodParameters = {"java.lang.String[]"},
+ methodAccess = {MethodAccessFlags.PUBLIC, MethodAccessFlags.STATIC})
+ })
+public class SomeClass {
+ // ...
+}
+```
+
+
## My use case is not covered!<a id="other-uses"></a>
+The annotation library is in active development and not all use cases are
+described here or supported. Reach out to the R8 team by
+[filing a new issue in our tracker](https://issuetracker.google.com/issues/new?component=326788).
+Describe your use case and we will look at how best to support it.
+
## Troubleshooting<a id="troubleshooting"></a>
+
+If an annotation is not working as expected it may be helpful to inspect the
+rules that have been extracted for the annotation. This can be done by
+inspecting the configuration output of the shrinker. For R8 you can use the
+command line argument `--pg-conf-output <path>` to emit the full configuration
+used by R8.
diff --git a/doc/keepanno-guide.template.md b/doc/keepanno-guide.template.md
index 2002802..cfa98e2 100644
--- a/doc/keepanno-guide.template.md
+++ b/doc/keepanno-guide.template.md
@@ -72,14 +72,51 @@
## [Annotating code used by reflection (or via JNI)](used-by-reflection)
+TODO
+
## [Annotating APIs](apis)
+TODO
+
## [Migrating rules to annotations](migrating-rules)
+There is no automatic migration of keep rules. Keep annotations often invert the
+direction and rules have no indication of where the reflection is taking
+place or why. Thus, migrating existing keep rules requires user involvement.
+Keep rules also have a tendency to be very general, matching a large
+number of classes and members. Often the rules are much too broad and are
+keeping more than needed which will have a negative impact on the shrinkers
+ability to reduce size.
+
+First step in converting a rule is to determine the purpose of the rule. Is it
+API surface or is it reflection? Note that a very general rule may be covering
+several use cases and even a mix of both reflection and API usage.
+
+When migrating it is preferable to use `@UsesReflection` instead of
+`@UsedByReflection`. For very general rules it might not be easy or worth it to
+migrate without completely reevaluating the rule. If one still wants to replace
+it by annotations, the general `@KeepEdge` can be used to define a context
+independent keep annotation.
+
+[[[INCLUDE DOC:KeepMainMethods]]]
+
+[[[INCLUDE CODE:KeepMainMethods]]]
+
## [My use case is not covered!](other-uses)
+The annotation library is in active development and not all use cases are
+described here or supported. Reach out to the R8 team by
+[filing a new issue in our tracker](https://issuetracker.google.com/issues/new?component=326788).
+Describe your use case and we will look at how best to support it.
+
## [Troubleshooting](troubleshooting)
+
+If an annotation is not working as expected it may be helpful to inspect the
+rules that have been extracted for the annotation. This can be done by
+inspecting the configuration output of the shrinker. For R8 you can use the
+command line argument `--pg-conf-output <path>` to emit the full configuration
+used by R8.
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/MainMethodsDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/MainMethodsDocumentationTest.java
new file mode 100644
index 0000000..fdbe498
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/MainMethodsDocumentationTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno.doctests;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MainMethodsDocumentationTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello I'm kept!");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public MainMethodsDocumentationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testWithRuleExtraction() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, SomeClass.class);
+ }
+
+ /* INCLUDE DOC: KeepMainMethods
+ For example, to keep all main methods in the program one could use:
+ INCLUDE END */
+
+ static
+ // INCLUDE CODE: KeepMainMethods
+ @KeepEdge(
+ consequences = {
+ @KeepTarget(
+ kind = KeepItemKind.CLASS_AND_MEMBERS,
+ methodName = "main",
+ methodReturnType = "void",
+ methodParameters = {"java.lang.String[]"},
+ methodAccess = {MethodAccessFlags.PUBLIC, MethodAccessFlags.STATIC})
+ })
+ public class SomeClass {
+ // ...
+ }
+
+ // INCLUDE END
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello I'm kept!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
index 518d613..6fd05eb 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.keepanno.annotations.UsedByNative;
import com.android.tools.r8.keepanno.annotations.UsedByReflection;
import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.keepanno.doctests.MainMethodsDocumentationTest;
import com.android.tools.r8.keepanno.doctests.UsesReflectionDocumentationTest;
import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.Generator;
import com.android.tools.r8.utils.FileUtils;
@@ -76,7 +77,8 @@
UsedByReflection.class,
UsedByNative.class,
KeepForApi.class);
- populateCodeAndDocReplacements(UsesReflectionDocumentationTest.class);
+ populateCodeAndDocReplacements(
+ UsesReflectionDocumentationTest.class, MainMethodsDocumentationTest.class);
}
private Map<String, String> getTypeLinkReplacements(Class<?>... classes) {