Version 1.4.45

Cherry pick: Synthesize keep rules for recompilation
CL: https://r8-review.googlesource.com/c/r8/+/33161

Bug: 124493944
Change-Id: Ib3299c174d7c3f3d1e6220f8ccdc45a1af39c2ad
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e4d1496..3d9336e 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -375,6 +375,9 @@
       }
       ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
       configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
+      if (InternalOptions.shouldEnableKeepRuleSynthesisForRecompilation()) {
+        configurationBuilder.enableKeepRuleSynthesisForRecompilation();
+      }
 
       if (proguardConfigurationConsumer != null) {
         proguardConfigurationConsumer.accept(configurationBuilder);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 112d49a..a566ac3 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.44";
+  public static final String LABEL = "1.4.45";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 0f511d4..eb9916e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import java.nio.file.Path;
@@ -63,6 +64,7 @@
         ProguardPathFilter.builder().disable();
     private boolean forceProguardCompatibility = false;
     private boolean overloadAggressively;
+    private boolean keepRuleSynthesisForRecompilation = false;
 
     private Builder(DexItemFactory dexItemFactory, Reporter reporter) {
       this.dexItemFactory = dexItemFactory;
@@ -273,8 +275,29 @@
       this.overloadAggressively = overloadAggressively;
     }
 
-    public ProguardConfiguration buildRaw() {
+    public void enableKeepRuleSynthesisForRecompilation() {
+      this.keepRuleSynthesisForRecompilation = true;
+    }
 
+    /**
+     * This synthesizes a set of keep rules that are necessary in order to be able to successfully
+     * recompile the generated dex files with the same keep rules.
+     */
+    public void synthesizeKeepRulesForRecompilation() {
+      List<ProguardConfigurationRule> synthesizedKeepRules = new ArrayList<>();
+      for (ProguardConfigurationRule rule : rules) {
+        ProguardConfigurationUtils.synthesizeKeepRulesForRecompilation(rule, synthesizedKeepRules);
+      }
+      if (rules.addAll(synthesizedKeepRules)) {
+        parsedConfiguration.add(
+            StringUtils.lines(
+                synthesizedKeepRules.stream()
+                    .map(ProguardClassSpecification::toString)
+                    .toArray(String[]::new)));
+      }
+    }
+
+    public ProguardConfiguration buildRaw() {
       ProguardConfiguration configuration = new ProguardConfiguration(
           String.join(System.lineSeparator(), parsedConfiguration),
           dexItemFactory,
@@ -334,6 +357,10 @@
         }));
       }
 
+      if (keepRuleSynthesisForRecompilation) {
+        synthesizeKeepRulesForRecompilation();
+      }
+
       return buildRaw();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index f9846b8..7dbae8a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -26,6 +26,14 @@
         }
       };
 
+  private static Origin synthesizedRecompilationOrigin =
+      new Origin(Origin.root()) {
+        @Override
+        public String part() {
+          return "<SYNTHESIZED_RECOMPILATION_RULE>";
+        }
+      };
+
   public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
     ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
     builder.setOrigin(proguardCompatOrigin);
@@ -165,4 +173,21 @@
     }
     return false;
   }
+
+  public static void synthesizeKeepRulesForRecompilation(
+      ProguardConfigurationRule rule, List<ProguardConfigurationRule> synthesizedKeepRules) {
+    if (rule.hasInheritanceClassName()) {
+      ProguardTypeMatcher inheritanceClassName = rule.getInheritanceClassName();
+      synthesizedKeepRules.add(
+          ProguardKeepRule.builder()
+              .setOrigin(synthesizedRecompilationOrigin)
+              .setType(ProguardKeepRuleType.KEEP)
+              .setClassType(
+                  rule.getInheritanceIsExtends()
+                      ? ProguardClassType.CLASS
+                      : ProguardClassType.INTERFACE)
+              .setClassNames(ProguardClassNameList.singletonList(inheritanceClassName))
+              .build());
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
index f67634e..7455469 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class ProguardKeepRuleBase extends ProguardConfigurationRule {
 
@@ -20,13 +21,19 @@
       super();
     }
 
-    public void setType(ProguardKeepRuleType type) {
+    public B setType(ProguardKeepRuleType type) {
       this.type = type;
+      return self();
     }
 
     public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
       return modifiersBuilder;
     }
+
+    public B updateModifiers(Consumer<ProguardKeepRuleModifiers.Builder> consumer) {
+      consumer.accept(getModifiersBuilder());
+      return self();
+    }
   }
 
   private final ProguardKeepRuleType type;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index 4b9c63b..eb2acee 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -21,8 +21,9 @@
       this.allowsOptimization = allowsOptimization;
     }
 
-    public void setAllowsObfuscation(boolean allowsObfuscation) {
+    public Builder setAllowsObfuscation(boolean allowsObfuscation) {
       this.allowsObfuscation = allowsObfuscation;
+      return this;
     }
 
     public void setIncludeDescriptorClasses(boolean includeDescriptorClasses) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 86cb9d0..59cbf25 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -282,6 +282,11 @@
 
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
+  public static boolean shouldEnableKeepRuleSynthesisForRecompilation() {
+    return Version.isDev()
+        && System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null;
+  }
+
   private static Set<String> getExtensiveLoggingFilter() {
     String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
     if (property != null) {