Read META-INF rules and configuration injars in a fixed point.

Change-Id: I420913e5b5211193ed1f157abcc51da60a7ac1f5
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e579889..0bf09ac 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.profile.art.ArtProfileForRewriting;
+import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardConfigurationParserOptions;
@@ -30,6 +31,7 @@
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
 import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
 import com.android.tools.r8.utils.DumpInputFlags;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -40,18 +42,21 @@
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Deque;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -556,47 +561,6 @@
       ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
       configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
 
-      if (proguardConfigurationConsumerForTesting != null) {
-        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
-      }
-
-      // Process Proguard configurations supplied through data resources in the input.
-      DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
-          new DataResourceProvider.Visitor() {
-            @Override
-            public void visit(DataDirectoryResource directory) {
-              // Don't do anything.
-            }
-
-            @Override
-            public void visit(DataEntryResource resource) {
-              if (resource.getName().startsWith("META-INF/proguard/")) {
-                try (InputStream in = resource.getByteStream()) {
-                  ProguardConfigurationSource source =
-                      new ProguardConfigurationSourceBytes(in, resource.getOrigin());
-                  parser.parse(source);
-                } catch (ResourceException e) {
-                  reporter.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
-                      resource.getOrigin()));
-                } catch (Exception e) {
-                  reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
-                }
-              }
-            }
-          };
-
-      getAppBuilder().getProgramResourceProviders().stream()
-          .map(ProgramResourceProvider::getDataResourceProvider)
-          .filter(Objects::nonNull)
-          .forEach(
-              dataResourceProvider -> {
-                try {
-                  dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
-                } catch (ResourceException e) {
-                  reporter.error(new ExceptionDiagnostic(e));
-                }
-              });
-
       if (getMode() == CompilationMode.DEBUG) {
         disableMinification = true;
         configurationBuilder.disableOptimization();
@@ -610,10 +574,12 @@
         configurationBuilder.disableObfuscation();
       }
 
+      if (proguardConfigurationConsumerForTesting != null) {
+        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
+      }
+      amendWithRulesAndProvidersForInjarsAndMetaInf(reporter, parser);
       ProguardConfiguration configuration = configurationBuilder.build();
-      getAppBuilder()
-          .addFilteredProgramArchives(configuration.getInjars())
-          .addFilteredLibraryArchives(configuration.getLibraryjars());
+      getAppBuilder().addFilteredLibraryArchives(configuration.getLibraryjars());
 
       assert getProgramConsumer() != null;
 
@@ -673,6 +639,64 @@
       return command;
     }
 
+    private void amendWithRulesAndProvidersForInjarsAndMetaInf(
+        Reporter reporter, ProguardConfigurationParser parser) {
+
+      // Process Proguard configurations supplied through data resources in the input.
+      DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
+          new DataResourceProvider.Visitor() {
+            @Override
+            public void visit(DataDirectoryResource directory) {
+              // Don't do anything.
+            }
+
+            @Override
+            public void visit(DataEntryResource resource) {
+              if (resource.getName().startsWith("META-INF/proguard/")) {
+                try (InputStream in = resource.getByteStream()) {
+                  ProguardConfigurationSource source =
+                      new ProguardConfigurationSourceBytes(in, resource.getOrigin());
+                  parser.parse(source);
+                } catch (ResourceException e) {
+                  reporter.error(
+                      new StringDiagnostic(
+                          "Failed to open input: " + e.getMessage(), resource.getOrigin()));
+                } catch (Exception e) {
+                  reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
+                }
+              }
+            }
+          };
+
+      // Since -injars can itself reference archives with rules and that in turn have -injars the
+      // completion of amending rules and providers must run in a fixed point. The fixed point is
+      // reached once the injars set is stable.
+      Set<FilteredClassPath> seenInjars = SetUtils.newIdentityHashSet();
+      Deque<ProgramResourceProvider> providers =
+          new ArrayDeque<>(getAppBuilder().getProgramResourceProviders());
+      while (true) {
+        for (FilteredClassPath injar : parser.getConfigurationBuilder().getInjars()) {
+          if (seenInjars.add(injar)) {
+            ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar);
+            providers.add(provider);
+          }
+        }
+        if (providers.isEmpty()) {
+          return;
+        }
+        while (!providers.isEmpty()) {
+          DataResourceProvider dataResourceProvider = providers.pop().getDataResourceProvider();
+          if (dataResourceProvider != null) {
+            try {
+              dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
+            } catch (ResourceException e) {
+              reporter.error(new ExceptionDiagnostic(e));
+            }
+          }
+        }
+      }
+    }
+
     // Internal for-testing method to add post-processors of the proguard configuration.
     void addProguardConfigurationConsumerForTesting(Consumer<ProguardConfiguration.Builder> c) {
       Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumerForTesting;