[KeepAnno] Print keep-annotation meta-info to configuration consumers

This adds the meta-info printing and the rest is just toString on the
AST.  The printing format should likely coincide with the distribution
format for normalized edges once that format is chosen.

Bug: b/323816623
Change-Id: I338662616ed607392ff891af7853a183293ebc92
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 05ee81c..9932654 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -215,6 +215,12 @@
 
   @Override
   public String toString() {
-    return "KeepEdge{" + "preconditions=" + preconditions + ", consequences=" + consequences + '}';
+    return "KeepEdge{metainfo="
+        + getMetaInfo()
+        + ", preconditions="
+        + preconditions
+        + ", consequences="
+        + consequences
+        + '}';
   }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
index 6d4705a..9cf3305 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.ast;
 
+import com.android.tools.r8.keepanno.keeprules.RulePrintingUtils;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 public class KeepEdgeMetaInfo {
@@ -62,6 +65,21 @@
     return version;
   }
 
+  public String toString() {
+    List<String> props = new ArrayList<>(3);
+    if (hasVersion()) {
+      props.add("version=" + version);
+    }
+    if (hasContext()) {
+      props.add("context=" + context.getDescriptorString());
+    }
+    if (hasDescription()) {
+      props.add(
+          "description=\"" + RulePrintingUtils.escapeLineBreaks(description.description) + "\"");
+    }
+    return "MetaInfo{" + String.join(", ", props) + "}";
+  }
+
   public static class Builder {
     private KeepEdgeContext context = KeepEdgeContext.none();
     private KeepEdgeDescription description = KeepEdgeDescription.empty();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index fe43d5c..dbc94d4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -868,6 +868,7 @@
       assert appView.dexItemFactory().verifyNoCachedTypeElements();
 
       // Generate the resulting application resources.
+      writeKeepDeclarationsToConfigurationConsumer(keepDeclarations);
       writeApplication(appView, inputApp, executorService);
 
       if (options.androidResourceProvider != null && options.androidResourceConsumer != null) {
@@ -888,6 +889,27 @@
     }
   }
 
+  private void writeKeepDeclarationsToConfigurationConsumer(
+      List<KeepDeclaration> keepDeclarations) {
+    if (options.configurationConsumer == null) {
+      return;
+    }
+    if (keepDeclarations.isEmpty()) {
+      return;
+    }
+    for (KeepDeclaration declaration : keepDeclarations) {
+      List<String> lines = StringUtils.splitLines(declaration.toString());
+      StringBuilder builder = new StringBuilder();
+      builder.append("# Start of content from keep annotations\n");
+      for (String line : lines) {
+        builder.append("# ").append(line).append("\n");
+      }
+      builder.append("# End of content from keep annotations\n");
+      ExceptionUtils.withConsumeResourceHandler(
+          options.reporter, options.configurationConsumer, builder.toString());
+    }
+  }
+
   private static ForwardingConsumer wrapConsumerStoreBytesInList(
       Map<String, byte[]> dexFileContent,
       DexIndexedConsumer programConsumer,
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
index e1c5dd9..905cc81 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -38,13 +38,22 @@
   @Test
   public void test() throws Exception {
     testForKeepAnno(parameters)
+        .enableNativeInterpretation()
         .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setExcludedOuterClass(getClass())
         .inspectOutputConfig(
             rules -> {
-              assertThat(rules, containsString("context: " + descriptor(A.class) + "foo()V"));
-              assertThat(rules, containsString("description: Keep the\\nstring-valued fields"));
+              if (parameters.isNativeR8()) {
+                // TODO(b/323816623): Once a final distribution format is defined for normalized
+                //  edges, that format should likely be the bases of the annotation printing too.
+                assertThat(rules, containsString("context=" + descriptor(A.class) + "foo()V"));
+                assertThat(
+                    rules, containsString("description=\"Keep the\\nstring-valued fields\""));
+              } else {
+                assertThat(rules, containsString("context: " + descriptor(A.class) + "foo()V"));
+                assertThat(rules, containsString("description: Keep the\\nstring-valued fields"));
+              }
             })
         .run(TestClass.class)
         .assertSuccessWithOutput(EXPECTED)