Refactor embedded Proguard rules extractor
Bug: b/377144587
Change-Id: Ic7ac37612f9087af5563003bb89578303e17be53
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index f2197c4..4b1738b 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -34,7 +34,6 @@
import com.android.tools.r8.shaking.ProguardConfigurationParserOptions;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardConfigurationSource;
-import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.startup.StartupProfileProvider;
@@ -43,6 +42,7 @@
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.EmbeddedRulesExtractor;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -54,13 +54,12 @@
import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SemanticVersion;
+import com.android.tools.r8.utils.SemanticVersionUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
-import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -858,30 +857,13 @@
Reporter reporter, ProguardConfigurationParser parser) {
Supplier<SemanticVersion> semanticVersionSupplier =
- Suppliers.memoize(
- () -> {
- SemanticVersion compilerVersion =
- fakeCompilerVersion == null
- ? SemanticVersion.create(
- Version.getMajorVersion(),
- Version.getMinorVersion(),
- Version.getPatchVersion())
- : fakeCompilerVersion;
- if (compilerVersion.getMajor() < 0) {
- compilerVersion =
- SemanticVersion.create(
- Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
- reporter.warning(
- "Running R8 version "
- + Version.getVersionString()
- + ", which cannot be represented as a semantic version. Using"
- + " an artificial version newer than any known version for selecting"
- + " Proguard configurations embedded under META-INF/. This means that"
- + " all rules with a '-upto-' qualifier will be excluded and all rules"
- + " with a -from- qualifier will be included.");
- }
- return compilerVersion;
- });
+ SemanticVersionUtils.compilerVersionSemanticVersionSupplier(
+ fakeCompilerVersion,
+ "Using an artificial version newer than any known version for selecting"
+ + " Proguard configurations embedded under META-INF/. This means that"
+ + " all rules with a '-upto-' qualifier will be excluded and all rules"
+ + " with a -from- qualifier will be included.",
+ reporter);
Set<FilteredClassPath> seen = SetUtils.newIdentityHashSet();
// Find resources in program providers. Both from API and added through legacy -injars in
// configuration files.
@@ -934,8 +916,8 @@
DataResourceProvider dataResourceProvider) {
if (dataResourceProvider != null) {
try {
- ExtractEmbeddedRules embeddedProguardConfigurationVisitor =
- new ExtractEmbeddedRules(reporter, semanticVersionSupplier);
+ EmbeddedRulesExtractor embeddedProguardConfigurationVisitor =
+ new EmbeddedRulesExtractor(reporter, semanticVersionSupplier);
dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
embeddedProguardConfigurationVisitor.parseRelevantRules(parser);
} catch (ResourceException e) {
@@ -1534,126 +1516,4 @@
.build();
}
- private static class ExtractEmbeddedRules implements DataResourceProvider.Visitor {
-
- private final Supplier<SemanticVersion> compilerVersionSupplier;
- private final Reporter reporter;
- private final List<ProguardConfigurationSource> proguardSources = new ArrayList<>();
- private final List<ProguardConfigurationSource> r8Sources = new ArrayList<>();
- private SemanticVersion compilerVersion;
-
- public ExtractEmbeddedRules(
- Reporter reporter, Supplier<SemanticVersion> compilerVersionSupplier) {
- this.compilerVersionSupplier = compilerVersionSupplier;
- this.reporter = reporter;
- }
-
- @Override
- public void visit(DataDirectoryResource directory) {
- // Don't do anything.
- }
-
- @Override
- public void visit(DataEntryResource resource) {
- if (relevantProguardResource(resource)) {
- assert !relevantR8Resource(resource);
- readProguardConfigurationSource(resource, proguardSources::add);
- } else if (relevantR8Resource(resource)) {
- assert !relevantProguardResource(resource);
- readProguardConfigurationSource(resource, r8Sources::add);
- }
- }
-
- private void readProguardConfigurationSource(
- DataEntryResource resource, Consumer<ProguardConfigurationSource> consumer) {
- try (InputStream in = resource.getByteStream()) {
- consumer.accept(new ProguardConfigurationSourceBytes(in, resource.getOrigin()));
- } 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()));
- }
- }
-
- private boolean relevantProguardResource(DataEntryResource resource) {
- // Configurations in META-INF/com.android.tools/proguard/ are ignored.
- final String proguardPrefix = "META-INF/proguard";
- if (!resource.getName().startsWith(proguardPrefix)) {
- return false;
- }
- String withoutPrefix = resource.getName().substring(proguardPrefix.length());
- return withoutPrefix.startsWith("/");
- }
-
- private boolean relevantR8Resource(DataEntryResource resource) {
- final String r8Prefix = "META-INF/com.android.tools/r8";
- if (!resource.getName().startsWith(r8Prefix)) {
- return false;
- }
- String withoutPrefix = resource.getName().substring(r8Prefix.length());
- if (withoutPrefix.startsWith("/")) {
- // Everything under META-INF/com.android.tools/r8/ is included (not version specific).
- return true;
- }
- // Expect one of the following patterns:
- // com.android.tools/r8-from-1.5.0/
- // com.android.tools/r8-upto-1.6.0/
- // com.android.tools/r8-from-1.5.0-upto-1.6.0/
- final String fromPrefix = "-from-";
- final String uptoPrefix = "-upto-";
- if (!withoutPrefix.startsWith(fromPrefix) && !withoutPrefix.startsWith(uptoPrefix)) {
- return false;
- }
-
- SemanticVersion from = SemanticVersion.min();
- SemanticVersion upto = null;
-
- if (withoutPrefix.startsWith(fromPrefix)) {
- withoutPrefix = withoutPrefix.substring(fromPrefix.length());
- int versionEnd = StringUtils.indexOf(withoutPrefix, '-', '/');
- if (versionEnd == -1) {
- return false;
- }
- try {
- from = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
- } catch (IllegalArgumentException e) {
- return false;
- }
- withoutPrefix = withoutPrefix.substring(versionEnd);
- }
- if (withoutPrefix.startsWith(uptoPrefix)) {
- withoutPrefix = withoutPrefix.substring(uptoPrefix.length());
- int versionEnd = withoutPrefix.indexOf('/');
- if (versionEnd == -1) {
- return false;
- }
- try {
- upto = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
- if (compilerVersion == null) {
- compilerVersion = compilerVersionSupplier.get();
- }
- return compilerVersion.isNewerOrEqual(from)
- && (upto == null || upto.isNewer(compilerVersion));
- }
-
- private void parse(
- List<ProguardConfigurationSource> sources, ProguardConfigurationParser parser) {
- for (ProguardConfigurationSource source : sources) {
- try {
- parser.parse(source);
- } catch (Exception e) {
- reporter.error(new ExceptionDiagnostic(e, source.getOrigin()));
- }
- }
- }
-
- void parseRelevantRules(ProguardConfigurationParser parser) {
- parse(!r8Sources.isEmpty() ? r8Sources : proguardSources, parser);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/EmbeddedRulesExtractor.java b/src/main/java/com/android/tools/r8/utils/EmbeddedRulesExtractor.java
new file mode 100644
index 0000000..7f68aa3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EmbeddedRulesExtractor.java
@@ -0,0 +1,140 @@
+// Copyright (c) 2024, 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.utils;
+
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationSource;
+import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class EmbeddedRulesExtractor implements DataResourceProvider.Visitor {
+
+ private final Supplier<SemanticVersion> compilerVersionSupplier;
+ private final Reporter reporter;
+ private final List<ProguardConfigurationSource> proguardSources = new ArrayList<>();
+ private final List<ProguardConfigurationSource> r8Sources = new ArrayList<>();
+ private SemanticVersion compilerVersion;
+
+ public EmbeddedRulesExtractor(
+ Reporter reporter, Supplier<SemanticVersion> compilerVersionSupplier) {
+ this.compilerVersionSupplier = compilerVersionSupplier;
+ this.reporter = reporter;
+ }
+
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ // Don't do anything.
+ }
+
+ @Override
+ public void visit(DataEntryResource resource) {
+ if (isRelevantProguardResource(resource)) {
+ assert !isRelevantR8Resource(resource);
+ readProguardConfigurationSource(resource, proguardSources::add);
+ } else if (isRelevantR8Resource(resource)) {
+ assert !isRelevantProguardResource(resource);
+ readProguardConfigurationSource(resource, r8Sources::add);
+ }
+ }
+
+ private void readProguardConfigurationSource(
+ DataEntryResource resource, Consumer<ProguardConfigurationSource> consumer) {
+ try (InputStream in = resource.getByteStream()) {
+ consumer.accept(new ProguardConfigurationSourceBytes(in, resource.getOrigin()));
+ } 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()));
+ }
+ }
+
+ private boolean isRelevantProguardResource(DataEntryResource resource) {
+ // Configurations in META-INF/com.android.tools/proguard/ are ignored.
+ final String proguardPrefix = "META-INF/proguard";
+ if (!resource.getName().startsWith(proguardPrefix)) {
+ return false;
+ }
+ String withoutPrefix = resource.getName().substring(proguardPrefix.length());
+ return withoutPrefix.startsWith("/");
+ }
+
+ private boolean isRelevantR8Resource(DataEntryResource resource) {
+ final String r8Prefix = "META-INF/com.android.tools/r8";
+ if (!resource.getName().startsWith(r8Prefix)) {
+ return false;
+ }
+ String withoutPrefix = resource.getName().substring(r8Prefix.length());
+ if (withoutPrefix.startsWith("/")) {
+ // Everything under META-INF/com.android.tools/r8/ is included (not version specific).
+ return true;
+ }
+ // Expect one of the following patterns:
+ // com.android.tools/r8-from-1.5.0/
+ // com.android.tools/r8-upto-1.6.0/
+ // com.android.tools/r8-from-1.5.0-upto-1.6.0/
+ final String fromPrefix = "-from-";
+ final String uptoPrefix = "-upto-";
+ if (!withoutPrefix.startsWith(fromPrefix) && !withoutPrefix.startsWith(uptoPrefix)) {
+ return false;
+ }
+
+ SemanticVersion from = SemanticVersion.min();
+ SemanticVersion upto = null;
+
+ if (withoutPrefix.startsWith(fromPrefix)) {
+ withoutPrefix = withoutPrefix.substring(fromPrefix.length());
+ int versionEnd = StringUtils.indexOf(withoutPrefix, '-', '/');
+ if (versionEnd == -1) {
+ return false;
+ }
+ try {
+ from = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ withoutPrefix = withoutPrefix.substring(versionEnd);
+ }
+ if (withoutPrefix.startsWith(uptoPrefix)) {
+ withoutPrefix = withoutPrefix.substring(uptoPrefix.length());
+ int versionEnd = withoutPrefix.indexOf('/');
+ if (versionEnd == -1) {
+ return false;
+ }
+ try {
+ upto = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ if (compilerVersion == null) {
+ compilerVersion = compilerVersionSupplier.get();
+ }
+ return compilerVersion.isNewerOrEqual(from) && (upto == null || upto.isNewer(compilerVersion));
+ }
+
+ private void parse(
+ List<ProguardConfigurationSource> sources, ProguardConfigurationParser parser) {
+ for (ProguardConfigurationSource source : sources) {
+ try {
+ parser.parse(source);
+ } catch (Exception e) {
+ reporter.error(new ExceptionDiagnostic(e, source.getOrigin()));
+ }
+ }
+ }
+
+ public void parseRelevantRules(ProguardConfigurationParser parser) {
+ parse(!r8Sources.isEmpty() ? r8Sources : proguardSources, parser);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/SemanticVersionUtils.java b/src/main/java/com/android/tools/r8/utils/SemanticVersionUtils.java
new file mode 100644
index 0000000..238e620
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SemanticVersionUtils.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2024, 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.utils;
+
+import com.android.tools.r8.Version;
+import com.google.common.base.Suppliers;
+import java.util.function.Supplier;
+
+public class SemanticVersionUtils {
+
+ public static Supplier<SemanticVersion> compilerVersionSemanticVersionSupplier(
+ SemanticVersion forceCompilerVersion,
+ String artificialMaxVersionWarningInfo,
+ Reporter reporter) {
+ return Suppliers.memoize(
+ () -> {
+ SemanticVersion compilerVersion =
+ forceCompilerVersion == null
+ ? SemanticVersion.create(
+ Version.getMajorVersion(),
+ Version.getMinorVersion(),
+ Version.getPatchVersion())
+ : forceCompilerVersion;
+ if (compilerVersion.getMajor() < 0) {
+ compilerVersion =
+ SemanticVersion.create(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ reporter.warning(
+ "Running R8 version "
+ + Version.getVersionString()
+ + ", which cannot be represented as a semantic version."
+ + (artificialMaxVersionWarningInfo == null
+ ? ""
+ : (" " + artificialMaxVersionWarningInfo)));
+ }
+ return compilerVersion;
+ });
+ }
+}