| // 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; | |
| import com.android.tools.r8.graph.DexItemFactory; | |
| import com.android.tools.r8.keepanno.annotations.KeepForApi; | |
| import com.android.tools.r8.origin.CommandLineOrigin; | |
| import com.android.tools.r8.utils.AndroidApp; | |
| import com.android.tools.r8.utils.CliParserUtils; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.Reporter; | |
| import com.android.tools.r8.utils.StringDiagnostic; | |
| import com.android.tools.r8.utils.internal.CliParser; | |
| import com.android.tools.r8.utils.internal.SemanticVersion; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| @KeepForApi | |
| /* Experimental API to extract embedded rules from libraries. */ | |
| public class ExtractR8RulesCommand extends BaseCommand { | |
| private final StringConsumer rulesConsumer; | |
| private final boolean includeOriginComments; | |
| private final SemanticVersion compilerVersion; | |
| private final DexItemFactory factory; | |
| private final Reporter reporter; | |
| @KeepForApi | |
| public static class Builder extends BaseCommand.Builder<ExtractR8RulesCommand, Builder> { | |
| private final DexItemFactory factory = new DexItemFactory(); | |
| private StringConsumer rulesConsumer = null; | |
| private boolean includeOriginComments = false; | |
| private SemanticVersion compilerVersion = null; | |
| private Builder() {} | |
| private Builder(DiagnosticsHandler diagnosticsHandler) { | |
| super(diagnosticsHandler); | |
| } | |
| @Override | |
| ExtractR8RulesCommand.Builder self() { | |
| return this; | |
| } | |
| /** TBD */ | |
| public ExtractR8RulesCommand.Builder setRulesOutputPath(Path rulesOutputPath) { | |
| rulesConsumer = new StringConsumer.FileConsumer(rulesOutputPath); | |
| return self(); | |
| } | |
| /** TBD */ | |
| public ExtractR8RulesCommand.Builder setRulesConsumer(StringConsumer rulesConsumer) { | |
| this.rulesConsumer = rulesConsumer; | |
| return self(); | |
| } | |
| /** TBD */ | |
| public ExtractR8RulesCommand.Builder setIncludeOriginComments(boolean include) { | |
| this.includeOriginComments = include; | |
| return self(); | |
| } | |
| /** TBD */ | |
| public Builder setCompilerVersion(SemanticVersion version) { | |
| compilerVersion = version; | |
| return self(); | |
| } | |
| @Override | |
| protected ExtractR8RulesCommand makeCommand() { | |
| // If printing versions ignore everything else. | |
| if (isPrintHelp() || isPrintVersion()) { | |
| return new ExtractR8RulesCommand(isPrintHelp(), isPrintVersion()); | |
| } | |
| return new ExtractR8RulesCommand( | |
| factory, | |
| getAppBuilder().build(), | |
| rulesConsumer, | |
| includeOriginComments, | |
| compilerVersion, | |
| getReporter()); | |
| } | |
| } | |
| static String usageMessage() { | |
| return CliParserUtils.getUsageMessage(createParser()); | |
| } | |
| public static ExtractR8RulesCommand.Builder builder() { | |
| return new ExtractR8RulesCommand.Builder(); | |
| } | |
| public static ExtractR8RulesCommand.Builder builder(DiagnosticsHandler diagnosticsHandler) { | |
| return new ExtractR8RulesCommand.Builder(diagnosticsHandler); | |
| } | |
| public static ExtractR8RulesCommand.Builder parse(String[] args) { | |
| var builder = builder(); | |
| createParser() | |
| .parse( | |
| args, | |
| builder, | |
| err -> | |
| builder | |
| .getReporter() | |
| .fatalError(new StringDiagnostic(err, CommandLineOrigin.INSTANCE))); | |
| return builder; | |
| } | |
| public StringConsumer getRulesConsumer() { | |
| return rulesConsumer; | |
| } | |
| public boolean getIncludeOriginComments() { | |
| return includeOriginComments; | |
| } | |
| public SemanticVersion getCompilerVersion() { | |
| return compilerVersion; | |
| } | |
| Reporter getReporter() { | |
| return reporter; | |
| } | |
| private static CliParser<ExtractR8RulesCommand.Builder> createParser() { | |
| var header = "Usage: TBD"; | |
| var parser = new CliParser<ExtractR8RulesCommand.Builder>(header); | |
| return parser | |
| .option1( | |
| "--rules-output", | |
| "<file>", | |
| "Output the extracted keep rules.", | |
| (b, arg) -> b.setRulesOutputPath(Paths.get(arg))) | |
| .option1( | |
| "--compiler-version", | |
| "<version>", | |
| "Output the proguard rules extracted.", | |
| (b, arg) -> b.setCompilerVersion(SemanticVersion.parse(arg))) | |
| .option0( | |
| "--include-origin-comments", | |
| "Include comments with origin for extracted rules.", | |
| b -> b.setIncludeOriginComments(true)) | |
| .apply(CliParserUtils.addVersionOption(b -> b.setPrintVersion(true))) | |
| .apply(CliParserUtils.addHelpOption(b -> b.setPrintHelp(true))) | |
| .positional((b, arg) -> b.addProgramFiles(Paths.get(arg))); | |
| } | |
| private ExtractR8RulesCommand( | |
| DexItemFactory factory, | |
| AndroidApp inputApp, | |
| StringConsumer rulesConsumer, | |
| boolean includeOriginComments, | |
| SemanticVersion compilerVersion, | |
| Reporter reporter) { | |
| super(inputApp); | |
| this.factory = factory; | |
| this.rulesConsumer = rulesConsumer; | |
| this.includeOriginComments = includeOriginComments; | |
| this.compilerVersion = compilerVersion; | |
| this.reporter = reporter; | |
| } | |
| private ExtractR8RulesCommand(boolean printHelp, boolean printVersion) { | |
| super(printHelp, printVersion); | |
| this.factory = new DexItemFactory(); | |
| this.rulesConsumer = null; | |
| this.includeOriginComments = false; | |
| this.compilerVersion = null; | |
| this.reporter = new Reporter(); | |
| } | |
| @Override | |
| InternalOptions getInternalOptions() { | |
| InternalOptions internal = new InternalOptions(factory, reporter); | |
| internal.programConsumer = DexIndexedConsumer.emptyConsumer(); | |
| assert internal.retainCompileTimeAnnotations; | |
| internal.retainCompileTimeAnnotations = false; | |
| return internal; | |
| } | |
| } |