| // Copyright (c) 2017, 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 static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING; | 
 |  | 
 | import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation; | 
 | import com.android.tools.r8.dex.Marker.Tool; | 
 | import com.android.tools.r8.errors.DexFileOverflowDiagnostic; | 
 | import com.android.tools.r8.graph.DexItemFactory; | 
 | import com.android.tools.r8.inspector.Inspector; | 
 | import com.android.tools.r8.inspector.internal.InspectorImpl; | 
 | import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration; | 
 | import com.android.tools.r8.origin.Origin; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationParser; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationRule; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationSource; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationSourceFile; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings; | 
 | import com.android.tools.r8.utils.AndroidApiLevel; | 
 | import com.android.tools.r8.utils.AndroidApp; | 
 | import com.android.tools.r8.utils.AssertionConfigurationWithDefault; | 
 | import com.android.tools.r8.utils.InternalOptions; | 
 | import com.android.tools.r8.utils.InternalOptions.DesugarState; | 
 | import com.android.tools.r8.utils.Reporter; | 
 | import com.android.tools.r8.utils.StringDiagnostic; | 
 | import com.android.tools.r8.utils.ThreadUtils; | 
 | import com.google.common.collect.ImmutableList; | 
 | 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.function.BiPredicate; | 
 | import java.util.function.Consumer; | 
 |  | 
 | /** | 
 |  * Immutable command structure for an invocation of the {@link D8} compiler. | 
 |  * | 
 |  * <p>To build a D8 command use the {@link D8Command.Builder} class. For example: | 
 |  * | 
 |  * <pre> | 
 |  *   D8Command command = D8Command.builder() | 
 |  *     .addProgramFiles(path1, path2) | 
 |  *     .setMode(CompilationMode.RELEASE) | 
 |  *     .setOutput(Paths.get("output.zip", OutputMode.DexIndexed)) | 
 |  *     .build(); | 
 |  * </pre> | 
 |  */ | 
 | @Keep | 
 | public final class D8Command extends BaseCompilerCommand { | 
 |  | 
 |   private static class DefaultD8DiagnosticsHandler implements DiagnosticsHandler { | 
 |  | 
 |     @Override | 
 |     public void error(Diagnostic error) { | 
 |       if (error instanceof DexFileOverflowDiagnostic) { | 
 |         DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error; | 
 |         if (!overflowDiagnostic.hasMainDexSpecification()) { | 
 |           DiagnosticsHandler.super.error( | 
 |               new StringDiagnostic( | 
 |                   overflowDiagnostic.getDiagnosticMessage() + ". Try supplying a main-dex list")); | 
 |           return; | 
 |         } | 
 |       } | 
 |       DiagnosticsHandler.super.error(error); | 
 |     } | 
 |   } | 
 |  | 
 |   /** | 
 |    * Builder for constructing a D8Command. | 
 |    * | 
 |    * <p>A builder is obtained by calling {@link D8Command#builder}. | 
 |    */ | 
 |   @Keep | 
 |   public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> { | 
 |  | 
 |     private boolean intermediate = false; | 
 |     private DesugarGraphConsumer desugarGraphConsumer = null; | 
 |     private StringConsumer desugaredLibraryKeepRuleConsumer = null; | 
 |     private String synthesizedClassPrefix = ""; | 
 |     private boolean enableMainDexListCheck = true; | 
 |     private boolean minimalMainDex = false; | 
 |     private boolean skipDump = false; | 
 |     private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>(); | 
 |  | 
 |     private Builder() { | 
 |       this(new DefaultD8DiagnosticsHandler()); | 
 |     } | 
 |  | 
 |     private Builder(DiagnosticsHandler diagnosticsHandler) { | 
 |       super(diagnosticsHandler); | 
 |     } | 
 |  | 
 |     private Builder(AndroidApp app) { | 
 |       super(app); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Add dex program-data. | 
 |      */ | 
 |     @Override | 
 |     public Builder addDexProgramData(byte[] data, Origin origin) { | 
 |       guard(() -> getAppBuilder().addDexProgramData(data, origin)); | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Add classpath file resources. These have @Override to ensure binary compatibility. | 
 |      */ | 
 |     @Override | 
 |     public Builder addClasspathFiles(Path... files) { | 
 |       return super.addClasspathFiles(files); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Add classpath file resources. | 
 |      */ | 
 |     @Override | 
 |     public Builder addClasspathFiles(Collection<Path> files) { | 
 |       return super.addClasspathFiles(files); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Add classfile resources provider for class-path resources. | 
 |      */ | 
 |     @Override | 
 |     public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) { | 
 |       return super.addClasspathResourceProvider(provider); | 
 |     } | 
 |  | 
 |     /** Set input proguard map used for distribution of classes in multi-dex. */ | 
 |     public Builder setProguardInputMapFile(Path proguardInputMap) { | 
 |       getAppBuilder().setProguardMapInputData(proguardInputMap); | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Indicate if compilation is to intermediate results, i.e., intended for later merging. | 
 |      * | 
 |      * <p>Intermediate mode is implied if compiling results to a "file-per-class-file". | 
 |      */ | 
 |     public Builder setIntermediate(boolean value) { | 
 |       this.intermediate = value; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Set a consumer for receiving the keep rules to use when compiling the desugared library for | 
 |      * the program being compiled in this compilation. | 
 |      * | 
 |      * @param keepRuleConsumer Consumer to receive the content once produced. | 
 |      */ | 
 |     public Builder setDesugaredLibraryKeepRuleConsumer(StringConsumer keepRuleConsumer) { | 
 |       this.desugaredLibraryKeepRuleConsumer = keepRuleConsumer; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Get the consumer that will receive dependency information for desugaring. | 
 |      */ | 
 |     public DesugarGraphConsumer getDesugarGraphConsumer() { | 
 |       return desugarGraphConsumer; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Set the consumer that will receive dependency information for desugaring. | 
 |      * | 
 |      * <p>Setting the consumer will clear any previously set consumer. | 
 |      */ | 
 |     public Builder setDesugarGraphConsumer(DesugarGraphConsumer desugarGraphConsumer) { | 
 |       this.desugarGraphConsumer = desugarGraphConsumer; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Allow to skip to dump into file and dump into directory instruction, this is primarily used | 
 |      * for chained compilation in L8 so there are no duplicated dumps. | 
 |      */ | 
 |     Builder skipDump() { | 
 |       skipDump = true; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     @Override | 
 |     Builder self() { | 
 |       return this; | 
 |     } | 
 |  | 
 |     @Override | 
 |     CompilationMode defaultCompilationMode() { | 
 |       return CompilationMode.DEBUG; | 
 |     } | 
 |  | 
 |     Builder setSynthesizedClassesPrefix(String prefix) { | 
 |       synthesizedClassPrefix = prefix; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     @Deprecated | 
 |     // Internal helper for supporting bazel integration. | 
 |     Builder setEnableMainDexListCheck(boolean value) { | 
 |       enableMainDexListCheck = value; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     @Deprecated | 
 |     // Internal helper for supporting bazel integration. | 
 |     Builder setMinimalMainDex(boolean value) { | 
 |       minimalMainDex = value; | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ | 
 |     public Builder addMainDexRulesFiles(Path... paths) { | 
 |       return addMainDexRulesFiles(Arrays.asList(paths)); | 
 |     } | 
 |  | 
 |     /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ | 
 |     public Builder addMainDexRulesFiles(Collection<Path> paths) { | 
 |       guard(() -> paths.forEach(p -> mainDexRules.add(new ProguardConfigurationSourceFile(p)))); | 
 |       return self(); | 
 |     } | 
 |  | 
 |     /** Add proguard rules for automatic main-dex-list calculation. */ | 
 |     public Builder addMainDexRules(List<String> lines, Origin origin) { | 
 |       guard( | 
 |           () -> | 
 |               mainDexRules.add( | 
 |                   new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin))); | 
 |       return self(); | 
 |     } | 
 |  | 
 |     @Override | 
 |     void validate() { | 
 |       if (isPrintHelp()) { | 
 |         return; | 
 |       } | 
 |       Reporter reporter = getReporter(); | 
 |       if (getProgramConsumer() instanceof ClassFileConsumer) { | 
 |         reporter.warning("Compiling to Java class files with D8 is not officially supported"); | 
 |       } | 
 |       if (getAppBuilder().hasMainDexList()) { | 
 |         if (intermediate) { | 
 |           reporter.error("Option --main-dex-list cannot be used with --intermediate"); | 
 |         } | 
 |         if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { | 
 |           reporter.error("Option --main-dex-list cannot be used with --file-per-class"); | 
 |         } | 
 |       } | 
 |       if (!mainDexRules.isEmpty()) { | 
 |         if (intermediate) { | 
 |           reporter.error("Option --main-dex-rules cannot be used with --intermediate"); | 
 |         } | 
 |         if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { | 
 |           reporter.error("Option --main-dex-rules cannot be used with --file-per-class"); | 
 |         } | 
 |       } | 
 |       if (getMainDexListConsumer() != null | 
 |           && mainDexRules.isEmpty() | 
 |           && !getAppBuilder().hasMainDexList()) { | 
 |         reporter.error( | 
 |             "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list"); | 
 |       } | 
 |       if (getMinApiLevel() >= AndroidApiLevel.L.getLevel()) { | 
 |         if (getMainDexListConsumer() != null || getAppBuilder().hasMainDexList()) { | 
 |           reporter.error( | 
 |               "D8 does not support main-dex inputs and outputs when compiling to API level " | 
 |                   + AndroidApiLevel.L.getLevel() | 
 |                   + " and above"); | 
 |         } | 
 |       } | 
 |       if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) { | 
 |         reporter.error("Using desugared library configuration requires desugaring to be enabled"); | 
 |       } | 
 |       super.validate(); | 
 |     } | 
 |  | 
 |     @Override | 
 |     D8Command makeCommand() { | 
 |       if (isPrintHelp() || isPrintVersion()) { | 
 |         return new D8Command(isPrintHelp(), isPrintVersion()); | 
 |       } | 
 |  | 
 |       intermediate |= getProgramConsumer() instanceof DexFilePerClassFileConsumer; | 
 |  | 
 |       DexItemFactory factory = new DexItemFactory(); | 
 |       DesugaredLibraryConfiguration libraryConfiguration = | 
 |           getDesugaredLibraryConfiguration(factory, false); | 
 |  | 
 |       ImmutableList<ProguardConfigurationRule> mainDexKeepRules = | 
 |           ProguardConfigurationParser.parse(mainDexRules, factory, getReporter()); | 
 |  | 
 |       return new D8Command( | 
 |           getAppBuilder().build(), | 
 |           getMode(), | 
 |           getProgramConsumer(), | 
 |           getMainDexListConsumer(), | 
 |           getMinApiLevel(), | 
 |           getReporter(), | 
 |           getDesugaringState(), | 
 |           intermediate, | 
 |           isOptimizeMultidexForLinearAlloc(), | 
 |           getIncludeClassesChecksum(), | 
 |           getDexClassChecksumFilter(), | 
 |           getDesugarGraphConsumer(), | 
 |           desugaredLibraryKeepRuleConsumer, | 
 |           libraryConfiguration, | 
 |           getAssertionsConfiguration(), | 
 |           getOutputInspections(), | 
 |           synthesizedClassPrefix, | 
 |           skipDump, | 
 |           enableMainDexListCheck, | 
 |           minimalMainDex, | 
 |           mainDexKeepRules, | 
 |           getThreadCount(), | 
 |           factory); | 
 |     } | 
 |   } | 
 |  | 
 |   static final String USAGE_MESSAGE = D8CommandParser.USAGE_MESSAGE; | 
 |  | 
 |   private final boolean intermediate; | 
 |   private final DesugarGraphConsumer desugarGraphConsumer; | 
 |   private final StringConsumer desugaredLibraryKeepRuleConsumer; | 
 |   private final DesugaredLibraryConfiguration libraryConfiguration; | 
 |   private final String synthesizedClassPrefix; | 
 |   private final boolean skipDump; | 
 |   private final boolean enableMainDexListCheck; | 
 |   private final boolean minimalMainDex; | 
 |   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules; | 
 |   private final DexItemFactory factory; | 
 |  | 
 |   public static Builder builder() { | 
 |     return new Builder(); | 
 |   } | 
 |  | 
 |   public static Builder builder(DiagnosticsHandler diagnosticsHandler) { | 
 |     return new Builder(diagnosticsHandler); | 
 |   } | 
 |  | 
 |   // Internal builder to start from an existing AndroidApp. | 
 |   static Builder builder(AndroidApp app) { | 
 |     return new Builder(app); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Parse the D8 command-line. | 
 |    * | 
 |    * <p>Parsing will set the supplied options or their default value if they have any. | 
 |    * | 
 |    * @param args Command-line arguments array. | 
 |    * @param origin Origin description of the command-line arguments. | 
 |    * @return D8 command builder with state set up according to parsed command line. | 
 |    */ | 
 |   public static Builder parse(String[] args, Origin origin) { | 
 |     return D8CommandParser.parse(args, origin); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Parse the D8 command-line. | 
 |    * | 
 |    * <p>Parsing will set the supplied options or their default value if they have any. | 
 |    * | 
 |    * @param args Command-line arguments array. | 
 |    * @param origin Origin description of the command-line arguments. | 
 |    * @param handler Custom defined diagnostics handler. | 
 |    * @return D8 command builder with state set up according to parsed command line. | 
 |    */ | 
 |   public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) { | 
 |     return D8CommandParser.parse(args, origin, handler); | 
 |   } | 
 |  | 
 |   private D8Command( | 
 |       AndroidApp inputApp, | 
 |       CompilationMode mode, | 
 |       ProgramConsumer programConsumer, | 
 |       StringConsumer mainDexListConsumer, | 
 |       int minApiLevel, | 
 |       Reporter diagnosticsHandler, | 
 |       DesugarState enableDesugaring, | 
 |       boolean intermediate, | 
 |       boolean optimizeMultidexForLinearAlloc, | 
 |       boolean encodeChecksum, | 
 |       BiPredicate<String, Long> dexClassChecksumFilter, | 
 |       DesugarGraphConsumer desugarGraphConsumer, | 
 |       StringConsumer desugaredLibraryKeepRuleConsumer, | 
 |       DesugaredLibraryConfiguration libraryConfiguration, | 
 |       List<AssertionsConfiguration> assertionsConfiguration, | 
 |       List<Consumer<Inspector>> outputInspections, | 
 |       String synthesizedClassPrefix, | 
 |       boolean skipDump, | 
 |       boolean enableMainDexListCheck, | 
 |       boolean minimalMainDex, | 
 |       ImmutableList<ProguardConfigurationRule> mainDexKeepRules, | 
 |       int threadCount, | 
 |       DexItemFactory factory) { | 
 |     super( | 
 |         inputApp, | 
 |         mode, | 
 |         programConsumer, | 
 |         mainDexListConsumer, | 
 |         minApiLevel, | 
 |         diagnosticsHandler, | 
 |         enableDesugaring, | 
 |         optimizeMultidexForLinearAlloc, | 
 |         encodeChecksum, | 
 |         dexClassChecksumFilter, | 
 |         assertionsConfiguration, | 
 |         outputInspections, | 
 |         threadCount); | 
 |     this.intermediate = intermediate; | 
 |     this.desugarGraphConsumer = desugarGraphConsumer; | 
 |     this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; | 
 |     this.libraryConfiguration = libraryConfiguration; | 
 |     this.synthesizedClassPrefix = synthesizedClassPrefix; | 
 |     this.skipDump = skipDump; | 
 |     this.enableMainDexListCheck = enableMainDexListCheck; | 
 |     this.minimalMainDex = minimalMainDex; | 
 |     this.mainDexKeepRules = mainDexKeepRules; | 
 |     this.factory = factory; | 
 |   } | 
 |  | 
 |   private D8Command(boolean printHelp, boolean printVersion) { | 
 |     super(printHelp, printVersion); | 
 |     intermediate = false; | 
 |     desugarGraphConsumer = null; | 
 |     desugaredLibraryKeepRuleConsumer = null; | 
 |     libraryConfiguration = null; | 
 |     synthesizedClassPrefix = null; | 
 |     skipDump = false; | 
 |     enableMainDexListCheck = true; | 
 |     minimalMainDex = false; | 
 |     mainDexKeepRules = null; | 
 |     factory = null; | 
 |   } | 
 |  | 
 |   @Override | 
 |   InternalOptions getInternalOptions() { | 
 |     InternalOptions internal = new InternalOptions(factory, getReporter()); | 
 |     assert !internal.debug; | 
 |     internal.debug = getMode() == CompilationMode.DEBUG; | 
 |     internal.programConsumer = getProgramConsumer(); | 
 |     if (internal.isGeneratingClassFiles()) { | 
 |       internal.cfToCfDesugar = true; | 
 |     } | 
 |     internal.mainDexListConsumer = getMainDexListConsumer(); | 
 |     internal.minimalMainDex = internal.debug || minimalMainDex; | 
 |     internal.enableMainDexListCheck = enableMainDexListCheck; | 
 |     internal.minApiLevel = getMinApiLevel(); | 
 |     internal.intermediate = intermediate; | 
 |     internal.readCompileTimeAnnotations = intermediate; | 
 |     internal.desugarGraphConsumer = desugarGraphConsumer; | 
 |     internal.mainDexKeepRules = mainDexKeepRules; | 
 |  | 
 |     // Assert and fixup defaults. | 
 |     assert !internal.isShrinking(); | 
 |     assert !internal.isMinifying(); | 
 |     assert !internal.passthroughDexCode; | 
 |     internal.passthroughDexCode = true; | 
 |     assert internal.neverMergePrefixes.contains("j$."); | 
 |  | 
 |     // Assert some of R8 optimizations are disabled. | 
 |     assert !internal.enableInlining; | 
 |     assert !internal.enableClassInlining; | 
 |     assert internal.horizontalClassMergerOptions().isDisabled(); | 
 |     assert !internal.enableStaticClassMerging; | 
 |     assert !internal.enableVerticalClassMerging; | 
 |     assert !internal.enableClassStaticizer; | 
 |     assert !internal.enableEnumValueOptimization; | 
 |     assert !internal.outline.enabled; | 
 |     assert !internal.enableValuePropagation; | 
 |     assert !internal.enableLambdaMerging; | 
 |     assert !internal.enableTreeShakingOfLibraryMethodOverrides; | 
 |  | 
 |     internal.desugarState = getDesugarState(); | 
 |     internal.encodeChecksums = getIncludeClassesChecksum(); | 
 |     internal.dexClassChecksumFilter = getDexClassChecksumFilter(); | 
 |     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc(); | 
 |  | 
 |     internal.desugaredLibraryConfiguration = libraryConfiguration; | 
 |     internal.synthesizedClassPrefix = synthesizedClassPrefix; | 
 |     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; | 
 |  | 
 |     // Default is to remove all javac generated assertion code when generating dex. | 
 |     assert internal.assertionsConfiguration == null; | 
 |     internal.assertionsConfiguration = | 
 |         new AssertionConfigurationWithDefault( | 
 |             AssertionTransformation.DISABLE, getAssertionsConfiguration()); | 
 |  | 
 |     internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections()); | 
 |  | 
 |     if (!DETERMINISTIC_DEBUGGING) { | 
 |       assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; | 
 |       internal.threadCount = getThreadCount(); | 
 |     } | 
 |  | 
 |     if (skipDump) { | 
 |       internal.dumpInputToDirectory = null; | 
 |       internal.dumpInputToFile = null; | 
 |     } | 
 |     internal.dumpOptions = dumpOptions(); | 
 |  | 
 |     return internal; | 
 |   } | 
 |  | 
 |   private DumpOptions dumpOptions() { | 
 |     DumpOptions.Builder builder = DumpOptions.builder(Tool.D8); | 
 |     dumpBaseCommandOptions(builder); | 
 |     return builder | 
 |         .setIntermediate(intermediate) | 
 |         .setDesugaredLibraryConfiguration(libraryConfiguration) | 
 |         .build(); | 
 |   } | 
 | } |