| // Copyright (c) 2020, 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.ir.desugar.DesugaredLibraryConfiguration; |
| import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.Reporter; |
| import com.android.tools.r8.utils.StringDiagnostic; |
| import com.google.common.collect.ImmutableSet; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Immutable command structure for an invocation of the {@link BackportedMethodList} tool. |
| * |
| * <p>To build a BackportedMethodList command use the {@link BackportedMethodListCommand.Builder} |
| * class. For example: |
| * |
| * <pre> |
| * BackportedMethodListCommand command = BackportedMethodListCommand.builder() |
| * .setMinApiLevel(apiLevel) |
| * .setOutputPath(Paths.get("methods-list.txt")) |
| * .build(); |
| * </pre> |
| */ |
| @Keep |
| public class BackportedMethodListCommand { |
| |
| private final boolean printHelp; |
| private final boolean printVersion; |
| private final Reporter reporter; |
| private final int minApiLevel; |
| private final DesugaredLibraryConfiguration desugaredLibraryConfiguration; |
| private final AndroidApp app; |
| private final StringConsumer backportedMethodListConsumer; |
| private final DexItemFactory factory; |
| |
| public boolean isPrintHelp() { |
| return printHelp; |
| } |
| |
| public boolean isPrintVersion() { |
| return printVersion; |
| } |
| |
| Reporter getReporter() { |
| return reporter; |
| } |
| |
| public int getMinApiLevel() { |
| return minApiLevel; |
| } |
| |
| public DesugaredLibraryConfiguration getDesugaredLibraryConfiguration() { |
| return desugaredLibraryConfiguration; |
| } |
| |
| public StringConsumer getBackportedMethodListConsumer() { |
| return backportedMethodListConsumer; |
| } |
| |
| AndroidApp getInputApp() { |
| return app; |
| } |
| |
| private BackportedMethodListCommand(boolean printHelp, boolean printVersion) { |
| this.printHelp = printHelp; |
| this.printVersion = printVersion; |
| this.reporter = new Reporter(); |
| this.minApiLevel = -1; |
| this.desugaredLibraryConfiguration = null; |
| this.app = null; |
| this.backportedMethodListConsumer = null; |
| this.factory = null; |
| } |
| |
| private BackportedMethodListCommand( |
| Reporter reporter, |
| int minApiLevel, |
| DesugaredLibraryConfiguration desugaredLibraryConfiguration, |
| AndroidApp app, |
| StringConsumer backportedMethodListConsumer, |
| DexItemFactory factory) { |
| this.printHelp = false; |
| this.printVersion = false; |
| this.reporter = reporter; |
| this.minApiLevel = minApiLevel; |
| this.desugaredLibraryConfiguration = desugaredLibraryConfiguration; |
| this.app = app; |
| this.backportedMethodListConsumer = backportedMethodListConsumer; |
| this.factory = factory; |
| } |
| |
| InternalOptions getInternalOptions() { |
| InternalOptions options = new InternalOptions(factory, getReporter()); |
| options.minApiLevel = minApiLevel; |
| options.desugaredLibraryConfiguration = desugaredLibraryConfiguration; |
| return options; |
| } |
| |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| public static Builder builder(DiagnosticsHandler diagnosticsHandler) { |
| return new Builder(diagnosticsHandler); |
| } |
| |
| public static Builder parse(String[] args) { |
| final Set<String> OPTIONS_WITH_PARAMETER = |
| ImmutableSet.of("--output", "--min-api", "--desugared-lib", "--lib"); |
| |
| boolean hasDefinedApiLevel = false; |
| Builder builder = builder(); |
| for (int i = 0; i < args.length; i++) { |
| String arg = args[i].trim(); |
| String nextArg = null; |
| if (OPTIONS_WITH_PARAMETER.contains(arg)) { |
| if (++i < args.length) { |
| nextArg = args[i]; |
| } else { |
| builder.error(new StringDiagnostic("Missing parameter for " + args[i - 1] + ".")); |
| break; |
| } |
| } |
| if (arg.equals("--help")) { |
| builder.setPrintHelp(true); |
| } else if (arg.equals("--version")) { |
| builder.setPrintVersion(true); |
| } else if (arg.equals("--min-api")) { |
| if (hasDefinedApiLevel) { |
| builder.error(new StringDiagnostic("Cannot set multiple --min-api options")); |
| } else { |
| parseMinApi(builder, nextArg); |
| hasDefinedApiLevel = true; |
| } |
| } else if (arg.equals("--desugared-lib")) { |
| builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg))); |
| } else if (arg.equals("--lib")) { |
| builder.addLibraryFiles(Paths.get(nextArg)); |
| } else if (arg.equals("--output")) { |
| builder.setOutputPath(Paths.get(nextArg)); |
| } else { |
| builder.error(new StringDiagnostic("Unknown option: " + arg)); |
| } |
| } |
| return builder; |
| } |
| |
| private static void parseMinApi(Builder builder, String minApiString) { |
| int minApi; |
| try { |
| minApi = Integer.parseInt(minApiString); |
| } catch (NumberFormatException e) { |
| builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString)); |
| return; |
| } |
| if (minApi < 1) { |
| builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString)); |
| return; |
| } |
| builder.setMinApiLevel(minApi); |
| } |
| |
| @Keep |
| public static class Builder { |
| |
| private final Reporter reporter; |
| private int minApiLevel = AndroidApiLevel.B.getLevel(); |
| private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>(); |
| private final AndroidApp.Builder app; |
| private StringConsumer backportedMethodListConsumer; |
| private boolean printHelp = false; |
| private boolean printVersion = false; |
| |
| private Builder() { |
| this(new DiagnosticsHandler() {}); |
| } |
| |
| private Builder(DiagnosticsHandler diagnosticsHandler) { |
| this.app = AndroidApp.builder(); |
| this.reporter = new Reporter(diagnosticsHandler); |
| } |
| |
| /** |
| * Set the minimum API level for the application compiled. |
| * |
| * <p>The tool will only report backported methods which are not present at this API level. |
| * |
| * <p>The default is 1 if never set. |
| */ |
| public Builder setMinApiLevel(int minApiLevel) { |
| if (minApiLevel <= 0) { |
| reporter.error(new StringDiagnostic("Invalid minApiLevel: " + minApiLevel)); |
| } else { |
| this.minApiLevel = minApiLevel; |
| } |
| return this; |
| } |
| |
| public int getMinApiLevel() { |
| return minApiLevel; |
| } |
| |
| /** Desugared library configuration */ |
| public Builder addDesugaredLibraryConfiguration(StringResource configuration) { |
| desugaredLibraryConfigurationResources.add(configuration); |
| return this; |
| } |
| |
| /** Desugared library configuration */ |
| public Builder addDesugaredLibraryConfiguration(String configuration) { |
| return addDesugaredLibraryConfiguration( |
| StringResource.fromString(configuration, Origin.unknown())); |
| } |
| |
| /** The compilation SDK library (android.jar) */ |
| public Builder addLibraryResourceProvider(ClassFileResourceProvider provider) { |
| app.addLibraryResourceProvider(provider); |
| return this; |
| } |
| |
| /** The compilation SDK library (android.jar) */ |
| public Builder addLibraryFiles(Path... files) { |
| addLibraryFiles(Arrays.asList(files)); |
| return this; |
| } |
| |
| /** The compilation SDK library (android.jar) */ |
| public Builder addLibraryFiles(Collection<Path> files) { |
| for (Path path : files) { |
| app.addLibraryFile(path); |
| } |
| return this; |
| } |
| |
| DesugaredLibraryConfiguration getDesugaredLibraryConfiguration(DexItemFactory factory) { |
| if (desugaredLibraryConfigurationResources.isEmpty()) { |
| return DesugaredLibraryConfiguration.empty(); |
| } |
| if (desugaredLibraryConfigurationResources.size() > 1) { |
| reporter.fatalError("Only one desugared library configuration is supported."); |
| } |
| StringResource desugaredLibraryConfigurationResource = |
| desugaredLibraryConfigurationResources.get(0); |
| DesugaredLibraryConfigurationParser libraryParser = |
| new DesugaredLibraryConfigurationParser(factory, null, false, getMinApiLevel()); |
| return libraryParser.parse(desugaredLibraryConfigurationResource); |
| } |
| |
| /** Output file for the backported method list */ |
| public Builder setOutputPath(Path outputPath) { |
| backportedMethodListConsumer = |
| new StringConsumer.FileConsumer(outputPath) { |
| @Override |
| public void accept(String string, DiagnosticsHandler handler) { |
| super.accept(string, handler); |
| super.accept(System.lineSeparator(), handler); |
| } |
| }; |
| return this; |
| } |
| |
| /** Consumer receiving the the backported method list */ |
| public Builder setConsumer(StringConsumer consumer) { |
| this.backportedMethodListConsumer = consumer; |
| return this; |
| } |
| |
| /** True if the print-help flag is enabled. */ |
| public boolean isPrintHelp() { |
| return printHelp; |
| } |
| |
| /** Set the value of the print-help flag. */ |
| public Builder setPrintHelp(boolean printHelp) { |
| this.printHelp = printHelp; |
| return this; |
| } |
| |
| /** True if the print-version flag is enabled. */ |
| public boolean isPrintVersion() { |
| return printVersion; |
| } |
| |
| /** Set the value of the print-version flag. */ |
| public Builder setPrintVersion(boolean printVersion) { |
| this.printVersion = printVersion; |
| return this; |
| } |
| |
| private void error(Diagnostic diagnostic) { |
| reporter.error(diagnostic); |
| } |
| |
| public BackportedMethodListCommand build() { |
| AndroidApp library = app.build(); |
| if (!desugaredLibraryConfigurationResources.isEmpty() |
| && library.getLibraryResourceProviders().isEmpty()) { |
| reporter.error( |
| new StringDiagnostic("With desugared library configuration a library is required")); |
| } |
| |
| if (isPrintHelp() || isPrintVersion()) { |
| return new BackportedMethodListCommand(isPrintHelp(), isPrintVersion()); |
| } |
| |
| if (backportedMethodListConsumer == null) { |
| backportedMethodListConsumer = |
| new StringConsumer() { |
| @Override |
| public void accept(String string, DiagnosticsHandler handler) { |
| System.out.println(string); |
| } |
| |
| @Override |
| public void finished(DiagnosticsHandler handler) {} |
| }; |
| } |
| DexItemFactory factory = new DexItemFactory(); |
| return new BackportedMethodListCommand( |
| reporter, |
| minApiLevel, |
| getDesugaredLibraryConfiguration(factory), |
| library, |
| backportedMethodListConsumer, |
| factory); |
| } |
| } |
| } |