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;
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 99edc77..955069f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -76,6 +76,10 @@
this.reporter = reporter;
}
+ public List<FilteredClassPath> getInjars() {
+ return injars;
+ }
+
public void addParsedConfiguration(String source) {
parsedConfiguration.add(source);
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index a72a237..9f8c41b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -992,20 +992,25 @@
return this;
}
+ public ArchiveResourceProvider createAndAddProvider(FilteredClassPath archive) {
+ if (isArchive(archive.getPath())) {
+ ArchiveResourceProvider archiveResourceProvider =
+ new ArchiveResourceProvider(archive, ignoreDexInArchive);
+ addProgramResourceProvider(archiveResourceProvider);
+ return archiveResourceProvider;
+ }
+ reporter.error(
+ new StringDiagnostic(
+ "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
+ archive.getOrigin(),
+ archive.getPosition()));
+ return null;
+ }
+
/** Add filtered archives of program resources. */
public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives) {
for (FilteredClassPath archive : filteredArchives) {
- if (isArchive(archive.getPath())) {
- ArchiveResourceProvider archiveResourceProvider =
- new ArchiveResourceProvider(archive, ignoreDexInArchive);
- addProgramResourceProvider(archiveResourceProvider);
- } else {
- reporter.error(
- new StringDiagnostic(
- "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
- archive.getOrigin(),
- archive.getPosition()));
- }
+ createAndAddProvider(archive);
}
return this;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
index a0029a0..2741404 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
@@ -84,16 +85,26 @@
}
}
+ enum ProviderType {
+ API,
+ INJARS
+ }
+
@Parameter(0)
public TestParameters parameters;
@Parameter(1)
public LibraryType libraryType;
- @Parameters(name = "{0} AAR: {1}")
+ @Parameter(2)
+ public ProviderType providerType;
+
+ @Parameters(name = "{0}, AAR: {1}, {2}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), LibraryType.values());
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(),
+ LibraryType.values(),
+ ProviderType.values());
}
private Path buildLibrary(List<String> rules) throws Exception {
@@ -121,8 +132,10 @@
}
private CodeInspector runTest(List<String> rules) throws Exception {
+ Path library = buildLibrary(rules);
return testForR8(parameters.getBackend())
- .addProgramFiles(buildLibrary(rules))
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
.setMinApi(parameters.getApiLevel())
.compile()
.inspector();