Move parsed configuration logic into proguard configuration builder
This also fixes a bug where the rules of a given keep rule file were not
always emitted between the header and the footer in the parsed
configuration output.
Change-Id: Ib9dd11ba9bd56071d49d2c29e7bced254e6fba8b
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
index 4694ecd..00f8172 100644
--- a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
+++ b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
@@ -163,6 +163,11 @@
}
@Override
+ public void addParsedConfiguration(ProguardConfigurationSourceParser parser) {
+ // Intentionally empty.
+ }
+
+ @Override
public void addRule(ProguardConfigurationRule rule) {
ensureNewlineAfterComment();
write(rule.getSource());
@@ -339,9 +344,6 @@
}
@Override
- public void addParsedConfiguration(String s) {}
-
- @Override
public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
index bf2b323..f1866b4 100644
--- a/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
+++ b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
@@ -18,6 +18,7 @@
import java.util.List;
class ValidateLibraryConsumerRulesKeepRuleProcessor implements ProguardConfigurationParserConsumer {
+
private final Reporter reporter;
public ValidateLibraryConsumerRulesKeepRuleProcessor(Reporter reporter) {
@@ -85,7 +86,7 @@
}
@Override
- public void addParsedConfiguration(String s) {}
+ public void addParsedConfiguration(ProguardConfigurationSourceParser parser) {}
@Override
public void addRule(ProguardConfigurationRule rule) {}
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 4634bf4..7778a92 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.naming.DictionaryReader;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IncludeWorkItem;
import com.android.tools.r8.shaking.ProguardConfigurationParser.ProguardConfigurationSourceParser;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.Reporter;
@@ -25,7 +26,7 @@
public static class Builder implements ProguardConfigurationParserConsumer {
- private final List<String> parsedConfiguration = new ArrayList<>();
+ private final StringBuilder parsedConfiguration = new StringBuilder();
private final List<FilteredClassPath> injars = new ArrayList<>();
private final List<FilteredClassPath> libraryJars = new ArrayList<>();
@@ -82,8 +83,22 @@
}
@Override
- public void addParsedConfiguration(String source) {
- parsedConfiguration.add(source);
+ public void addParsedConfiguration(ProguardConfigurationSourceParser parser) {
+ parsedConfiguration.append(
+ "# The proguard configuration file for the following section is " + parser.getOrigin());
+ parsedConfiguration.append(System.lineSeparator());
+ int lastIncludePositionEnd = 0;
+ for (IncludeWorkItem pendingInclude : parser.getPendingIncludes()) {
+ int includePositionStart = pendingInclude.includePositionStart.getOffsetAsInt();
+ parsedConfiguration.append(
+ parser.getContentInRange(lastIncludePositionEnd, includePositionStart));
+ lastIncludePositionEnd = pendingInclude.includePositionEnd;
+ }
+ parsedConfiguration.append(parser.getContentAfter(lastIncludePositionEnd));
+ parsedConfiguration.append(System.lineSeparator());
+ parsedConfiguration.append("# End of content from ");
+ parsedConfiguration.append(parser.getOrigin());
+ parsedConfiguration.append(System.lineSeparator());
}
@Override
@@ -399,7 +414,7 @@
}
ProguardConfiguration configuration =
new ProguardConfiguration(
- String.join(System.lineSeparator(), parsedConfiguration),
+ parsedConfiguration.toString(),
dexItemFactory,
injars,
libraryJars,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index c6aaf59..368dad32 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -39,8 +39,11 @@
import java.nio.CharBuffer;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -224,12 +227,13 @@
private final String name;
private final String contents;
private int position = 0;
- private int positionAfterInclude = 0;
private int line = 1;
private int lineStartPosition = 0;
private Path baseDirectory;
private final Origin origin;
+ private final Deque<IncludeWorkItem> pendingIncludes = new ArrayDeque<>();
+
ProguardConfigurationSourceParser(ProguardConfigurationSource source) throws IOException {
// Strip any leading BOM here so it is not included in the text position.
contents = StringUtils.stripLeadingBOM(source.get());
@@ -238,14 +242,26 @@
this.origin = source.getOrigin();
}
+ public String getContentAfter(int start) {
+ return getContentInRange(start, contents.length());
+ }
+
+ public String getContentInRange(int start, int end) {
+ return contents.substring(start, end);
+ }
+
public String getContentSince(TextPosition start) {
- return contents.substring(start.getOffsetAsInt(), position);
+ return getContentInRange(start.getOffsetAsInt(), position);
}
public Origin getOrigin() {
return origin;
}
+ public Collection<IncludeWorkItem> getPendingIncludes() {
+ return pendingIncludes;
+ }
+
public void parse() throws ProguardRuleParserException {
do {
TextPosition whitespaceStart = getPosition();
@@ -253,14 +269,11 @@
configurationConsumer.addWhitespace(this, whitespaceStart);
}
} while (parseOption());
- // This may be unknown, but we want to always ensure that we don't attribute lines to the
- // wrong configuration.
- configurationConsumer.addParsedConfiguration(
- "# The proguard configuration file for the following section is " + origin.toString());
-
- // Collect the parsed configuration.
- configurationConsumer.addParsedConfiguration(contents.substring(positionAfterInclude));
- configurationConsumer.addParsedConfiguration("# End of content from " + origin);
+ configurationConsumer.addParsedConfiguration(this);
+ while (!pendingIncludes.isEmpty()) {
+ IncludeWorkItem includeWorkItem = pendingIncludes.removeFirst();
+ parseInclude(includeWorkItem.includePath, includeWorkItem.includePositionStart);
+ }
reporter.failIfPendingErrors();
}
@@ -268,10 +281,10 @@
if (eof()) {
return false;
}
- if (acceptArobaseInclude()) {
+ TextPosition optionStart = getPosition();
+ if (acceptArobaseInclude(optionStart)) {
return true;
}
- TextPosition optionStart = getPosition();
expectChar('-');
if (parseIgnoredOption(optionStart)
|| parseIgnoredOptionAndWarn(optionStart)
@@ -424,12 +437,8 @@
ProguardAssumeValuesRule rule = parseAssumeValuesRule(optionStart);
configurationConsumer.addRule(rule);
} else if (acceptString("include")) {
- // Collect the parsed configuration until the include.
- configurationConsumer.addParsedConfiguration(
- contents.substring(positionAfterInclude, position - ("include".length() + 1)));
skipWhitespace();
- parseInclude();
- positionAfterInclude = position;
+ enqueueInclude(optionStart);
} else if (acceptString("basedirectory")) {
skipWhitespace();
baseDirectory = parseFileName();
@@ -732,29 +741,33 @@
|| parseOptimizationOption(optionStart);
}
- private void parseInclude() throws ProguardRuleParserException {
- TextPosition start = getPosition();
- Path included = parseFileInputDependency(inputDependencyConsumer::acceptProguardInclude);
+ private void enqueueInclude(TextPosition optionStart) throws ProguardRuleParserException {
+ Path includePath = parseFileInputDependency(inputDependencyConsumer::acceptProguardInclude);
+ pendingIncludes.add(new IncludeWorkItem(includePath, optionStart, position));
+ }
+
+ private void parseInclude(Path includePath, TextPosition includePositionStart)
+ throws ProguardRuleParserException {
try {
- new ProguardConfigurationSourceParser(new ProguardConfigurationSourceFile(included))
+ new ProguardConfigurationSourceParser(new ProguardConfigurationSourceFile(includePath))
.parse();
} catch (FileNotFoundException | NoSuchFileException e) {
- throw parseError("Included file '" + included.toString() + "' not found",
- start, e);
+ throw parseError("Included file '" + includePath + "' not found", includePositionStart, e);
} catch (IOException e) {
- throw parseError("Failed to read included file '" + included.toString() + "'",
- start, e);
+ throw parseError(
+ "Failed to read included file '" + includePath + "'", includePositionStart, e);
}
}
- private boolean acceptArobaseInclude() throws ProguardRuleParserException {
+ private boolean acceptArobaseInclude(TextPosition optionStart)
+ throws ProguardRuleParserException {
if (remainingChars() < 2) {
return false;
}
if (!acceptChar('@')) {
return false;
}
- parseInclude();
+ enqueueInclude(optionStart);
return true;
}
@@ -2335,4 +2348,17 @@
this.negated = negated;
}
}
+
+ static class IncludeWorkItem {
+
+ final Path includePath;
+ final TextPosition includePositionStart;
+ final int includePositionEnd;
+
+ IncludeWorkItem(Path includePath, TextPosition includePositionStart, int includePositionEnd) {
+ this.includePath = includePath;
+ this.includePositionStart = includePositionStart;
+ this.includePositionEnd = includePositionEnd;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
index e3771ec..ab2dc15 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
@@ -12,7 +12,7 @@
public interface ProguardConfigurationParserConsumer {
- void addParsedConfiguration(String s);
+ void addParsedConfiguration(ProguardConfigurationSourceParser parser);
void addRule(ProguardConfigurationRule rule);
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index cb788dc..e50d5e6 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -3023,4 +3023,47 @@
assertEquals(MaximumRemovedAndroidLogLevelRule.VERBOSE, rule.getMaxRemovedAndroidLogLevel());
}
}
+
+ @Test
+ public void testParsedConfigurationWithInclude() throws Exception {
+ Path config = temp.newFile("config.txt").toPath().toAbsolutePath();
+ Path include1 = temp.newFile("include1.txt").toPath().toAbsolutePath();
+ Path include11 = temp.newFile("include1_1.txt").toPath().toAbsolutePath();
+ Path include2 = temp.newFile("include2.txt").toPath().toAbsolutePath();
+ FileUtils.writeTextFile(
+ config,
+ StringUtils.joinLines(
+ "# Before Include 1",
+ "-include " + include1,
+ "# After Include 1",
+ "-include " + include2,
+ "# After Include 2"));
+ FileUtils.writeTextFile(
+ include1, StringUtils.joinLines("# Include 1", "-include " + include11));
+ FileUtils.writeTextFile(include11, "# Include 1.1");
+ FileUtils.writeTextFile(include2, "# Include 2");
+ parser.parse(config);
+ verifyParserEndsCleanly();
+ String parsedConfiguration = builder.build().getParsedConfiguration();
+ assertEquals(
+ StringUtils.lines(
+ "# The proguard configuration file for the following section is config.txt",
+ "# Before Include 1",
+ "", // -include include1.txt
+ "# After Include 1",
+ "", // -include include2.txt
+ "# After Include 2",
+ "# End of content from config.txt",
+ "# The proguard configuration file for the following section is include1.txt",
+ "# Include 1",
+ "", // -include include1_1.txt.
+ "# End of content from include1.txt",
+ "# The proguard configuration file for the following section is include1_1.txt",
+ "# Include 1.1",
+ "# End of content from include1_1.txt",
+ "# The proguard configuration file for the following section is include2.txt",
+ "# Include 2",
+ "# End of content from include2.txt"),
+ StringUtils.replaceAll(parsedConfiguration, temp.getRoot().toString() + "/", ""));
+ }
}
\ No newline at end of file