Add keep rule validation API
Bug: b/437139566
Change-Id: I5466949148999446cf1552ab4f33b2ecbf2e3055
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 49672a7..fa8a50c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -919,13 +919,16 @@
private ProguardConfiguration makeConfiguration(DexItemFactory factory) {
ProguardConfigurationParserOptions parserOptions =
parserOptionsBuilder.setForceProguardCompatibility(forceProguardCompatibility).build();
+ ProguardConfiguration.Builder configurationBuilder =
+ ProguardConfiguration.builder(factory, getReporter())
+ .setForceProguardCompatibility(forceProguardCompatibility);
ProguardConfigurationParser parser =
new ProguardConfigurationParser(
- factory, getReporter(), parserOptions, inputDependencyGraphConsumer);
- ProguardConfiguration.Builder configurationBuilder =
- parser
- .getConfigurationBuilder()
- .setForceProguardCompatibility(forceProguardCompatibility);
+ factory,
+ getReporter(),
+ parserOptions,
+ inputDependencyGraphConsumer,
+ configurationBuilder);
if (!proguardConfigs.isEmpty()) {
parser.parse(proguardConfigs);
}
@@ -948,7 +951,7 @@
}
// Add embedded keep rules.
- amendWithRulesAndProvidersForInjarsAndMetaInf(getReporter(), parser);
+ amendWithRulesAndProvidersForInjarsAndMetaInf(getReporter(), parser, configurationBuilder);
// Extract out rules for keep annotations and amend the configuration.
// TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer.
@@ -959,7 +962,9 @@
}
private void amendWithRulesAndProvidersForInjarsAndMetaInf(
- Reporter reporter, ProguardConfigurationParser parser) {
+ Reporter reporter,
+ ProguardConfigurationParser parser,
+ ProguardConfiguration.Builder configurationBuilder) {
Supplier<SemanticVersion> semanticVersionSupplier =
SemanticVersionUtils.compilerVersionSemanticVersionSupplier(
@@ -978,7 +983,7 @@
.map(ProgramResourceProvider::getDataResourceProvider)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
- for (FilteredClassPath injar : parser.getConfigurationBuilder().getInjars()) {
+ for (FilteredClassPath injar : configurationBuilder.getInjars()) {
if (seen.add(injar)) {
ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar);
if (provider != null) {
@@ -999,8 +1004,7 @@
.map(ClassFileResourceProvider::getDataResourceProvider)
.filter(Objects::nonNull)
.forEach(providers::add);
- for (FilteredClassPath libraryjar :
- parser.getConfigurationBuilder().build().getLibraryjars()) {
+ for (FilteredClassPath libraryjar : configurationBuilder.build().getLibraryjars()) {
if (seen.add(libraryjar)) {
ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(libraryjar);
if (provider != null) {
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java b/src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java
new file mode 100644
index 0000000..3921207
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@KeepForApi
+public class GlobalLibraryConsumerRuleDiagnostic implements Diagnostic {
+ private final Origin origin;
+ private final Position position;
+ private final String rule;
+
+ public GlobalLibraryConsumerRuleDiagnostic(Origin origin, Position position, String rule) {
+ this.origin = origin;
+ this.position = position;
+ this.rule = rule;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return rule + " not allowed in library consumer rules.";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java b/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java
new file mode 100644
index 0000000..c7c1c1a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@KeepForApi
+public class KeepAttributeLibraryConsumerRuleDiagnostic implements Diagnostic {
+
+ private final Origin origin;
+ private final Position position;
+ private final String attribute;
+
+ public KeepAttributeLibraryConsumerRuleDiagnostic(
+ Origin origin, Position position, String attribute) {
+ this.origin = origin;
+ this.position = position;
+ this.attribute = attribute;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Illegal attempt to keep the attribute '" + attribute + "' in library consumer rules.";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRules.java b/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRules.java
new file mode 100644
index 0000000..158894d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRules.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationParserConsumer;
+import com.android.tools.r8.utils.ExceptionUtils;
+
+@KeepForApi
+public class ProcessKeepRules {
+ public static void run(ProcessKeepRulesCommand command) throws CompilationFailedException {
+ ExceptionUtils.withCompilationHandler(
+ command.getReporter(),
+ () -> {
+ // This is the only valid form of keep rule processing for now.
+ assert command.getValidateLibraryConsumerRules();
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProguardConfigurationParserConsumer configurationConsumer =
+ new ValidateLibraryConsumerRulesKeepRuleProcessor(command.getReporter());
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(
+ dexItemFactory, command.getReporter(), configurationConsumer);
+ parser.parse(command.getKeepRules());
+ command.getReporter().failIfPendingErrors();
+ });
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommand.java b/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommand.java
new file mode 100644
index 0000000..81f18f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommand.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.shaking.ProguardConfigurationSource;
+import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@KeepForApi
+public class ProcessKeepRulesCommand {
+ private final Reporter reporter;
+ private final List<ProguardConfigurationSource> keepRules;
+ private final boolean validateLibraryConsumerRules;
+
+ private ProcessKeepRulesCommand(
+ Reporter reporter,
+ List<ProguardConfigurationSource> keepRules,
+ boolean validateLibraryConsumerRules) {
+ this.reporter = reporter;
+ this.keepRules = keepRules;
+ this.validateLibraryConsumerRules = validateLibraryConsumerRules;
+ }
+
+ /**
+ * Utility method for obtaining a <code>ProcessKeepRules.Builder</code> with a default diagnostics
+ * handler.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Utility method for obtaining a <code>ProcessKeepRules.Builder</code>.
+ *
+ * @param diagnosticsHandler The diagnostics handler for consuming messages.
+ */
+ public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+ return new Builder(diagnosticsHandler);
+ }
+
+ Reporter getReporter() {
+ return reporter;
+ }
+
+ boolean getValidateLibraryConsumerRules() {
+ return validateLibraryConsumerRules;
+ }
+
+ List<ProguardConfigurationSource> getKeepRules() {
+ return keepRules;
+ }
+
+ @KeepForApi
+ public static class Builder {
+
+ private final Reporter reporter;
+ private List<ProguardConfigurationSource> keepRuleFiles = new ArrayList<>();
+ private boolean validateLibraryConsumerRules = false;
+
+ // TODO(b/447161121) introduce a DefaultDiagnosticHandler instead.
+ private Builder() {
+ this(new DiagnosticsHandler() {});
+ }
+
+ private Builder(DiagnosticsHandler diagnosticsHandler) {
+ this.reporter = new Reporter(diagnosticsHandler);
+ }
+
+ /** Add proguard configuration-file resources. */
+ public Builder addKeepRuleFiles(Collection<Path> paths) {
+ for (Path path : paths) {
+ keepRuleFiles.add(new ProguardConfigurationSourceFile(path));
+ }
+ return this;
+ }
+
+ public Builder setLibraryConsumerRuleValidation(boolean enable) {
+ validateLibraryConsumerRules = enable;
+ return this;
+ }
+
+ public ProcessKeepRulesCommand build() {
+ validate();
+ return new ProcessKeepRulesCommand(reporter, keepRuleFiles, validateLibraryConsumerRules);
+ }
+
+ private void validate() {
+ if (keepRuleFiles.isEmpty()) {
+ reporter.error("No keep rule files provided.");
+ }
+ if (!validateLibraryConsumerRules) {
+ reporter.error("No rule validation enabled.");
+ }
+ reporter.failIfPendingErrors();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
new file mode 100644
index 0000000..371e1cb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.shaking.ProguardClassNameList;
+import com.android.tools.r8.shaking.ProguardConfigurationParserConsumer;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.shaking.ProguardPathList;
+import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Path;
+import java.util.List;
+
+class ValidateLibraryConsumerRulesKeepRuleProcessor implements ProguardConfigurationParserConsumer {
+ private final Reporter reporter;
+
+ public ValidateLibraryConsumerRulesKeepRuleProcessor(Reporter reporter) {
+ super();
+ this.reporter = reporter;
+ }
+
+ private void handleGlobalRule(Origin origin, Position position, String rule) {
+ reporter.error(new GlobalLibraryConsumerRuleDiagnostic(origin, position, rule));
+ }
+
+ private void handleKeepAttribute(Origin origin, Position position, String attribute) {
+ reporter.error(new KeepAttributeLibraryConsumerRuleDiagnostic(origin, position, attribute));
+ }
+
+ @Override
+ public void disableOptimization(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-dontoptimize");
+ }
+
+ @Override
+ public void disableObfuscation(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-dontobfuscate");
+ }
+
+ @Override
+ public void disableShrinking(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-dontshrink");
+ }
+
+ @Override
+ public void enableRepackageClasses(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-repackageclasses");
+ }
+
+ @Override
+ public void enableFlattenPackageHierarchy(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-flattenpackagehierarchy");
+ }
+
+ @Override
+ public void enableAllowAccessModification(Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-allowaccessmodification");
+ }
+
+ @Override
+ public void setRenameSourceFileAttribute(String s, Origin origin, Position position) {
+ handleGlobalRule(origin, position, "-renamesourcefileattribute");
+ }
+
+ @Override
+ public void addParsedConfiguration(String s) {}
+
+ @Override
+ public void addRule(ProguardConfigurationRule rule) {}
+
+ @Override
+ public void addKeepAttributePatterns(
+ List<String> attributesPatterns, Origin origin, Position position) {
+ // TODO(b/270289387): Add support for more attributes.
+ ProguardKeepAttributes keepAttributes = ProguardKeepAttributes.fromPatterns(attributesPatterns);
+ if (keepAttributes.lineNumberTable) {
+ handleKeepAttribute(origin, position, ProguardKeepAttributes.LINE_NUMBER_TABLE);
+ }
+ if (keepAttributes.runtimeInvisibleAnnotations) {
+ handleKeepAttribute(origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS);
+ }
+ if (keepAttributes.runtimeInvisibleTypeAnnotations) {
+ handleKeepAttribute(
+ origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+ }
+ if (keepAttributes.runtimeInvisibleParameterAnnotations) {
+ handleKeepAttribute(
+ origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
+ }
+ if (keepAttributes.sourceFile) {
+ handleKeepAttribute(origin, position, ProguardKeepAttributes.SOURCE_FILE);
+ }
+ }
+
+ @Override
+ public void addKeepPackageNamesPattern(ProguardClassNameList proguardClassNameList) {}
+
+ @Override
+ public void setKeepParameterNames(boolean b, Origin origin, Position position) {}
+
+ @Override
+ public void enableKeepDirectories() {}
+
+ @Override
+ public void addKeepDirectories(ProguardPathList proguardPathList) {}
+
+ @Override
+ public void setPrintUsage(boolean b) {}
+
+ @Override
+ public void setPrintUsageFile(Path path) {}
+
+ @Override
+ public void enableProtoShrinking() {}
+
+ @Override
+ public void setIgnoreWarnings(boolean b) {}
+
+ @Override
+ public void addDontWarnPattern(ProguardClassNameList pattern) {}
+
+ @Override
+ public void addDontNotePattern(ProguardClassNameList pattern) {}
+
+ @Override
+ public void setPrintConfiguration(boolean b) {}
+
+ @Override
+ public void setPrintConfigurationFile(Path path) {}
+
+ @Override
+ public void setPrintMapping(boolean b) {}
+
+ @Override
+ public void setPrintMappingFile(Path path) {}
+
+ @Override
+ public void setApplyMappingFile(Path path) {}
+
+ @Override
+ public void addInjars(List<FilteredClassPath> filteredClassPaths) {}
+
+ @Override
+ public void addLibraryJars(List<FilteredClassPath> filteredClassPaths) {}
+
+ @Override
+ public void setPrintSeeds(boolean b) {}
+
+ @Override
+ public void setSeedFile(Path path) {}
+
+ @Override
+ public void setObfuscationDictionary(Path path) {}
+
+ @Override
+ public void setClassObfuscationDictionary(Path path) {}
+
+ @Override
+ public void setPackageObfuscationDictionary(Path path) {}
+
+ @Override
+ public void addAdaptClassStringsPattern(ProguardClassNameList pattern) {}
+
+ @Override
+ public void addAdaptResourceFileContents(ProguardPathList pattern) {}
+
+ @Override
+ public void addAdaptResourceFilenames(ProguardPathList pattern) {}
+
+ @Override
+ public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {}
+
+ @Override
+ public PackageObfuscationMode getPackageObfuscationMode() {
+ return null;
+ }
+
+ @Override
+ public void setPackagePrefix(String s) {}
+
+ @Override
+ public void setFlattenPackagePrefix(String s) {}
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
index 312bead..a4c6d4b 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
@@ -152,7 +152,8 @@
.disableObfuscation()
.disableOptimization()
.addKeepAttributePatterns(ImmutableList.of("*"))
- .addAdaptResourceFilenames(ProguardPathList.builder().addFileName("**").build())
+ .applyAdaptResourceFilenamesBuilder(
+ b -> b.addPattern(ProguardPathList.builder().addFileName("**").build()))
.build(),
getReporter());
options.relocatorCompilation = true;
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 6e09df6..2d4e353 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -18,10 +18,11 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
public class ProguardConfiguration {
- public static class Builder {
+ public static class Builder implements ProguardConfigurationParserConsumer {
private final List<String> parsedConfiguration = new ArrayList<>();
private final List<FilteredClassPath> injars = new ArrayList<>();
@@ -29,8 +30,6 @@
private final List<FilteredClassPath> libraryJars = new ArrayList<>();
private final Reporter reporter;
- private PackageObfuscationMode packageObfuscationMode = PackageObfuscationMode.NONE;
- private String packagePrefix = "";
private boolean allowAccessModification;
private boolean ignoreWarnings;
private boolean optimizing = true;
@@ -71,6 +70,8 @@
private boolean forceProguardCompatibility = false;
private boolean protoShrinking = false;
private int maxRemovedAndroidLogLevel = MaximumRemovedAndroidLogLevelRule.NOT_SET;
+ PackageObfuscationMode packageObfuscationMode = PackageObfuscationMode.NONE;
+ String packagePrefix = "";
private Builder(DexItemFactory dexItemFactory, Reporter reporter) {
this.dexItemFactory = dexItemFactory;
@@ -81,40 +82,46 @@
return injars;
}
+ @Override
public void addParsedConfiguration(String source) {
parsedConfiguration.add(source);
}
+ @Override
public void addInjars(List<FilteredClassPath> injars) {
this.injars.addAll(injars);
}
+ @Override
public void addLibraryJars(List<FilteredClassPath> libraryJars) {
this.libraryJars.addAll(libraryJars);
}
- public PackageObfuscationMode getPackageObfuscationMode() {
- return packageObfuscationMode;
+ @Override
+ public void enableAllowAccessModification(Origin origin, Position position) {
+ this.allowAccessModification = true;
}
- public void setPackagePrefix(String packagePrefix) {
- packageObfuscationMode = PackageObfuscationMode.REPACKAGE;
- this.packagePrefix = packagePrefix;
- }
-
- public void setFlattenPackagePrefix(String packagePrefix) {
- packageObfuscationMode = PackageObfuscationMode.FLATTEN;
- this.packagePrefix = packagePrefix;
- }
-
- public void setAllowAccessModification(boolean allowAccessModification) {
- this.allowAccessModification = allowAccessModification;
- }
-
+ @Override
public void setIgnoreWarnings(boolean ignoreWarnings) {
this.ignoreWarnings = ignoreWarnings;
}
+ @Override
+ public void disableOptimization(Origin origin, Position position) {
+ this.optimizing = false;
+ }
+
+ @Override
+ public void disableObfuscation(Origin origin, Position position) {
+ this.obfuscating = false;
+ }
+
+ @Override
+ public void disableShrinking(Origin origin, Position position) {
+ this.shrinking = false;
+ }
+
public Builder disableOptimization() {
this.optimizing = false;
return this;
@@ -146,32 +153,39 @@
return this;
}
+ @Override
public void setPrintConfiguration(boolean printConfiguration) {
this.printConfiguration = printConfiguration;
}
+ @Override
public void setPrintConfigurationFile(Path file) {
assert printConfiguration;
this.printConfigurationFile = file;
}
+ @Override
public void setPrintUsage(boolean printUsage) {
this.printUsage = printUsage;
}
+ @Override
public void setPrintUsageFile(Path printUsageFile) {
this.printUsageFile = printUsageFile;
}
+ @Override
public void setPrintMapping(boolean printMapping) {
this.printMapping = printMapping;
}
+ @Override
public void setPrintMappingFile(Path file) {
assert printMapping;
this.printMappingFile = file;
}
+ @Override
public void setApplyMappingFile(Path file) {
this.applyMappingFile = file;
}
@@ -180,53 +194,113 @@
return applyMappingFile != null;
}
- public void setRenameSourceFileAttribute(String renameSourceFileAttribute) {
+ @Override
+ public void setRenameSourceFileAttribute(
+ String renameSourceFileAttribute, Origin origin, Position position) {
this.renameSourceFileAttribute = renameSourceFileAttribute;
}
+ @Override
+ public void addKeepAttributePatterns(
+ List<String> keepAttributePatterns, Origin origin, Position position) {
+ this.keepAttributePatterns.addAll(keepAttributePatterns);
+ }
+
public Builder addKeepAttributePatterns(List<String> keepAttributePatterns) {
this.keepAttributePatterns.addAll(keepAttributePatterns);
return this;
}
+ @Override
public void addRule(ProguardConfigurationRule rule) {
this.rules.add(rule);
}
+ @Override
public void addKeepPackageNamesPattern(ProguardClassNameList pattern) {
keepPackageNamesPatterns.addPattern(pattern);
}
+ @Override
+ public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {
+ assert maxRemovedAndroidLogLevel >= MaximumRemovedAndroidLogLevelRule.NONE;
+ if (this.maxRemovedAndroidLogLevel == MaximumRemovedAndroidLogLevelRule.NOT_SET) {
+ this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
+ } else {
+ // If there are multiple -maximumremovedandroidloglevel rules we only allow removing logging
+ // calls that are removable according to all rules.
+ this.maxRemovedAndroidLogLevel =
+ Math.min(this.maxRemovedAndroidLogLevel, maxRemovedAndroidLogLevel);
+ }
+ }
+
+ public int getMaxRemovedAndroidLogLevel() {
+ return maxRemovedAndroidLogLevel;
+ }
+
+ @Override
+ public PackageObfuscationMode getPackageObfuscationMode() {
+ return packageObfuscationMode;
+ }
+
+ @Override
+ public void setPackagePrefix(String packagePrefix) {
+ this.packagePrefix = packagePrefix;
+ }
+
+ @Override
+ public void setFlattenPackagePrefix(String packagePrefix) {
+ this.packagePrefix = packagePrefix;
+ }
+
+ @Override
+ public void enableFlattenPackageHierarchy(Origin origin, Position position) {
+ packageObfuscationMode = PackageObfuscationMode.FLATTEN;
+ }
+
+ @Override
+ public void enableRepackageClasses(Origin origin, Position position) {
+ packageObfuscationMode = PackageObfuscationMode.REPACKAGE;
+ }
+
+ @Override
public void addDontWarnPattern(ProguardClassNameList pattern) {
dontWarnPatterns.addPattern(pattern);
}
+ @Override
public void addDontNotePattern(ProguardClassNameList pattern) {
dontNotePatterns.addPattern(pattern);
}
+ @Override
public void setSeedFile(Path seedFile) {
this.seedFile = seedFile;
}
+ @Override
public void setPrintSeeds(boolean printSeeds) {
this.printSeeds = printSeeds;
}
+ @Override
public void setObfuscationDictionary(Path obfuscationDictionary) {
this.obfuscationDictionary = obfuscationDictionary;
}
+ @Override
public void setClassObfuscationDictionary(Path classObfuscationDictionary) {
this.classObfuscationDictionary = classObfuscationDictionary;
}
+ @Override
public void setPackageObfuscationDictionary(Path packageObfuscationDictionary) {
this.packageObfuscationDictionary = packageObfuscationDictionary;
}
- public void setKeepParameterNames(boolean keepParameterNames, Origin optionOrigin,
- Position optionPosition) {
+ @Override
+ public void setKeepParameterNames(
+ boolean keepParameterNames, Origin optionOrigin, Position optionPosition) {
assert optionOrigin != null || !keepParameterNames;
this.keepParameterNames = keepParameterNames;
this.keepParameterNamesOptionOrigin = optionOrigin;
@@ -245,23 +319,33 @@
return keepParameterNamesOptionPosition;
}
+ @Override
public void addAdaptClassStringsPattern(ProguardClassNameList pattern) {
adaptClassStrings.addPattern(pattern);
}
- public Builder addAdaptResourceFilenames(ProguardPathList pattern) {
+ @Override
+ public void addAdaptResourceFilenames(ProguardPathList pattern) {
adaptResourceFilenames.addPattern(pattern);
+ }
+
+ public Builder applyAdaptResourceFilenamesBuilder(
+ Consumer<ProguardPathFilter.Builder> consumer) {
+ consumer.accept(adaptResourceFilenames);
return this;
}
+ @Override
public void addAdaptResourceFileContents(ProguardPathList pattern) {
adaptResourceFileContents.addPattern(pattern);
}
+ @Override
public void enableKeepDirectories() {
keepDirectories.enable();
}
+ @Override
public void addKeepDirectories(ProguardPathList pattern) {
keepDirectories.addPattern(pattern);
}
@@ -275,26 +359,11 @@
return this;
}
+ @Override
public void enableProtoShrinking() {
protoShrinking = true;
}
- public int getMaxRemovedAndroidLogLevel() {
- return maxRemovedAndroidLogLevel;
- }
-
- public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {
- assert maxRemovedAndroidLogLevel >= MaximumRemovedAndroidLogLevelRule.NONE;
- if (this.maxRemovedAndroidLogLevel == MaximumRemovedAndroidLogLevelRule.NOT_SET) {
- this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
- } else {
- // If there are multiple -maximumremovedandroidloglevel rules we only allow removing logging
- // calls that are removable according to all rules.
- this.maxRemovedAndroidLogLevel =
- Math.min(this.maxRemovedAndroidLogLevel, maxRemovedAndroidLogLevel);
- }
- }
-
public ProguardConfiguration buildRaw() {
ProguardKeepAttributes proguardKeepAttributes =
ProguardKeepAttributes.fromPatterns(keepAttributePatterns);
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 33f0bd5..0f505fd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -49,7 +49,7 @@
public class ProguardConfigurationParser {
- private final ProguardConfiguration.Builder configurationBuilder;
+ private final ProguardConfigurationParserConsumer configurationConsumer;
private final DexItemFactory dexItemFactory;
private final ProguardConfigurationParserOptions options;
@@ -129,7 +129,7 @@
public ProguardConfigurationParser(
DexItemFactory dexItemFactory,
Reporter reporter,
- ProguardConfiguration.Builder configurationBuilder) {
+ ProguardConfigurationParserConsumer configurationConsumer) {
this(
dexItemFactory,
reporter,
@@ -141,7 +141,7 @@
.setEnableTestingOptions(false)
.build(),
null,
- configurationBuilder);
+ configurationConsumer);
}
public ProguardConfigurationParser(
@@ -169,8 +169,8 @@
Reporter reporter,
ProguardConfigurationParserOptions options,
InputDependencyGraphConsumer inputDependencyConsumer,
- ProguardConfiguration.Builder configurationBuilder) {
- this.configurationBuilder = configurationBuilder;
+ ProguardConfigurationParserConsumer configurationConsumer) {
+ this.configurationConsumer = configurationConsumer;
this.dexItemFactory = dexItemFactory;
this.options = options;
this.reporter = reporter;
@@ -194,10 +194,6 @@
};
}
- public ProguardConfiguration.Builder getConfigurationBuilder() {
- return configurationBuilder;
- }
-
public void parse(Path path) {
parse(ImmutableList.of(new ProguardConfigurationSourceFile(path)));
}
@@ -250,12 +246,12 @@
} while (parseOption());
// This may be unknown, but we want to always ensure that we don't attribute lines to the
// wrong configuration.
- configurationBuilder.addParsedConfiguration(
+ configurationConsumer.addParsedConfiguration(
"# The proguard configuration file for the following section is " + origin.toString());
// Collect the parsed configuration.
- configurationBuilder.addParsedConfiguration(contents.substring(positionAfterInclude));
- configurationBuilder.addParsedConfiguration("# End of content from " + origin);
+ configurationConsumer.addParsedConfiguration(contents.substring(positionAfterInclude));
+ configurationConsumer.addParsedConfiguration("# End of content from " + origin);
reporter.failIfPendingErrors();
}
@@ -286,44 +282,47 @@
// is not present.
keepKotlinMetadata.markAsUsed();
keepKotlinJvmNameAnnotation.markAsUsed();
- configurationBuilder.addRule(keepKotlinMetadata);
- configurationBuilder.addRule(keepKotlinJvmNameAnnotation);
- configurationBuilder.addKeepAttributePatterns(
- Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS));
- configurationBuilder.addKeepAttributePatterns(
- Collections.singletonList(RUNTIME_INVISIBLE_ANNOTATIONS));
+ configurationConsumer.addRule(keepKotlinMetadata);
+ configurationConsumer.addRule(keepKotlinJvmNameAnnotation);
+ configurationConsumer.addKeepAttributePatterns(
+ Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS),
+ origin,
+ getPosition(optionStart));
+ configurationConsumer.addKeepAttributePatterns(
+ Collections.singletonList(RUNTIME_INVISIBLE_ANNOTATIONS),
+ origin,
+ getPosition(optionStart));
} else if (acceptString("renamesourcefileattribute")) {
skipWhitespace();
- if (isOptionalArgumentGiven()) {
- configurationBuilder.setRenameSourceFileAttribute(acceptQuotedOrUnquotedString());
- } else {
- configurationBuilder.setRenameSourceFileAttribute("");
- }
+ String renameSourceFileAttribute =
+ isOptionalArgumentGiven() ? acceptQuotedOrUnquotedString() : "";
+ configurationConsumer.setRenameSourceFileAttribute(
+ renameSourceFileAttribute, origin, getPosition(optionStart));
} else if (acceptString("keepattributes")) {
- parseKeepAttributes();
+ parseKeepAttributes(getPosition(optionStart));
} else if (acceptString("keeppackagenames")) {
- parseClassFilter(configurationBuilder::addKeepPackageNamesPattern);
+ parseClassFilter(configurationConsumer::addKeepPackageNamesPattern);
} else if (acceptString("keepparameternames")) {
- configurationBuilder.setKeepParameterNames(true, origin, getPosition(optionStart));
+ configurationConsumer.setKeepParameterNames(true, origin, getPosition(optionStart));
} else if (acceptString("checkdiscard")) {
ProguardCheckDiscardRule rule =
parseRuleWithClassSpec(optionStart, ProguardCheckDiscardRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("checkenumstringsdiscarded")) {
// Not supported, ignore.
parseRuleWithClassSpec(optionStart, ProguardCheckDiscardRule.builder());
} else if (acceptString("keepdirectories")) {
- configurationBuilder.enableKeepDirectories();
- parsePathFilter(configurationBuilder::addKeepDirectories);
+ configurationConsumer.enableKeepDirectories();
+ parsePathFilter(configurationConsumer::addKeepDirectories);
} else if (acceptString("keep")) {
ProguardKeepRule rule = parseKeepRule(optionStart);
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("whyareyoukeeping")) {
ProguardWhyAreYouKeepingRule rule =
parseRuleWithClassSpec(optionStart, ProguardWhyAreYouKeepingRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("dontoptimize")) {
- configurationBuilder.disableOptimization();
+ configurationConsumer.disableOptimization(origin, getPosition(optionStart));
} else if (acceptString("optimizationpasses")) {
skipWhitespace();
Integer expectedOptimizationPasses = acceptInteger();
@@ -333,41 +332,42 @@
}
infoIgnoringOptions("optimizationpasses", optionStart);
} else if (acceptString("dontobfuscate")) {
- configurationBuilder.disableObfuscation();
+ configurationConsumer.disableObfuscation(origin, getPosition(optionStart));
} else if (acceptString("dontshrink")) {
- configurationBuilder.disableShrinking();
+ configurationConsumer.disableShrinking(origin, getPosition(optionStart));
} else if (acceptString("printusage")) {
- configurationBuilder.setPrintUsage(true);
+ configurationConsumer.setPrintUsage(true);
skipWhitespace();
if (isOptionalArgumentGiven()) {
- configurationBuilder.setPrintUsageFile(parseFileName(false));
+ configurationConsumer.setPrintUsageFile(parseFileName(false));
}
} else if (acceptString("shrinkunusedprotofields")) {
- configurationBuilder.enableProtoShrinking();
+ configurationConsumer.enableProtoShrinking();
} else if (acceptString("ignorewarnings")) {
- configurationBuilder.setIgnoreWarnings(true);
+ configurationConsumer.setIgnoreWarnings(true);
} else if (acceptString("dontwarn")) {
- parseClassFilter(configurationBuilder::addDontWarnPattern);
+ parseClassFilter(configurationConsumer::addDontWarnPattern);
} else if (acceptString("dontnote")) {
- parseClassFilter(configurationBuilder::addDontNotePattern);
+ parseClassFilter(configurationConsumer::addDontNotePattern);
} else if (acceptString(REPACKAGE_CLASSES)) {
- if (configurationBuilder.getPackageObfuscationMode() == PackageObfuscationMode.FLATTEN) {
+ if (configurationConsumer.getPackageObfuscationMode() == PackageObfuscationMode.FLATTEN) {
warnOverridingOptions(REPACKAGE_CLASSES, FLATTEN_PACKAGE_HIERARCHY, optionStart);
}
skipWhitespace();
char quote = acceptQuoteIfPresent();
if (isQuote(quote)) {
- configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
+ configurationConsumer.setPackagePrefix(parsePackageNameOrEmptyString());
expectClosingQuote(quote);
} else {
if (hasNextChar('-')) {
- configurationBuilder.setPackagePrefix("");
+ configurationConsumer.setPackagePrefix("");
} else {
- configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
+ configurationConsumer.setPackagePrefix(parsePackageNameOrEmptyString());
}
}
+ configurationConsumer.enableRepackageClasses(origin, getPosition(optionStart));
} else if (acceptString(FLATTEN_PACKAGE_HIERARCHY)) {
- if (configurationBuilder.getPackageObfuscationMode() == PackageObfuscationMode.REPACKAGE) {
+ if (configurationConsumer.getPackageObfuscationMode() == PackageObfuscationMode.REPACKAGE) {
warnOverridingOptions(REPACKAGE_CLASSES, FLATTEN_PACKAGE_HIERARCHY, optionStart);
skipWhitespace();
if (isOptionalArgumentGiven()) {
@@ -377,42 +377,43 @@
skipWhitespace();
char quote = acceptQuoteIfPresent();
if (isQuote(quote)) {
- configurationBuilder.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
+ configurationConsumer.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
expectClosingQuote(quote);
} else {
if (hasNextChar('-')) {
- configurationBuilder.setFlattenPackagePrefix("");
+ configurationConsumer.setFlattenPackagePrefix("");
} else {
- configurationBuilder.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
+ configurationConsumer.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
}
}
+ configurationConsumer.enableFlattenPackageHierarchy(origin, getPosition(optionStart));
}
} else if (acceptString("allowaccessmodification")) {
- configurationBuilder.setAllowAccessModification(true);
+ configurationConsumer.enableAllowAccessModification(origin, getPosition(optionStart));
} else if (acceptString("printconfiguration")) {
- configurationBuilder.setPrintConfiguration(true);
+ configurationConsumer.setPrintConfiguration(true);
skipWhitespace();
if (isOptionalArgumentGiven()) {
- configurationBuilder.setPrintConfigurationFile(parseFileName(false));
+ configurationConsumer.setPrintConfigurationFile(parseFileName(false));
}
} else if (acceptString("printmapping")) {
- configurationBuilder.setPrintMapping(true);
+ configurationConsumer.setPrintMapping(true);
skipWhitespace();
if (isOptionalArgumentGiven()) {
- configurationBuilder.setPrintMappingFile(parseFileName(false));
+ configurationConsumer.setPrintMappingFile(parseFileName(false));
}
} else if (acceptString("applymapping")) {
- configurationBuilder.setApplyMappingFile(
+ configurationConsumer.setApplyMappingFile(
parseFileInputDependency(inputDependencyConsumer::acceptProguardApplyMapping));
} else if (acceptString("assumenosideeffects")) {
ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule(optionStart);
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("assumevalues")) {
ProguardAssumeValuesRule rule = parseAssumeValuesRule(optionStart);
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("include")) {
// Collect the parsed configuration until the include.
- configurationBuilder.addParsedConfiguration(
+ configurationConsumer.addParsedConfiguration(
contents.substring(positionAfterInclude, position - ("include".length() + 1)));
skipWhitespace();
parseInclude();
@@ -421,44 +422,44 @@
skipWhitespace();
baseDirectory = parseFileName(false);
} else if (acceptString("injars")) {
- configurationBuilder.addInjars(
+ configurationConsumer.addInjars(
parseClassPath(inputDependencyConsumer::acceptProguardInJars));
} else if (acceptString("libraryjars")) {
- configurationBuilder.addLibraryJars(
+ configurationConsumer.addLibraryJars(
parseClassPath(inputDependencyConsumer::acceptProguardLibraryJars));
} else if (acceptString("printseeds")) {
- configurationBuilder.setPrintSeeds(true);
+ configurationConsumer.setPrintSeeds(true);
skipWhitespace();
if (isOptionalArgumentGiven()) {
- configurationBuilder.setSeedFile(parseFileName(false));
+ configurationConsumer.setSeedFile(parseFileName(false));
}
} else if (acceptString("obfuscationdictionary")) {
- configurationBuilder.setObfuscationDictionary(
+ configurationConsumer.setObfuscationDictionary(
parseFileInputDependency(inputDependencyConsumer::acceptProguardObfuscationDictionary));
} else if (acceptString("classobfuscationdictionary")) {
- configurationBuilder.setClassObfuscationDictionary(
+ configurationConsumer.setClassObfuscationDictionary(
parseFileInputDependency(
inputDependencyConsumer::acceptProguardClassObfuscationDictionary));
} else if (acceptString("packageobfuscationdictionary")) {
- configurationBuilder.setPackageObfuscationDictionary(
+ configurationConsumer.setPackageObfuscationDictionary(
parseFileInputDependency(
inputDependencyConsumer::acceptProguardPackageObfuscationDictionary));
} else if (acceptString("alwaysinline")) {
InlineRule rule =
parseRuleWithClassSpec(
optionStart, InlineRule.builder().setType(InlineRuleType.ALWAYS));
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
} else if (acceptString("adaptclassstrings")) {
- parseClassFilter(configurationBuilder::addAdaptClassStringsPattern);
+ parseClassFilter(configurationConsumer::addAdaptClassStringsPattern);
} else if (acceptString("adaptresourcefilenames")) {
- parsePathFilter(configurationBuilder::addAdaptResourceFilenames);
+ parsePathFilter(configurationConsumer::addAdaptResourceFilenames);
} else if (acceptString("adaptresourcefilecontents")) {
- parsePathFilter(configurationBuilder::addAdaptResourceFileContents);
+ parsePathFilter(configurationConsumer::addAdaptResourceFileContents);
} else if (acceptString("identifiernamestring")) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(optionStart, ProguardIdentifierNameStringRule.builder()));
} else if (acceptString("if")) {
- configurationBuilder.addRule(parseIfRule(optionStart));
+ configurationConsumer.addRule(parseIfRule(optionStart));
} else if (parseMaximumRemovedAndroidLogLevelRule(optionStart)) {
return true;
} else {
@@ -479,20 +480,20 @@
if (acceptString(CheckEnumUnboxedRule.RULE_NAME)) {
CheckEnumUnboxedRule checkEnumUnboxedRule = parseCheckEnumUnboxedRule(optionStart);
if (options.isExperimentalCheckEnumUnboxedEnabled()) {
- configurationBuilder.addRule(checkEnumUnboxedRule);
+ configurationConsumer.addRule(checkEnumUnboxedRule);
}
return true;
}
if (acceptString(ConvertCheckNotNullRule.RULE_NAME)) {
ConvertCheckNotNullRule convertCheckNotNullRule = parseConvertCheckNotNullRule(optionStart);
if (options.isExperimentalConvertCheckNotNullEnabled()) {
- configurationBuilder.addRule(convertCheckNotNullRule);
+ configurationConsumer.addRule(convertCheckNotNullRule);
}
return true;
}
if (options.isExperimentalWhyAreYouNotInliningEnabled()) {
if (acceptString(WhyAreYouNotInliningRule.RULE_NAME)) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(optionStart, WhyAreYouNotInliningRule.builder()));
return true;
}
@@ -506,123 +507,123 @@
if (acceptString("assumemayhavesideeffects")) {
ProguardAssumeMayHaveSideEffectsRule rule =
parseAssumeMayHaveSideEffectsRule(optionStart);
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(KeepConstantArgumentRule.RULE_NAME)) {
KeepConstantArgumentRule rule =
parseRuleWithClassSpec(optionStart, KeepConstantArgumentRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(KeepUnusedArgumentRule.RULE_NAME)) {
KeepUnusedArgumentRule rule =
parseRuleWithClassSpec(optionStart, KeepUnusedArgumentRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(KeepUnusedReturnValueRule.RULE_NAME)) {
KeepUnusedReturnValueRule rule =
parseRuleWithClassSpec(optionStart, KeepUnusedReturnValueRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("alwaysclassinline")) {
ClassInlineRule rule =
parseRuleWithClassSpec(
optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.ALWAYS));
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("neverclassinline")) {
ClassInlineRule rule =
parseRuleWithClassSpec(
optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.NEVER));
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("neverinline")) {
InlineRule rule =
parseRuleWithClassSpec(
optionStart, InlineRule.builder().setType(InlineRuleType.NEVER));
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("neversinglecallerinline")) {
InlineRule rule =
parseRuleWithClassSpec(
optionStart, InlineRule.builder().setType(InlineRuleType.NEVER_SINGLE_CALLER));
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoAccessModificationRule.RULE_NAME)) {
NoAccessModificationRule rule =
parseRuleWithClassSpec(optionStart, NoAccessModificationRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoFieldTypeStrengtheningRule.RULE_NAME)) {
NoFieldTypeStrengtheningRule rule =
parseRuleWithClassSpec(optionStart, NoFieldTypeStrengtheningRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) {
NoUnusedInterfaceRemovalRule rule =
parseRuleWithClassSpec(optionStart, NoUnusedInterfaceRemovalRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoVerticalClassMergingRule.RULE_NAME)) {
NoVerticalClassMergingRule rule =
parseRuleWithClassSpec(optionStart, NoVerticalClassMergingRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoHorizontalClassMergingRule.RULE_NAME)) {
NoHorizontalClassMergingRule rule =
parseRuleWithClassSpec(optionStart, NoHorizontalClassMergingRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoMethodStaticizingRule.RULE_NAME)) {
NoMethodStaticizingRule rule =
parseRuleWithClassSpec(optionStart, NoMethodStaticizingRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoParameterReorderingRule.RULE_NAME)) {
NoParameterReorderingRule rule =
parseRuleWithClassSpec(optionStart, NoParameterReorderingRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
NoParameterTypeStrengtheningRule rule =
parseRuleWithClassSpec(optionStart, NoParameterTypeStrengtheningRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoRedundantFieldLoadEliminationRule.RULE_NAME)) {
NoRedundantFieldLoadEliminationRule rule =
parseRuleWithClassSpec(optionStart, NoRedundantFieldLoadEliminationRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) {
NoReturnTypeStrengtheningRule rule =
parseRuleWithClassSpec(optionStart, NoReturnTypeStrengtheningRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("neverpropagatevalue")) {
NoValuePropagationRule rule =
parseRuleWithClassSpec(optionStart, NoValuePropagationRule.builder());
- configurationBuilder.addRule(rule);
+ configurationConsumer.addRule(rule);
return true;
}
if (acceptString("neverreprocessclassinitializer")) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(
optionStart,
ReprocessClassInitializerRule.builder()
@@ -630,14 +631,14 @@
return true;
}
if (acceptString("neverreprocessmethod")) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(
optionStart,
ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.NEVER)));
return true;
}
if (acceptString("reprocessclassinitializer")) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(
optionStart,
ReprocessClassInitializerRule.builder()
@@ -645,7 +646,7 @@
return true;
}
if (acceptString("reprocessmethod")) {
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
parseRuleWithClassSpec(
optionStart,
ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.ALWAYS)));
@@ -734,7 +735,7 @@
return true;
}
- private void parseKeepAttributes() throws ProguardRuleParserException {
+ private void parseKeepAttributes(Position position) throws ProguardRuleParserException {
List<String> attributesPatterns = acceptKeepAttributesPatternList();
if (attributesPatterns.isEmpty()) {
throw parseError("Expected attribute pattern list");
@@ -752,7 +753,7 @@
+ ")"));
}
}
- configurationBuilder.addKeepAttributePatterns(attributesPatterns);
+ configurationConsumer.addKeepAttributePatterns(attributesPatterns, origin, position);
}
private boolean skipFlag(String name) {
@@ -929,10 +930,10 @@
}
if (builder.hasClassType()) {
Position end = getPosition();
- configurationBuilder.addRule(
+ configurationConsumer.addRule(
builder.setEnd(end).setSource(getSourceSnippet(contents, start, end)).build());
} else {
- configurationBuilder.joinMaxRemovedAndroidLogLevel(maxRemovedAndroidLogLevel);
+ configurationConsumer.joinMaxRemovedAndroidLogLevel(maxRemovedAndroidLogLevel);
}
return true;
}
@@ -2307,4 +2308,4 @@
this.negated = negated;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
new file mode 100644
index 0000000..f0fbdd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2025, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import java.nio.file.Path;
+import java.util.List;
+
+public interface ProguardConfigurationParserConsumer {
+
+ void addParsedConfiguration(String s);
+
+ void addRule(ProguardConfigurationRule rule);
+
+ void addKeepAttributePatterns(List<String> attributesPatterns, Origin origin, Position position);
+
+ void setRenameSourceFileAttribute(String s, Origin origin, Position position);
+
+ void addKeepPackageNamesPattern(ProguardClassNameList proguardClassNameList);
+
+ void setKeepParameterNames(boolean b, Origin origin, Position position);
+
+ void enableKeepDirectories();
+
+ void addKeepDirectories(ProguardPathList proguardPathList);
+
+ void disableOptimization(Origin origin, Position position);
+
+ void disableObfuscation(Origin origin, Position position);
+
+ void disableShrinking(Origin origin, Position position);
+
+ void setPrintUsage(boolean b);
+
+ void setPrintUsageFile(Path path);
+
+ void enableProtoShrinking();
+
+ void setIgnoreWarnings(boolean b);
+
+ void addDontWarnPattern(ProguardClassNameList pattern);
+
+ void addDontNotePattern(ProguardClassNameList pattern);
+
+ void enableAllowAccessModification(Origin origin, Position position);
+
+ void setPrintConfiguration(boolean b);
+
+ void setPrintConfigurationFile(Path path);
+
+ void setPrintMapping(boolean b);
+
+ void setPrintMappingFile(Path path);
+
+ void setApplyMappingFile(Path path);
+
+ void addInjars(List<FilteredClassPath> filteredClassPaths);
+
+ void addLibraryJars(List<FilteredClassPath> filteredClassPaths);
+
+ void setPrintSeeds(boolean b);
+
+ void setSeedFile(Path path);
+
+ void setObfuscationDictionary(Path path);
+
+ void setClassObfuscationDictionary(Path path);
+
+ void setPackageObfuscationDictionary(Path path);
+
+ void addAdaptClassStringsPattern(ProguardClassNameList pattern);
+
+ void addAdaptResourceFileContents(ProguardPathList pattern);
+
+ void addAdaptResourceFilenames(ProguardPathList pattern);
+
+ void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel);
+
+ PackageObfuscationMode getPackageObfuscationMode();
+
+ void setPackagePrefix(String s);
+
+ void setFlattenPackagePrefix(String s);
+
+ void enableRepackageClasses(Origin origin, Position position);
+
+ void enableFlattenPackageHierarchy(Origin origin, Position position);
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index 954a409..cf407e6 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -46,6 +46,7 @@
public boolean annotationDefault = false;
public boolean stackMapTable = false;
public boolean permittedSubclasses = false;
+ public boolean lineNumberTable = false;
public ProguardKeepAttributes() {}
@@ -63,6 +64,7 @@
annotationDefault = true;
stackMapTable = true;
permittedSubclasses = true;
+ lineNumberTable = true;
return this;
}
@@ -136,6 +138,7 @@
annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
stackMapTable = update(stackMapTable, STACK_MAP_TABLE, patterns);
permittedSubclasses = update(permittedSubclasses, PERMITTED_SUBCLASSES, patterns);
+ lineNumberTable = update(lineNumberTable, LINE_NUMBER_TABLE, patterns);
}
public void ensureValid(boolean forceProguardCompatibility) {
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
index 522639b..9cd9048 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
@@ -50,13 +50,11 @@
buildInnerClasses(GenericSignatureCorrectnessHelperTests.class)
.addLibraryFile(ToolHelper.getJava8RuntimeJar())
.build(),
- factory -> {
- ProguardConfiguration.Builder builder =
- ProguardConfiguration.builder(
- factory, new Reporter(new TestDiagnosticMessagesImpl()));
- builder.addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE));
- return builder.build();
- });
+ factory ->
+ ProguardConfiguration.builder(
+ factory, new Reporter(new TestDiagnosticMessagesImpl()))
+ .addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE))
+ .build());
GenericSignatureContextBuilder contextBuilder = GenericSignatureContextBuilder.create(appView);
GenericSignatureCorrectnessHelper.createForVerification(appView, contextBuilder)
.run(appView.appInfo().classes());
@@ -195,13 +193,11 @@
.addClassProgramData(transformations)
.addLibraryFile(ToolHelper.getJava8RuntimeJar())
.build(),
- factory -> {
- ProguardConfiguration.Builder builder =
- ProguardConfiguration.builder(
- factory, new Reporter(new TestDiagnosticMessagesImpl()));
- builder.addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE));
- return builder.build();
- });
+ factory ->
+ ProguardConfiguration.builder(
+ factory, new Reporter(new TestDiagnosticMessagesImpl()))
+ .addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE))
+ .build());
GenericSignatureContextBuilder contextBuilder = GenericSignatureContextBuilder.create(appView);
GenericSignatureCorrectnessHelper check =
GenericSignatureCorrectnessHelper.createForInitialCheck(appView, contextBuilder);
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index dd432d3..1067960 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -49,7 +49,7 @@
R8Command.builder(),
pgConfig -> {
pgConfig.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {}));
- pgConfig.setRenameSourceFileAttribute(TEST_FILE);
+ pgConfig.setRenameSourceFileAttribute(TEST_FILE, null, null);
pgConfig.addKeepAttributePatterns(
ImmutableList.of("SourceFile", "LineNumberTable"));
})
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
new file mode 100644
index 0000000..13ec056
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2025, 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.processkeeprules;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.OriginMatcher.hasPart;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class ProcessKeepRulesCommandTest extends TestBase {
+
+ private static final Map<String, String> testRules =
+ ImmutableMap.<String, String>builder()
+ .put("-dontoptimize", "-dontoptimize not allowed in library consumer rules.")
+ .put("-dontobfuscate", "-dontobfuscate not allowed in library consumer rules.")
+ .put("-dontshrink", "-dontshrink not allowed in library consumer rules.")
+ .put("-repackageclasses", "-repackageclasses not allowed in library consumer rules.")
+ .put(
+ "-flattenpackagehierarchy",
+ "-flattenpackagehierarchy not allowed in library consumer rules.")
+ .put(
+ "-allowaccessmodification",
+ "-allowaccessmodification not allowed in library consumer rules.")
+ .put(
+ "-keepattributes LineNumberTable",
+ "Illegal attempt to keep LineNumberTable in library consumer rules.")
+ .put(
+ "-keepattributes RuntimeInvisibleAnnotations",
+ "Illegal attempt to keep RuntimeInvisibleAnnotations in library consumer rules.")
+ .put(
+ "-keepattributes RuntimeInvisibleTypeAnnotations",
+ "Illegal attempt to keep RuntimeInvisibleTypeAnnotations in library consumer rules.")
+ .put(
+ "-keepattributes RuntimeInvisibleParameterAnnotations",
+ "Illegal attempt to keep RuntimeInvisibleParameterAnnotations in library consumer"
+ + " rules.")
+ .put(
+ "-keepattributes SourceFile",
+ "Illegal attempt to keep SourceFile in library consumer rules.")
+ .put(
+ "-renamesourcefileattribute",
+ "-renamesourcefileattribute not allowed in library consumer rules.")
+ .build();
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameter(0)
+ public Map.Entry<String, String> configAndExpectedDiagnostic;
+
+ @Parameterized.Parameters(name = "{1}, configAndExpectedDiagnostic = {0}")
+ public static List<Object[]> data() throws IOException {
+ return buildParameters(testRules.entrySet(), getTestParameters().withNoneRuntime().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+ Path tempFile = getStaticTemp().newFile().toPath();
+ Files.write(tempFile, configAndExpectedDiagnostic.getKey().getBytes(StandardCharsets.UTF_8));
+ ProcessKeepRulesCommand command =
+ ProcessKeepRulesCommand.builder(diagnostics)
+ .addKeepRuleFiles(ImmutableList.of(tempFile))
+ .setLibraryConsumerRuleValidation(true)
+ .build();
+ try {
+ ProcessKeepRules.run(command);
+ fail("Expect the compilation to fail.");
+ } catch (CompilationFailedException e) {
+ diagnostics.assertErrorsMatch(
+ allOf(
+ configAndExpectedDiagnostic.getKey().startsWith("-keepattributes")
+ ? diagnosticType(KeepAttributeLibraryConsumerRuleDiagnostic.class)
+ : diagnosticType(GlobalLibraryConsumerRuleDiagnostic.class),
+ diagnosticOrigin(hasPart(tempFile.toString())),
+ diagnosticMessage(equalTo(configAndExpectedDiagnostic.getValue()))));
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index 6220f5d..c38ef15 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -949,8 +949,9 @@
ProguardConfiguration.Builder builder =
ProguardConfiguration.builder(factory, new Reporter());
builder.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {}));
- builder.addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE));
- return builder.build();
+ return builder
+ .addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE))
+ .build();
};
}
InternalOptions options =