Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1 | // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | package com.android.tools.r8; |
| 5 | |
Morten Krogh-Jespersen | 4fa5d82 | 2020-03-25 19:28:09 +0100 | [diff] [blame] | 6 | import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING; |
| 7 | |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 8 | import com.android.tools.r8.ProgramResource.Kind; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 9 | import com.android.tools.r8.dex.Marker.Tool; |
Ian Zerny | 1c6f954 | 2022-03-02 13:46:25 +0100 | [diff] [blame] | 10 | import com.android.tools.r8.dump.DumpOptions; |
Ian Zerny | 7247517 | 2018-09-18 05:52:00 +0200 | [diff] [blame] | 11 | import com.android.tools.r8.errors.DexFileOverflowDiagnostic; |
Ian Zerny | bbb59cc | 2018-12-21 14:23:58 +0100 | [diff] [blame] | 12 | import com.android.tools.r8.experimental.graphinfo.GraphConsumer; |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 13 | import com.android.tools.r8.features.FeatureSplitConfiguration; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 14 | import com.android.tools.r8.graph.DexItemFactory; |
Ian Zerny | 8f3ba44 | 2020-03-11 16:32:27 +0100 | [diff] [blame] | 15 | import com.android.tools.r8.inspector.Inspector; |
| 16 | import com.android.tools.r8.inspector.internal.InspectorImpl; |
Clément Béra | 24cb20a | 2022-02-17 09:30:14 +0000 | [diff] [blame] | 17 | import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 18 | import com.android.tools.r8.keepanno.asm.KeepEdgeReader; |
| 19 | import com.android.tools.r8.keepanno.ast.KeepEdge; |
| 20 | import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor; |
Ian Zerny | 6ddf0e9 | 2021-11-18 11:29:33 +0100 | [diff] [blame] | 21 | import com.android.tools.r8.naming.SourceFileRewriter; |
Yohann Roussel | 73df5e6 | 2017-11-20 14:06:32 +0100 | [diff] [blame] | 22 | import com.android.tools.r8.origin.Origin; |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 23 | import com.android.tools.r8.origin.PathOrigin; |
Christoffer Quist Adamsen | dafb046 | 2022-09-01 12:37:09 +0200 | [diff] [blame] | 24 | import com.android.tools.r8.profile.art.ArtProfileForRewriting; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 25 | import com.android.tools.r8.shaking.FilteredClassPath; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 26 | import com.android.tools.r8.shaking.ProguardConfiguration; |
| 27 | import com.android.tools.r8.shaking.ProguardConfigurationParser; |
Christoffer Quist Adamsen | 6e68954 | 2022-05-30 20:02:09 +0200 | [diff] [blame] | 28 | import com.android.tools.r8.shaking.ProguardConfigurationParserOptions; |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 29 | import com.android.tools.r8.shaking.ProguardConfigurationRule; |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 30 | import com.android.tools.r8.shaking.ProguardConfigurationSource; |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 31 | import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes; |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 32 | import com.android.tools.r8.shaking.ProguardConfigurationSourceFile; |
| 33 | import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings; |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 34 | import com.android.tools.r8.startup.StartupProfileProvider; |
Ian Zerny | 1ca4148 | 2018-12-18 13:42:49 +0000 | [diff] [blame] | 35 | import com.android.tools.r8.utils.AndroidApiLevel; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 36 | import com.android.tools.r8.utils.AndroidApp; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 37 | import com.android.tools.r8.utils.ArchiveResourceProvider; |
Søren Gjesse | 99e8a74 | 2020-01-07 13:59:38 +0100 | [diff] [blame] | 38 | import com.android.tools.r8.utils.AssertionConfigurationWithDefault; |
Søren Gjesse | 75a0979 | 2021-04-07 12:41:33 +0200 | [diff] [blame] | 39 | import com.android.tools.r8.utils.DumpInputFlags; |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 40 | import com.android.tools.r8.utils.ExceptionDiagnostic; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 41 | import com.android.tools.r8.utils.FileUtils; |
| 42 | import com.android.tools.r8.utils.InternalOptions; |
Søren Gjesse | 9a7876d | 2020-01-13 14:32:26 +0100 | [diff] [blame] | 43 | import com.android.tools.r8.utils.InternalOptions.DesugarState; |
Christoffer Quist Adamsen | 0cc904a | 2021-05-11 11:31:58 +0200 | [diff] [blame] | 44 | import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions; |
Tamas Kenez | 3b5829a | 2017-12-18 11:22:06 +0100 | [diff] [blame] | 45 | import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; |
Ian Zerny | 7744006 | 2022-08-26 13:49:08 +0200 | [diff] [blame] | 46 | import com.android.tools.r8.utils.ProgramClassCollection; |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 47 | import com.android.tools.r8.utils.Reporter; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 48 | import com.android.tools.r8.utils.SemanticVersion; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 49 | import com.android.tools.r8.utils.SetUtils; |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 50 | import com.android.tools.r8.utils.StringDiagnostic; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 51 | import com.android.tools.r8.utils.StringUtils; |
Søren Gjesse | 943389f | 2020-03-13 10:40:25 +0100 | [diff] [blame] | 52 | import com.android.tools.r8.utils.ThreadUtils; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 53 | import com.google.common.base.Suppliers; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 54 | import com.google.common.collect.ImmutableList; |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 55 | import java.io.InputStream; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 56 | import java.nio.file.Path; |
| 57 | import java.nio.file.Paths; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 58 | import java.util.ArrayDeque; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 59 | import java.util.ArrayList; |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 60 | import java.util.Arrays; |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 61 | import java.util.Collection; |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 62 | import java.util.Collections; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 63 | import java.util.Deque; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 64 | import java.util.List; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 65 | import java.util.Optional; |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 66 | import java.util.Set; |
Alan Leung | 4b5db8e | 2019-07-23 13:53:10 -0700 | [diff] [blame] | 67 | import java.util.function.BiPredicate; |
Jinseong Jeon | bcbe4cb | 2017-08-15 21:29:20 -0700 | [diff] [blame] | 68 | import java.util.function.Consumer; |
Rico Wind | 25f6eab | 2019-09-06 11:06:11 +0200 | [diff] [blame] | 69 | import java.util.function.Function; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 70 | import java.util.function.Supplier; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 71 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 72 | /** |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 73 | * Immutable command structure for an invocation of the {@link R8} compiler. |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 74 | * |
| 75 | * <p>To build a R8 command use the {@link R8Command.Builder} class. For example: |
| 76 | * |
| 77 | * <pre> |
| 78 | * R8Command command = R8Command.builder() |
| 79 | * .addProgramFiles(path1, path2) |
| 80 | * .setMode(CompilationMode.RELEASE) |
| 81 | * .setOutput(Paths.get("output.zip", OutputMode.DexIndexed)) |
| 82 | * .build(); |
| 83 | * </pre> |
| 84 | */ |
Mathias Rav | 3fb4a3a | 2018-05-29 15:41:36 +0200 | [diff] [blame] | 85 | @Keep |
| 86 | public final class R8Command extends BaseCompilerCommand { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 87 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 88 | /** |
| 89 | * Builder for constructing a R8Command. |
| 90 | * |
| 91 | * <p>A builder is obtained by calling {@link R8Command#builder}. |
| 92 | */ |
Mathias Rav | 3fb4a3a | 2018-05-29 15:41:36 +0200 | [diff] [blame] | 93 | @Keep |
Søren Gjesse | c4e5e93 | 2017-09-04 17:01:23 +0200 | [diff] [blame] | 94 | public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 95 | |
Morten Krogh-Jespersen | ff59ed3 | 2018-10-08 15:15:57 +0200 | [diff] [blame] | 96 | private static class DefaultR8DiagnosticsHandler implements DiagnosticsHandler { |
Ian Zerny | 7247517 | 2018-09-18 05:52:00 +0200 | [diff] [blame] | 97 | |
| 98 | @Override |
| 99 | public void error(Diagnostic error) { |
| 100 | if (error instanceof DexFileOverflowDiagnostic) { |
| 101 | DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error; |
| 102 | if (!overflowDiagnostic.hasMainDexSpecification()) { |
Morten Krogh-Jespersen | ff59ed3 | 2018-10-08 15:15:57 +0200 | [diff] [blame] | 103 | DiagnosticsHandler.super.error( |
Ian Zerny | 7247517 | 2018-09-18 05:52:00 +0200 | [diff] [blame] | 104 | new StringDiagnostic( |
| 105 | overflowDiagnostic.getDiagnosticMessage() |
| 106 | + ". Try supplying a main-dex list or main-dex rules")); |
| 107 | return; |
| 108 | } |
| 109 | } |
Morten Krogh-Jespersen | ff59ed3 | 2018-10-08 15:15:57 +0200 | [diff] [blame] | 110 | DiagnosticsHandler.super.error(error); |
Ian Zerny | 7247517 | 2018-09-18 05:52:00 +0200 | [diff] [blame] | 111 | } |
| 112 | } |
| 113 | |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 114 | private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>(); |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 115 | private Consumer<ProguardConfiguration.Builder> proguardConfigurationConsumerForTesting = null; |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 116 | private Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null; |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 117 | private StringConsumer desugaredLibraryKeepRuleConsumer = null; |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 118 | private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>(); |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 119 | private boolean disableTreeShaking = false; |
| 120 | private boolean disableMinification = false; |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 121 | private boolean disableVerticalClassMerging = false; |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 122 | private boolean forceProguardCompatibility = false; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 123 | private Optional<Boolean> includeDataResources = Optional.empty(); |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 124 | private StringConsumer proguardUsageConsumer = null; |
| 125 | private StringConsumer proguardSeedsConsumer = null; |
| 126 | private StringConsumer proguardConfigurationConsumer = null; |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 127 | private GraphConsumer keptGraphConsumer = null; |
| 128 | private GraphConsumer mainDexKeptGraphConsumer = null; |
Ian Zerny | ec9566d | 2022-03-02 13:41:14 +0100 | [diff] [blame] | 129 | private InputDependencyGraphConsumer inputDependencyGraphConsumer = null; |
Rico Wind | 25f6eab | 2019-09-06 11:06:11 +0200 | [diff] [blame] | 130 | private final List<FeatureSplit> featureSplits = new ArrayList<>(); |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 131 | private String synthesizedClassPrefix = ""; |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 132 | private boolean enableMissingLibraryApiModeling = false; |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 133 | private boolean enableExperimentalKeepAnnotations = false; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 134 | private SemanticVersion fakeCompilerVersion = null; |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 135 | |
Christoffer Quist Adamsen | 6e68954 | 2022-05-30 20:02:09 +0200 | [diff] [blame] | 136 | private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder = |
| 137 | ProguardConfigurationParserOptions.builder().readEnvironment(); |
Søren Gjesse | c641bcc | 2022-06-29 10:00:34 +0200 | [diff] [blame] | 138 | private final boolean allowDexInArchive = |
| 139 | System.getProperty("com.android.tools.r8.allowDexInputToR8") != null; |
Søren Gjesse | c9e940f | 2018-05-28 12:56:56 +0200 | [diff] [blame] | 140 | |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 141 | // TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing. |
Ian Zerny | 7247517 | 2018-09-18 05:52:00 +0200 | [diff] [blame] | 142 | Builder() { |
| 143 | this(new DefaultR8DiagnosticsHandler()); |
| 144 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 145 | |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 146 | Builder(DiagnosticsHandler diagnosticsHandler) { |
Søren Gjesse | c25649a | 2018-02-01 12:41:28 +0100 | [diff] [blame] | 147 | super(diagnosticsHandler); |
Søren Gjesse | c641bcc | 2022-06-29 10:00:34 +0200 | [diff] [blame] | 148 | setIgnoreDexInArchive(!allowDexInArchive); |
Søren Gjesse | 8fb83c8 | 2017-12-01 08:00:10 +0100 | [diff] [blame] | 149 | } |
| 150 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 151 | private Builder(AndroidApp app) { |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 152 | super(app); |
Søren Gjesse | c641bcc | 2022-06-29 10:00:34 +0200 | [diff] [blame] | 153 | setIgnoreDexInArchive(!allowDexInArchive); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 154 | } |
| 155 | |
Christoffer Quist Adamsen | 0eef4b0 | 2018-08-01 10:29:47 +0200 | [diff] [blame] | 156 | private Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) { |
| 157 | super(app, diagnosticsHandler); |
Søren Gjesse | c641bcc | 2022-06-29 10:00:34 +0200 | [diff] [blame] | 158 | setIgnoreDexInArchive(!allowDexInArchive); |
Christoffer Quist Adamsen | 0eef4b0 | 2018-08-01 10:29:47 +0200 | [diff] [blame] | 159 | } |
| 160 | |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 161 | // Internal |
| 162 | |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 163 | void setDisableVerticalClassMerging(boolean disableVerticalClassMerging) { |
| 164 | this.disableVerticalClassMerging = disableVerticalClassMerging; |
Christoffer Quist Adamsen | 3b23783 | 2018-08-15 16:56:11 +0200 | [diff] [blame] | 165 | } |
| 166 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 167 | @Override |
| 168 | Builder self() { |
| 169 | return this; |
| 170 | } |
| 171 | |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 172 | @Override |
| 173 | CompilationMode defaultCompilationMode() { |
| 174 | return CompilationMode.RELEASE; |
| 175 | } |
| 176 | |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 177 | Builder setSynthesizedClassesPrefix(String prefix) { |
| 178 | synthesizedClassPrefix = prefix; |
| 179 | return self(); |
| 180 | } |
| 181 | |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 182 | Builder setFakeCompilerVersion(SemanticVersion version) { |
| 183 | fakeCompilerVersion = version; |
| 184 | return self(); |
| 185 | } |
| 186 | |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 187 | /** |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 188 | * Disable tree shaking. |
| 189 | * |
| 190 | * <p>If true, tree shaking is completely disabled, otherwise tree shaking is configured by |
| 191 | * ProGuard configuration settings. |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 192 | */ |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 193 | public Builder setDisableTreeShaking(boolean disableTreeShaking) { |
| 194 | this.disableTreeShaking = disableTreeShaking; |
Jinseong Jeon | a4317e6 | 2017-07-31 22:50:07 -0700 | [diff] [blame] | 195 | return self(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 196 | } |
| 197 | |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 198 | /** |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 199 | * Disable minification of names. |
| 200 | * |
| 201 | * <p>If true, minification of names is completely disabled, otherwise minification of names is |
| 202 | * configured by ProGuard configuration settings. |
Tamas Kenez | 887d57f | 2017-08-22 17:07:25 +0200 | [diff] [blame] | 203 | */ |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 204 | public Builder setDisableMinification(boolean disableMinification) { |
| 205 | this.disableMinification = disableMinification; |
Tamas Kenez | 887d57f | 2017-08-22 17:07:25 +0200 | [diff] [blame] | 206 | return self(); |
| 207 | } |
| 208 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 209 | /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 210 | public Builder addMainDexRulesFiles(Path... paths) { |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 211 | return addMainDexRulesFiles(Arrays.asList(paths)); |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 212 | } |
| 213 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 214 | /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 215 | public Builder addMainDexRulesFiles(Collection<Path> paths) { |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 216 | guard(() -> paths.forEach(p -> mainDexRules.add(new ProguardConfigurationSourceFile(p)))); |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 217 | return self(); |
| 218 | } |
| 219 | |
Ian Zerny | 5b65b3f | 2017-12-22 13:37:45 +0100 | [diff] [blame] | 220 | /** Add proguard rules for automatic main-dex-list calculation. */ |
Yohann Roussel | 73df5e6 | 2017-11-20 14:06:32 +0100 | [diff] [blame] | 221 | public Builder addMainDexRules(List<String> lines, Origin origin) { |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 222 | guard( |
| 223 | () -> |
| 224 | mainDexRules.add( |
| 225 | new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin))); |
Jinseong Jeon | a4317e6 | 2017-07-31 22:50:07 -0700 | [diff] [blame] | 226 | return self(); |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 227 | } |
| 228 | |
Ian Zerny | 797abef | 2022-04-01 15:39:02 +0200 | [diff] [blame] | 229 | /** |
| 230 | * Set Proguard compatibility mode. |
| 231 | * |
| 232 | * <p>If true, R8 will attempt to retain more compatibility with Proguard. Most notably, R8 will |
| 233 | * introduce rules for keeping more default constructors as well as various attributes. Note |
| 234 | * that setting R8 in compatibility mode will result in larger residual programs. |
| 235 | */ |
| 236 | public Builder setProguardCompatibility(boolean value) { |
| 237 | this.forceProguardCompatibility = value; |
| 238 | return self(); |
| 239 | } |
| 240 | |
| 241 | /** Get the current value of Proguard compatibility mode. */ |
| 242 | public boolean getProguardCompatibility() { |
| 243 | return forceProguardCompatibility; |
| 244 | } |
| 245 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 246 | /** Add proguard configuration-file resources. */ |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 247 | public Builder addProguardConfigurationFiles(Path... paths) { |
Yohann Roussel | 11efcc0 | 2017-12-01 12:04:36 +0100 | [diff] [blame] | 248 | guard(() -> { |
| 249 | for (Path path : paths) { |
| 250 | proguardConfigs.add(new ProguardConfigurationSourceFile(path)); |
| 251 | } |
| 252 | }); |
Jinseong Jeon | a4317e6 | 2017-07-31 22:50:07 -0700 | [diff] [blame] | 253 | return self(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 254 | } |
| 255 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 256 | /** Add proguard configuration-file resources. */ |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 257 | public Builder addProguardConfigurationFiles(List<Path> paths) { |
Yohann Roussel | 11efcc0 | 2017-12-01 12:04:36 +0100 | [diff] [blame] | 258 | guard(() -> { |
| 259 | for (Path path : paths) { |
| 260 | proguardConfigs.add(new ProguardConfigurationSourceFile(path)); |
| 261 | } |
| 262 | }); |
Søren Gjesse | a26ef84 | 2017-09-01 11:46:43 +0200 | [diff] [blame] | 263 | return self(); |
| 264 | } |
| 265 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 266 | /** Add proguard configuration. */ |
Yohann Roussel | 73df5e6 | 2017-11-20 14:06:32 +0100 | [diff] [blame] | 267 | public Builder addProguardConfiguration(List<String> lines, Origin origin) { |
Yohann Roussel | 11efcc0 | 2017-12-01 12:04:36 +0100 | [diff] [blame] | 268 | guard(() -> proguardConfigs.add( |
| 269 | new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin))); |
Jinseong Jeon | a4317e6 | 2017-07-31 22:50:07 -0700 | [diff] [blame] | 270 | return self(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 271 | } |
| 272 | |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 273 | /** |
Ian Zerny | 5b65b3f | 2017-12-22 13:37:45 +0100 | [diff] [blame] | 274 | * Set an output destination to which proguard-map content should be written. |
| 275 | * |
| 276 | * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link |
| 277 | * #setProguardMapConsumer}. Note that any subsequent call to this method or {@link |
| 278 | * #setProguardMapConsumer} will override the previous setting. |
| 279 | * |
| 280 | * @param proguardMapOutput File-system path to write output at. |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 281 | */ |
Morten Krogh-Jespersen | 946bea9 | 2021-03-18 20:29:42 +0100 | [diff] [blame] | 282 | @Override |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 283 | public Builder setProguardMapOutputPath(Path proguardMapOutput) { |
Morten Krogh-Jespersen | 946bea9 | 2021-03-18 20:29:42 +0100 | [diff] [blame] | 284 | return super.setProguardMapOutputPath(proguardMapOutput); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 285 | } |
| 286 | |
Ian Zerny | 5b65b3f | 2017-12-22 13:37:45 +0100 | [diff] [blame] | 287 | /** |
| 288 | * Set a consumer for receiving the proguard-map content. |
| 289 | * |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 290 | * <p>Note that any subsequent call to this method or {@link #setProguardMapOutputPath} will |
Ian Zerny | 5b65b3f | 2017-12-22 13:37:45 +0100 | [diff] [blame] | 291 | * override the previous setting. |
| 292 | * |
| 293 | * @param proguardMapConsumer Consumer to receive the content once produced. |
| 294 | */ |
Morten Krogh-Jespersen | 946bea9 | 2021-03-18 20:29:42 +0100 | [diff] [blame] | 295 | @Override |
Ian Zerny | 5b65b3f | 2017-12-22 13:37:45 +0100 | [diff] [blame] | 296 | public Builder setProguardMapConsumer(StringConsumer proguardMapConsumer) { |
Morten Krogh-Jespersen | 946bea9 | 2021-03-18 20:29:42 +0100 | [diff] [blame] | 297 | return super.setProguardMapConsumer(proguardMapConsumer); |
Tamas Kenez | 9f85561 | 2017-09-20 18:09:03 +0200 | [diff] [blame] | 298 | } |
| 299 | |
Søren Gjesse | 4d01502 | 2018-09-14 13:26:57 +0200 | [diff] [blame] | 300 | /** |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 301 | * Set a consumer for receiving the keep rules to use when compiling the desugared library for |
| 302 | * the program being compiled in this compilation. |
| 303 | * |
| 304 | * @param keepRuleConsumer Consumer to receive the content once produced. |
| 305 | */ |
| 306 | public Builder setDesugaredLibraryKeepRuleConsumer(StringConsumer keepRuleConsumer) { |
| 307 | this.desugaredLibraryKeepRuleConsumer = keepRuleConsumer; |
| 308 | return self(); |
| 309 | } |
| 310 | |
| 311 | /** |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 312 | * Set a consumer for receiving the proguard usage information. |
| 313 | * |
| 314 | * <p>Note that any subsequent calls to this method will replace the previous setting. |
| 315 | * |
| 316 | * @param proguardUsageConsumer Consumer to receive usage information. |
| 317 | */ |
| 318 | public Builder setProguardUsageConsumer(StringConsumer proguardUsageConsumer) { |
| 319 | this.proguardUsageConsumer = proguardUsageConsumer; |
| 320 | return self(); |
| 321 | } |
| 322 | |
| 323 | /** |
| 324 | * Set a consumer for receiving the proguard seeds information. |
| 325 | * |
| 326 | * <p>Note that any subsequent calls to this method will replace the previous setting. |
| 327 | * |
| 328 | * @param proguardSeedsConsumer Consumer to receive seeds information. |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 329 | */ |
| 330 | public Builder setProguardSeedsConsumer(StringConsumer proguardSeedsConsumer) { |
| 331 | this.proguardSeedsConsumer = proguardSeedsConsumer; |
| 332 | return self(); |
| 333 | } |
| 334 | |
| 335 | /** |
| 336 | * Set a consumer for receiving the proguard configuration information. |
| 337 | * |
| 338 | * <p>Note that any subsequent calls to this method will replace the previous setting. |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 339 | * @param proguardConfigurationConsumer |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 340 | */ |
| 341 | public Builder setProguardConfigurationConsumer(StringConsumer proguardConfigurationConsumer) { |
| 342 | this.proguardConfigurationConsumer = proguardConfigurationConsumer; |
| 343 | return self(); |
| 344 | } |
| 345 | |
| 346 | /** |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 347 | * Set a consumer for receiving kept-graph events. |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 348 | */ |
| 349 | public Builder setKeptGraphConsumer(GraphConsumer graphConsumer) { |
| 350 | this.keptGraphConsumer = graphConsumer; |
| 351 | return self(); |
| 352 | } |
| 353 | |
| 354 | /** |
| 355 | * Set a consumer for receiving kept-graph events for the content of the main-dex output. |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 356 | */ |
| 357 | public Builder setMainDexKeptGraphConsumer(GraphConsumer graphConsumer) { |
| 358 | this.mainDexKeptGraphConsumer = graphConsumer; |
| 359 | return self(); |
| 360 | } |
| 361 | |
| 362 | /** |
Ian Zerny | ec9566d | 2022-03-02 13:41:14 +0100 | [diff] [blame] | 363 | * Set a consumer for receiving dependency edges for files referenced from inputs. |
| 364 | * |
| 365 | * <p>Note that these dependency edges are for files read when reading/parsing other input |
| 366 | * files, such as the proguard configuration files. For compilation dependencies for incremental |
| 367 | * desugaring see {@code setDesugarGraphConsumer}. |
| 368 | */ |
| 369 | public Builder setInputDependencyGraphConsumer( |
| 370 | InputDependencyGraphConsumer inputDependencyGraphConsumer) { |
| 371 | this.inputDependencyGraphConsumer = inputDependencyGraphConsumer; |
| 372 | return self(); |
| 373 | } |
| 374 | |
| 375 | /** |
Søren Gjesse | 4d01502 | 2018-09-14 13:26:57 +0200 | [diff] [blame] | 376 | * Set the output path-and-mode. |
| 377 | * |
| 378 | * <p>Setting the output path-and-mode will override any previous set consumer or any previous |
| 379 | * output path-and-mode, and implicitly sets the appropriate program consumer to write the |
| 380 | * output. |
| 381 | * |
| 382 | * <p>By default data resources from the input will be included in the output. (see {@link |
| 383 | * #setOutput(Path, OutputMode, boolean) for details} |
| 384 | * |
| 385 | * @param outputPath Path to write the output to. Must be an archive or and existing directory. |
| 386 | * @param outputMode Mode in which to write the output. |
| 387 | */ |
| 388 | @Override |
| 389 | public Builder setOutput(Path outputPath, OutputMode outputMode) { |
| 390 | setOutput(outputPath, outputMode, true); |
| 391 | return self(); |
| 392 | } |
| 393 | |
| 394 | /** |
| 395 | * Set the output path-and-mode and control if data resources are included. |
| 396 | * |
| 397 | * <p>In addition to setting the output path-and-mode (see {@link #setOutput(Path, OutputMode)}) |
| 398 | * this can control if data resources should be included or not. |
| 399 | * |
| 400 | * <p>Data resources are non Java classfile items in the input. |
| 401 | * |
| 402 | * <p>If data resources are not included they are ignored in the input and will not produce |
| 403 | * anything in the output. If data resources are included they are processed according to the |
| 404 | * configuration and written to the output. |
| 405 | * |
| 406 | * @param outputPath Path to write the output to. Must be an archive or and existing directory. |
| 407 | * @param outputMode Mode in which to write the output. |
| 408 | * @param includeDataResources If data resources from the input should be included in the |
| 409 | * output. |
| 410 | */ |
| 411 | @Override |
| 412 | public Builder setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) { |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 413 | this.includeDataResources = Optional.of(includeDataResources); |
Søren Gjesse | 4d01502 | 2018-09-14 13:26:57 +0200 | [diff] [blame] | 414 | return super.setOutput(outputPath, outputMode, includeDataResources); |
| 415 | } |
| 416 | |
Ian Zerny | 113faac | 2017-12-14 12:58:23 +0100 | [diff] [blame] | 417 | @Override |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 418 | public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) { |
| 419 | return super.addProgramResourceProvider( |
| 420 | new EnsureNonDexProgramResourceProvider(programProvider)); |
| 421 | } |
| 422 | |
Rico Wind | 25f6eab | 2019-09-06 11:06:11 +0200 | [diff] [blame] | 423 | /** |
| 424 | * Add a {@link FeatureSplit} to the app. The {@link FeatureSplit} contains input and output |
| 425 | * providers, that enables us to generate dynamic apps with optional modules. |
| 426 | * |
| 427 | * @param featureSplitGenerator A function that uses the supplied {@link FeatureSplit.Builder} |
| 428 | * to generate a {@link FeatureSplit}. |
| 429 | */ |
| 430 | public Builder addFeatureSplit( |
| 431 | Function<FeatureSplit.Builder, FeatureSplit> featureSplitGenerator) { |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 432 | FeatureSplit featureSplit = featureSplitGenerator.apply(FeatureSplit.builder(getReporter())); |
| 433 | featureSplits.add(featureSplit); |
Rico Wind | 8b6d4aa | 2019-11-06 08:53:32 +0100 | [diff] [blame] | 434 | for (ProgramResourceProvider programResourceProvider : featureSplit |
| 435 | .getProgramResourceProviders()) { |
| 436 | // Data resources are handled separately and passed directly to the feature split consumer. |
| 437 | ProgramResourceProvider providerWithoutDataResources = new ProgramResourceProvider() { |
| 438 | @Override |
| 439 | public Collection<ProgramResource> getProgramResources() throws ResourceException { |
| 440 | return programResourceProvider.getProgramResources(); |
| 441 | } |
| 442 | |
| 443 | @Override |
| 444 | public DataResourceProvider getDataResourceProvider() { |
| 445 | return null; |
| 446 | } |
| 447 | }; |
| 448 | addProgramResourceProvider(providerWithoutDataResources); |
| 449 | } |
Rico Wind | 25f6eab | 2019-09-06 11:06:11 +0200 | [diff] [blame] | 450 | return self(); |
| 451 | } |
| 452 | |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 453 | /** |
| 454 | * Enable experimental/pre-release support for modeling missing library APIs. |
| 455 | * |
| 456 | * <p>This allows enabling the feature while it is still default disabled by the compiler. Once |
| 457 | * the feature is default enabled, calling this method will have no affect. |
| 458 | */ |
| 459 | @Deprecated |
| 460 | public Builder setEnableExperimentalMissingLibraryApiModeling(boolean enable) { |
| 461 | this.enableMissingLibraryApiModeling = enable; |
| 462 | return self(); |
| 463 | } |
| 464 | |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 465 | @Deprecated |
| 466 | public Builder setEnableExperimentalKeepAnnotations(boolean enable) { |
| 467 | this.enableExperimentalKeepAnnotations = enable; |
| 468 | return self(); |
| 469 | } |
| 470 | |
Søren Gjesse | 7320ce5 | 2018-05-07 15:45:22 +0200 | [diff] [blame] | 471 | @Override |
| 472 | protected InternalProgramOutputPathConsumer createProgramOutputConsumer( |
| 473 | Path path, |
| 474 | OutputMode mode, |
| 475 | boolean consumeDataResources) { |
Søren Gjesse | 4d01502 | 2018-09-14 13:26:57 +0200 | [diff] [blame] | 476 | return super.createProgramOutputConsumer(path, mode, consumeDataResources); |
Søren Gjesse | 7320ce5 | 2018-05-07 15:45:22 +0200 | [diff] [blame] | 477 | } |
| 478 | |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 479 | /** |
| 480 | * Add a collection of startup profile providers that should be used for distributing the |
| 481 | * program classes in dex. The given startup profiles are also used to disallow optimizations |
| 482 | * across the startup and post-startup boundary. |
| 483 | * |
| 484 | * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does |
| 485 | * not support native multidex (API<=20). |
| 486 | * |
| 487 | * <p>NOTE: This API is experimental and may be subject to changes. |
| 488 | */ |
| 489 | @Override |
| 490 | public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) { |
| 491 | return super.addStartupProfileProviders(startupProfileProviders); |
| 492 | } |
| 493 | |
| 494 | /** |
| 495 | * Add a collection of startup profile providers that should be used for distributing the |
| 496 | * program classes in dex. The given startup profiles are also used to disallow optimizations |
| 497 | * across the startup and post-startup boundary. |
| 498 | * |
| 499 | * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does |
| 500 | * not support native multidex (API<=20). |
| 501 | * |
| 502 | * <p>NOTE: This API is experimental and may be subject to changes. |
| 503 | */ |
| 504 | @Override |
| 505 | public Builder addStartupProfileProviders( |
| 506 | Collection<StartupProfileProvider> startupProfileProviders) { |
| 507 | return super.addStartupProfileProviders(startupProfileProviders); |
| 508 | } |
| 509 | |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 510 | @Override |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 511 | void validate() { |
Rico Wind | 5a6662a | 2020-08-27 08:39:21 +0200 | [diff] [blame] | 512 | if (isPrintHelp()) { |
| 513 | return; |
| 514 | } |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 515 | Reporter reporter = getReporter(); |
Ian Zerny | 113faac | 2017-12-14 12:58:23 +0100 | [diff] [blame] | 516 | if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { |
| 517 | reporter.error("R8 does not support compiling to a single DEX file per Java class file"); |
| 518 | } |
Mads Ager | d9ae2a8 | 2018-11-29 13:01:55 +0100 | [diff] [blame] | 519 | if (getMainDexListConsumer() != null |
Ian Zerny | 1b958eb | 2017-12-19 10:35:33 +0100 | [diff] [blame] | 520 | && mainDexRules.isEmpty() |
| 521 | && !getAppBuilder().hasMainDexList()) { |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 522 | reporter.error( |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 523 | "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list"); |
Søren Gjesse | c801ecc | 2017-08-03 13:40:06 +0200 | [diff] [blame] | 524 | } |
Ian Zerny | 1ca4148 | 2018-12-18 13:42:49 +0000 | [diff] [blame] | 525 | if (!(getProgramConsumer() instanceof ClassFileConsumer) |
| 526 | && getMinApiLevel() >= AndroidApiLevel.L.getLevel()) { |
| 527 | if (getMainDexListConsumer() != null |
| 528 | || !mainDexRules.isEmpty() |
| 529 | || getAppBuilder().hasMainDexList()) { |
| 530 | reporter.error( |
| 531 | "R8 does not support main-dex inputs and outputs when compiling to API level " |
| 532 | + AndroidApiLevel.L.getLevel() |
| 533 | + " and above"); |
| 534 | } |
| 535 | } |
Rico Wind | 25f6eab | 2019-09-06 11:06:11 +0200 | [diff] [blame] | 536 | for (FeatureSplit featureSplit : featureSplits) { |
| 537 | assert featureSplit.getProgramConsumer() instanceof DexIndexedConsumer; |
| 538 | if (!(getProgramConsumer() instanceof DexIndexedConsumer)) { |
| 539 | reporter.error("R8 does not support class file output when using feature splits"); |
| 540 | } |
| 541 | } |
| 542 | |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 543 | for (Path file : programFiles) { |
| 544 | if (FileUtils.isDexFile(file)) { |
| 545 | reporter.error(new StringDiagnostic( |
| 546 | "R8 does not support compiling DEX inputs", new PathOrigin(file))); |
| 547 | } |
| 548 | } |
Mathias Rav | 86d2602 | 2018-03-16 10:13:34 +0100 | [diff] [blame] | 549 | if (getProgramConsumer() instanceof ClassFileConsumer && isMinApiLevelSet()) { |
| 550 | reporter.error("R8 does not support --min-api when compiling to class files"); |
| 551 | } |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 552 | if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) { |
| 553 | reporter.error("Using desugared library configuration requires desugaring to be enabled"); |
Søren Gjesse | ad54aa5 | 2019-07-16 15:30:01 +0200 | [diff] [blame] | 554 | } |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 555 | super.validate(); |
Søren Gjesse | c801ecc | 2017-08-03 13:40:06 +0200 | [diff] [blame] | 556 | } |
| 557 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 558 | @Override |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 559 | R8Command makeCommand() { |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 560 | // If printing versions ignore everything else. |
| 561 | if (isPrintHelp() || isPrintVersion()) { |
| 562 | return new R8Command(isPrintHelp(), isPrintVersion()); |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 563 | } |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 564 | return makeR8Command(); |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 565 | } |
| 566 | |
Ian Zerny | e09adb7 | 2018-05-08 11:12:12 +0200 | [diff] [blame] | 567 | private R8Command makeR8Command() { |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 568 | Reporter reporter = getReporter(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 569 | DexItemFactory factory = new DexItemFactory(); |
Ian Zerny | 99683a3 | 2021-01-12 19:11:56 +0100 | [diff] [blame] | 570 | List<ProguardConfigurationRule> mainDexKeepRules = |
| 571 | ProguardConfigurationParser.parse(mainDexRules, factory, reporter); |
Tamas Kenez | 5677922 | 2017-10-13 15:16:45 +0200 | [diff] [blame] | 572 | |
Clément Béra | 24cb20a | 2022-02-17 09:30:14 +0000 | [diff] [blame] | 573 | DesugaredLibrarySpecification desugaredLibrarySpecification = |
clementbera | 79854ab | 2019-09-24 14:47:06 +0200 | [diff] [blame] | 574 | getDesugaredLibraryConfiguration(factory, false); |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 575 | |
Søren Gjesse | 9147505 | 2018-09-25 14:38:39 +0200 | [diff] [blame] | 576 | ProguardConfigurationParser parser = |
Ian Zerny | ec9566d | 2022-03-02 13:41:14 +0100 | [diff] [blame] | 577 | new ProguardConfigurationParser( |
Christoffer Quist Adamsen | 6e68954 | 2022-05-30 20:02:09 +0200 | [diff] [blame] | 578 | factory, reporter, parserOptionsBuilder.build(), inputDependencyGraphConsumer); |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 579 | if (!proguardConfigs.isEmpty()) { |
Yohann Roussel | f17d02d | 2017-11-29 18:08:09 +0100 | [diff] [blame] | 580 | parser.parse(proguardConfigs); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 581 | } |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 582 | ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder(); |
| 583 | configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 584 | |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 585 | if (getMode() == CompilationMode.DEBUG) { |
| 586 | disableMinification = true; |
| 587 | configurationBuilder.disableOptimization(); |
| 588 | } |
| 589 | |
Stephan Herhut | 1a9eee4 | 2018-01-31 10:51:10 +0100 | [diff] [blame] | 590 | if (disableTreeShaking) { |
| 591 | configurationBuilder.disableShrinking(); |
| 592 | } |
| 593 | |
| 594 | if (disableMinification) { |
| 595 | configurationBuilder.disableObfuscation(); |
| 596 | } |
| 597 | |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 598 | if (proguardConfigurationConsumerForTesting != null) { |
| 599 | proguardConfigurationConsumerForTesting.accept(configurationBuilder); |
| 600 | } |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 601 | |
| 602 | // Add embedded keep rules. |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 603 | amendWithRulesAndProvidersForInjarsAndMetaInf(reporter, parser); |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 604 | |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 605 | // Extract out rules for keep annotations and amend the configuration. |
| 606 | // TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer. |
| 607 | extractKeepAnnotationRules(parser); |
Tamas Kenez | 5677922 | 2017-10-13 15:16:45 +0200 | [diff] [blame] | 608 | ProguardConfiguration configuration = configurationBuilder.build(); |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 609 | getAppBuilder().addFilteredLibraryArchives(configuration.getLibraryjars()); |
Tamas Kenez | 5677922 | 2017-10-13 15:16:45 +0200 | [diff] [blame] | 610 | |
Ian Zerny | 113faac | 2017-12-14 12:58:23 +0100 | [diff] [blame] | 611 | assert getProgramConsumer() != null; |
| 612 | |
Søren Gjesse | 9a7876d | 2020-01-13 14:32:26 +0100 | [diff] [blame] | 613 | DesugarState desugaring = |
| 614 | (getProgramConsumer() instanceof ClassFileConsumer) |
| 615 | ? DesugarState.OFF |
| 616 | : getDesugaringState(); |
Mathias Rav | 1d6ef62 | 2018-02-27 11:57:36 +0100 | [diff] [blame] | 617 | |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 618 | FeatureSplitConfiguration featureSplitConfiguration = |
Christoffer Quist Adamsen | c94779d | 2020-09-10 08:20:41 +0200 | [diff] [blame] | 619 | !featureSplits.isEmpty() ? new FeatureSplitConfiguration(featureSplits) : null; |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 620 | |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 621 | R8Command command = |
| 622 | new R8Command( |
| 623 | getAppBuilder().build(), |
Ian Zerny | 113faac | 2017-12-14 12:58:23 +0100 | [diff] [blame] | 624 | getProgramConsumer(), |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 625 | mainDexKeepRules, |
Mads Ager | d9ae2a8 | 2018-11-29 13:01:55 +0100 | [diff] [blame] | 626 | getMainDexListConsumer(), |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 627 | configuration, |
| 628 | getMode(), |
| 629 | getMinApiLevel(), |
| 630 | reporter, |
Mathias Rav | 1d6ef62 | 2018-02-27 11:57:36 +0100 | [diff] [blame] | 631 | desugaring, |
Stephan Herhut | 1a9eee4 | 2018-01-31 10:51:10 +0100 | [diff] [blame] | 632 | configuration.isShrinking(), |
| 633 | configuration.isObfuscating(), |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 634 | disableVerticalClassMerging, |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 635 | forceProguardCompatibility, |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 636 | includeDataResources, |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 637 | proguardMapConsumer, |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 638 | proguardUsageConsumer, |
| 639 | proguardSeedsConsumer, |
| 640 | proguardConfigurationConsumer, |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 641 | keptGraphConsumer, |
| 642 | mainDexKeptGraphConsumer, |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 643 | syntheticProguardRulesConsumer, |
Søren Gjesse | ad54aa5 | 2019-07-16 15:30:01 +0200 | [diff] [blame] | 644 | isOptimizeMultidexForLinearAlloc(), |
Alan Leung | 4b5db8e | 2019-07-23 13:53:10 -0700 | [diff] [blame] | 645 | getIncludeClassesChecksum(), |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 646 | getDexClassChecksumFilter(), |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 647 | desugaredLibraryKeepRuleConsumer, |
Clément Béra | ddd19a1 | 2021-12-09 08:14:53 +0000 | [diff] [blame] | 648 | desugaredLibrarySpecification, |
Søren Gjesse | 95fce5f | 2019-12-16 10:33:51 +0100 | [diff] [blame] | 649 | featureSplitConfiguration, |
Ian Zerny | 8f3ba44 | 2020-03-11 16:32:27 +0100 | [diff] [blame] | 650 | getAssertionsConfiguration(), |
Søren Gjesse | 943389f | 2020-03-13 10:40:25 +0100 | [diff] [blame] | 651 | getOutputInspections(), |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 652 | synthesizedClassPrefix, |
Søren Gjesse | 75a0979 | 2021-04-07 12:41:33 +0200 | [diff] [blame] | 653 | getThreadCount(), |
Ian Zerny | b23c47c | 2021-10-06 08:00:19 +0200 | [diff] [blame] | 654 | getDumpInputFlags(), |
Ian Zerny | 1433d58 | 2021-10-12 09:44:48 +0200 | [diff] [blame] | 655 | getMapIdProvider(), |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 656 | getSourceFileProvider(), |
Ian Zerny | bbd2392 | 2022-06-07 10:33:31 +0200 | [diff] [blame] | 657 | enableMissingLibraryApiModeling, |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 658 | getAndroidPlatformBuild(), |
Christoffer Quist Adamsen | dafb046 | 2022-09-01 12:37:09 +0200 | [diff] [blame] | 659 | getArtProfilesForRewriting(), |
Ian Zerny | 7744006 | 2022-08-26 13:49:08 +0200 | [diff] [blame] | 660 | getStartupProfileProviders(), |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 661 | getClassConflictResolver(), |
| 662 | fakeCompilerVersion); |
Ian Zerny | ec9566d | 2022-03-02 13:41:14 +0100 | [diff] [blame] | 663 | |
| 664 | if (inputDependencyGraphConsumer != null) { |
| 665 | inputDependencyGraphConsumer.finished(); |
| 666 | } |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 667 | return command; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 668 | } |
Ian Zerny | 4479c73 | 2018-01-04 14:04:56 +0100 | [diff] [blame] | 669 | |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 670 | private void amendWithRulesAndProvidersForInjarsAndMetaInf( |
| 671 | Reporter reporter, ProguardConfigurationParser parser) { |
| 672 | |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 673 | // Since -injars can itself reference archives with rules and that in turn have -injars the |
| 674 | // completion of amending rules and providers must run in a fixed point. The fixed point is |
| 675 | // reached once the injars set is stable. |
| 676 | Set<FilteredClassPath> seenInjars = SetUtils.newIdentityHashSet(); |
| 677 | Deque<ProgramResourceProvider> providers = |
| 678 | new ArrayDeque<>(getAppBuilder().getProgramResourceProviders()); |
| 679 | while (true) { |
| 680 | for (FilteredClassPath injar : parser.getConfigurationBuilder().getInjars()) { |
| 681 | if (seenInjars.add(injar)) { |
| 682 | ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar); |
Ian Zerny | 3fcd2bd | 2023-01-30 20:14:12 +0100 | [diff] [blame] | 683 | if (provider != null) { |
| 684 | providers.add(provider); |
| 685 | } |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 686 | } |
| 687 | } |
| 688 | if (providers.isEmpty()) { |
| 689 | return; |
| 690 | } |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 691 | |
| 692 | Supplier<SemanticVersion> semanticVersionSupplier = |
| 693 | Suppliers.memoize( |
| 694 | () -> { |
| 695 | SemanticVersion compilerVersion = |
| 696 | fakeCompilerVersion == null |
| 697 | ? SemanticVersion.create( |
| 698 | Version.getMajorVersion(), |
| 699 | Version.getMinorVersion(), |
| 700 | Version.getPatchVersion()) |
| 701 | : fakeCompilerVersion; |
| 702 | if (compilerVersion.getMajor() < 0) { |
Søren Gjesse | 6402f44 | 2023-02-14 12:44:53 +0100 | [diff] [blame] | 703 | compilerVersion = |
| 704 | SemanticVersion.create( |
| 705 | Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 706 | reporter.warning( |
| 707 | "Running R8 version " |
| 708 | + Version.getVersionString() |
Søren Gjesse | 6402f44 | 2023-02-14 12:44:53 +0100 | [diff] [blame] | 709 | + ", which cannot be represented as a semantic version. Using" |
| 710 | + " an artificial version newer than any known version for selecting" |
| 711 | + " Proguard configurations embedded under META-INF/. This means that" |
| 712 | + " all rules with a '-max-' qualifier will be excluded and all rules" |
| 713 | + " with a -min- qualifier will be included."); |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 714 | } |
| 715 | return compilerVersion; |
| 716 | }); |
| 717 | |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 718 | while (!providers.isEmpty()) { |
| 719 | DataResourceProvider dataResourceProvider = providers.pop().getDataResourceProvider(); |
| 720 | if (dataResourceProvider != null) { |
| 721 | try { |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 722 | ExtractEmbeddedRules embeddedProguardConfigurationVisitor = |
| 723 | new ExtractEmbeddedRules(reporter, semanticVersionSupplier); |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 724 | dataResourceProvider.accept(embeddedProguardConfigurationVisitor); |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 725 | embeddedProguardConfigurationVisitor.parseRelevantRules(parser); |
Ian Zerny | 140fad6 | 2023-01-27 10:33:19 +0100 | [diff] [blame] | 726 | } catch (ResourceException e) { |
| 727 | reporter.error(new ExceptionDiagnostic(e)); |
| 728 | } |
| 729 | } |
| 730 | } |
| 731 | } |
| 732 | } |
| 733 | |
Ian Zerny | c158285 | 2023-01-30 13:45:36 +0100 | [diff] [blame] | 734 | private void extractKeepAnnotationRules(ProguardConfigurationParser parser) { |
| 735 | if (!enableExperimentalKeepAnnotations) { |
| 736 | return; |
| 737 | } |
| 738 | try { |
| 739 | for (ProgramResourceProvider provider : getAppBuilder().getProgramResourceProviders()) { |
| 740 | for (ProgramResource resource : provider.getProgramResources()) { |
| 741 | if (resource.getKind() == Kind.CF) { |
| 742 | Set<KeepEdge> edges = KeepEdgeReader.readKeepEdges(resource.getBytes()); |
| 743 | KeepRuleExtractor extractor = |
| 744 | new KeepRuleExtractor( |
| 745 | rule -> { |
| 746 | ProguardConfigurationSourceStrings source = |
| 747 | new ProguardConfigurationSourceStrings( |
| 748 | Collections.singletonList(rule), null, resource.getOrigin()); |
| 749 | parser.parse(source); |
| 750 | }); |
| 751 | edges.forEach(extractor::extract); |
| 752 | } |
| 753 | } |
| 754 | } |
| 755 | } catch (ResourceException e) { |
| 756 | throw getAppBuilder().getReporter().fatalError(new ExceptionDiagnostic(e)); |
| 757 | } |
| 758 | } |
| 759 | |
Ian Zerny | 4479c73 | 2018-01-04 14:04:56 +0100 | [diff] [blame] | 760 | // Internal for-testing method to add post-processors of the proguard configuration. |
| 761 | void addProguardConfigurationConsumerForTesting(Consumer<ProguardConfiguration.Builder> c) { |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 762 | Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumerForTesting; |
| 763 | proguardConfigurationConsumerForTesting = |
Ian Zerny | 4479c73 | 2018-01-04 14:04:56 +0100 | [diff] [blame] | 764 | builder -> { |
| 765 | if (oldConsumer != null) { |
| 766 | oldConsumer.accept(builder); |
| 767 | } |
| 768 | c.accept(builder); |
| 769 | }; |
| 770 | } |
Søren Gjesse | c9e940f | 2018-05-28 12:56:56 +0200 | [diff] [blame] | 771 | |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 772 | void addSyntheticProguardRulesConsumerForTesting( |
| 773 | Consumer<List<ProguardConfigurationRule>> consumer) { |
| 774 | syntheticProguardRulesConsumer = |
| 775 | syntheticProguardRulesConsumer == null |
| 776 | ? consumer |
| 777 | : syntheticProguardRulesConsumer.andThen(consumer); |
| 778 | |
| 779 | } |
| 780 | |
Christoffer Quist Adamsen | 62971ce | 2022-06-03 09:53:36 +0200 | [diff] [blame] | 781 | void setEnableExperimentalCheckEnumUnboxed() { |
| 782 | parserOptionsBuilder.setEnableExperimentalCheckEnumUnboxed(true); |
| 783 | } |
| 784 | |
Christoffer Quist Adamsen | 067d98a | 2022-06-01 14:13:38 +0200 | [diff] [blame] | 785 | void setEnableExperimentalConvertCheckNotNull() { |
| 786 | parserOptionsBuilder.setEnableExperimentalConvertCheckNotNull(true); |
| 787 | } |
| 788 | |
Christoffer Quist Adamsen | 6e68954 | 2022-05-30 20:02:09 +0200 | [diff] [blame] | 789 | void setEnableExperimentalWhyAreYouNotInlining() { |
| 790 | parserOptionsBuilder.setEnableExperimentalWhyAreYouNotInlining(true); |
| 791 | } |
| 792 | |
Søren Gjesse | c08521d | 2018-07-05 16:08:02 +0200 | [diff] [blame] | 793 | // Internal for-testing method to allow proguard options only available for testing. |
Christoffer Quist Adamsen | 6e68954 | 2022-05-30 20:02:09 +0200 | [diff] [blame] | 794 | void setEnableTestProguardOptions() { |
| 795 | parserOptionsBuilder.setEnableTestingOptions(true); |
Søren Gjesse | c08521d | 2018-07-05 16:08:02 +0200 | [diff] [blame] | 796 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 797 | } |
| 798 | |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 799 | // Wrapper class to ensure that R8 does not allow DEX as program inputs. |
| 800 | private static class EnsureNonDexProgramResourceProvider implements ProgramResourceProvider { |
| 801 | |
| 802 | final ProgramResourceProvider provider; |
| 803 | |
| 804 | public EnsureNonDexProgramResourceProvider(ProgramResourceProvider provider) { |
| 805 | this.provider = provider; |
| 806 | } |
| 807 | |
| 808 | @Override |
| 809 | public Collection<ProgramResource> getProgramResources() throws ResourceException { |
| 810 | Collection<ProgramResource> resources = provider.getProgramResources(); |
| 811 | for (ProgramResource resource : resources) { |
| 812 | if (resource.getKind() == Kind.DEX) { |
| 813 | throw new ResourceException(resource.getOrigin(), |
| 814 | "R8 does not support compiling DEX inputs"); |
| 815 | } |
| 816 | } |
| 817 | return resources; |
| 818 | } |
Søren Gjesse | b72b743 | 2018-05-16 09:48:56 +0200 | [diff] [blame] | 819 | |
| 820 | @Override |
| 821 | public DataResourceProvider getDataResourceProvider() { |
| 822 | return provider.getDataResourceProvider(); |
| 823 | } |
Ian Zerny | 1fede15 | 2018-01-05 07:28:29 +0100 | [diff] [blame] | 824 | } |
| 825 | |
Ian Zerny | aefe7bc | 2022-05-04 11:28:19 +0200 | [diff] [blame] | 826 | static String getUsageMessage() { |
| 827 | return R8CommandParser.getUsageMessage(); |
| 828 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 829 | |
Morten Krogh-Jespersen | 3ebe216 | 2019-01-10 11:04:46 +0100 | [diff] [blame] | 830 | private final List<ProguardConfigurationRule> mainDexKeepRules; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 831 | private final ProguardConfiguration proguardConfiguration; |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 832 | private final boolean enableTreeShaking; |
| 833 | private final boolean enableMinification; |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 834 | private final boolean disableVerticalClassMerging; |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 835 | private final boolean forceProguardCompatibility; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 836 | private final Optional<Boolean> includeDataResources; |
Ian Zerny | 3c34a7a | 2017-12-06 11:05:00 +0100 | [diff] [blame] | 837 | private final StringConsumer proguardMapConsumer; |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 838 | private final StringConsumer proguardUsageConsumer; |
| 839 | private final StringConsumer proguardSeedsConsumer; |
| 840 | private final StringConsumer proguardConfigurationConsumer; |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 841 | private final GraphConsumer keptGraphConsumer; |
| 842 | private final GraphConsumer mainDexKeptGraphConsumer; |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 843 | private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer; |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 844 | private final StringConsumer desugaredLibraryKeepRuleConsumer; |
Clément Béra | 24cb20a | 2022-02-17 09:30:14 +0000 | [diff] [blame] | 845 | private final DesugaredLibrarySpecification desugaredLibrarySpecification; |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 846 | private final FeatureSplitConfiguration featureSplitConfiguration; |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 847 | private final String synthesizedClassPrefix; |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 848 | private final boolean enableMissingLibraryApiModeling; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 849 | private final SemanticVersion fakeCompilerVersion; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 850 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 851 | /** Get a new {@link R8Command.Builder}. */ |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 852 | public static Builder builder() { |
| 853 | return new Builder(); |
| 854 | } |
| 855 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 856 | /** Get a new {@link R8Command.Builder} using a custom defined diagnostics handler. */ |
Søren Gjesse | 8fb83c8 | 2017-12-01 08:00:10 +0100 | [diff] [blame] | 857 | public static Builder builder(DiagnosticsHandler diagnosticsHandler) { |
| 858 | return new Builder(diagnosticsHandler); |
| 859 | } |
| 860 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 861 | // Internal builder to start from an existing AndroidApp. |
| 862 | static Builder builder(AndroidApp app) { |
| 863 | return new Builder(app); |
| 864 | } |
| 865 | |
Christoffer Quist Adamsen | 0eef4b0 | 2018-08-01 10:29:47 +0200 | [diff] [blame] | 866 | // Internal builder to start from an existing AndroidApp. |
| 867 | static Builder builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) { |
| 868 | return new Builder(app, diagnosticsHandler); |
| 869 | } |
| 870 | |
Ian Zerny | 113faac | 2017-12-14 12:58:23 +0100 | [diff] [blame] | 871 | /** |
| 872 | * Parse the R8 command-line. |
| 873 | * |
| 874 | * Parsing will set the supplied options or their default value if they have any. |
| 875 | * |
| 876 | * @param args Command-line arguments array. |
| 877 | * @param origin Origin description of the command-line arguments. |
| 878 | * @return R8 command builder with state set up according to parsed command line. |
| 879 | */ |
| 880 | public static Builder parse(String[] args, Origin origin) { |
Mathias Rav | a2a64f1 | 2018-05-29 12:52:41 +0200 | [diff] [blame] | 881 | return R8CommandParser.parse(args, origin); |
Ian Zerny | baaad9f | 2018-01-09 07:45:24 +0100 | [diff] [blame] | 882 | } |
| 883 | |
| 884 | /** |
| 885 | * Parse the R8 command-line. |
| 886 | * |
| 887 | * Parsing will set the supplied options or their default value if they have any. |
| 888 | * |
| 889 | * @param args Command-line arguments array. |
| 890 | * @param origin Origin description of the command-line arguments. |
| 891 | * @param handler Custom defined diagnostics handler. |
| 892 | * @return R8 command builder with state set up according to parsed command line. |
| 893 | */ |
| 894 | public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) { |
Mathias Rav | a2a64f1 | 2018-05-29 12:52:41 +0200 | [diff] [blame] | 895 | return R8CommandParser.parse(args, origin, handler); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 896 | } |
| 897 | |
Ian Zerny | aefe7bc | 2022-05-04 11:28:19 +0200 | [diff] [blame] | 898 | /** Get the help description for the R8 supported flags. */ |
| 899 | public static List<ParseFlagInfo> getParseFlagsInformation() { |
| 900 | return ImmutableList.copyOf(R8CommandParser.getFlags()); |
| 901 | } |
| 902 | |
Ian Zerny | baaad9f | 2018-01-09 07:45:24 +0100 | [diff] [blame] | 903 | private R8Command( |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 904 | AndroidApp inputApp, |
Ian Zerny | 7efd28d | 2017-12-12 09:17:42 +0100 | [diff] [blame] | 905 | ProgramConsumer programConsumer, |
Morten Krogh-Jespersen | 3ebe216 | 2019-01-10 11:04:46 +0100 | [diff] [blame] | 906 | List<ProguardConfigurationRule> mainDexKeepRules, |
Ian Zerny | 7c5c4f7 | 2017-12-06 13:37:07 +0100 | [diff] [blame] | 907 | StringConsumer mainDexListConsumer, |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 908 | ProguardConfiguration proguardConfiguration, |
| 909 | CompilationMode mode, |
| 910 | int minApiLevel, |
Yohann Roussel | 9a8d437 | 2017-11-21 14:36:44 +0100 | [diff] [blame] | 911 | Reporter reporter, |
Søren Gjesse | 9a7876d | 2020-01-13 14:32:26 +0100 | [diff] [blame] | 912 | DesugarState enableDesugaring, |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 913 | boolean enableTreeShaking, |
| 914 | boolean enableMinification, |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 915 | boolean disableVerticalClassMerging, |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 916 | boolean forceProguardCompatibility, |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 917 | Optional<Boolean> includeDataResources, |
Ian Zerny | 3c34a7a | 2017-12-06 11:05:00 +0100 | [diff] [blame] | 918 | StringConsumer proguardMapConsumer, |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 919 | StringConsumer proguardUsageConsumer, |
| 920 | StringConsumer proguardSeedsConsumer, |
| 921 | StringConsumer proguardConfigurationConsumer, |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 922 | GraphConsumer keptGraphConsumer, |
| 923 | GraphConsumer mainDexKeptGraphConsumer, |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 924 | Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer, |
Søren Gjesse | ad54aa5 | 2019-07-16 15:30:01 +0200 | [diff] [blame] | 925 | boolean optimizeMultidexForLinearAlloc, |
Alan Leung | 4b5db8e | 2019-07-23 13:53:10 -0700 | [diff] [blame] | 926 | boolean encodeChecksum, |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 927 | BiPredicate<String, Long> dexClassChecksumFilter, |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 928 | StringConsumer desugaredLibraryKeepRuleConsumer, |
Clément Béra | 24cb20a | 2022-02-17 09:30:14 +0000 | [diff] [blame] | 929 | DesugaredLibrarySpecification desugaredLibrarySpecification, |
Søren Gjesse | 95fce5f | 2019-12-16 10:33:51 +0100 | [diff] [blame] | 930 | FeatureSplitConfiguration featureSplitConfiguration, |
Ian Zerny | 8f3ba44 | 2020-03-11 16:32:27 +0100 | [diff] [blame] | 931 | List<AssertionsConfiguration> assertionsConfiguration, |
Søren Gjesse | 943389f | 2020-03-13 10:40:25 +0100 | [diff] [blame] | 932 | List<Consumer<Inspector>> outputInspections, |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 933 | String synthesizedClassPrefix, |
Søren Gjesse | 75a0979 | 2021-04-07 12:41:33 +0200 | [diff] [blame] | 934 | int threadCount, |
Ian Zerny | b23c47c | 2021-10-06 08:00:19 +0200 | [diff] [blame] | 935 | DumpInputFlags dumpInputFlags, |
Ian Zerny | 1433d58 | 2021-10-12 09:44:48 +0200 | [diff] [blame] | 936 | MapIdProvider mapIdProvider, |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 937 | SourceFileProvider sourceFileProvider, |
Ian Zerny | bbd2392 | 2022-06-07 10:33:31 +0200 | [diff] [blame] | 938 | boolean enableMissingLibraryApiModeling, |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 939 | boolean isAndroidPlatformBuild, |
Christoffer Quist Adamsen | dafb046 | 2022-09-01 12:37:09 +0200 | [diff] [blame] | 940 | List<ArtProfileForRewriting> artProfilesForRewriting, |
Ian Zerny | 7744006 | 2022-08-26 13:49:08 +0200 | [diff] [blame] | 941 | List<StartupProfileProvider> startupProfileProviders, |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 942 | ClassConflictResolver classConflictResolver, |
| 943 | SemanticVersion fakeCompilerVersion) { |
Søren Gjesse | cbeae78 | 2019-05-21 14:14:25 +0200 | [diff] [blame] | 944 | super( |
| 945 | inputApp, |
| 946 | mode, |
| 947 | programConsumer, |
| 948 | mainDexListConsumer, |
| 949 | minApiLevel, |
| 950 | reporter, |
| 951 | enableDesugaring, |
Søren Gjesse | ad54aa5 | 2019-07-16 15:30:01 +0200 | [diff] [blame] | 952 | optimizeMultidexForLinearAlloc, |
Alan Leung | 4b5db8e | 2019-07-23 13:53:10 -0700 | [diff] [blame] | 953 | encodeChecksum, |
Søren Gjesse | 95fce5f | 2019-12-16 10:33:51 +0100 | [diff] [blame] | 954 | dexClassChecksumFilter, |
Ian Zerny | 8f3ba44 | 2020-03-11 16:32:27 +0100 | [diff] [blame] | 955 | assertionsConfiguration, |
Søren Gjesse | 943389f | 2020-03-13 10:40:25 +0100 | [diff] [blame] | 956 | outputInspections, |
Søren Gjesse | 75a0979 | 2021-04-07 12:41:33 +0200 | [diff] [blame] | 957 | threadCount, |
Ian Zerny | b23c47c | 2021-10-06 08:00:19 +0200 | [diff] [blame] | 958 | dumpInputFlags, |
Ian Zerny | 1433d58 | 2021-10-12 09:44:48 +0200 | [diff] [blame] | 959 | mapIdProvider, |
Ian Zerny | bbd2392 | 2022-06-07 10:33:31 +0200 | [diff] [blame] | 960 | sourceFileProvider, |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 961 | isAndroidPlatformBuild, |
Christoffer Quist Adamsen | dafb046 | 2022-09-01 12:37:09 +0200 | [diff] [blame] | 962 | artProfilesForRewriting, |
Ian Zerny | 7744006 | 2022-08-26 13:49:08 +0200 | [diff] [blame] | 963 | startupProfileProviders, |
| 964 | classConflictResolver); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 965 | assert proguardConfiguration != null; |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 966 | assert mainDexKeepRules != null; |
| 967 | this.mainDexKeepRules = mainDexKeepRules; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 968 | this.proguardConfiguration = proguardConfiguration; |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 969 | this.enableTreeShaking = enableTreeShaking; |
| 970 | this.enableMinification = enableMinification; |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 971 | this.disableVerticalClassMerging = disableVerticalClassMerging; |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 972 | this.forceProguardCompatibility = forceProguardCompatibility; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 973 | this.includeDataResources = includeDataResources; |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 974 | this.proguardMapConsumer = proguardMapConsumer; |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 975 | this.proguardUsageConsumer = proguardUsageConsumer; |
| 976 | this.proguardSeedsConsumer = proguardSeedsConsumer; |
| 977 | this.proguardConfigurationConsumer = proguardConfigurationConsumer; |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 978 | this.keptGraphConsumer = keptGraphConsumer; |
| 979 | this.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer; |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 980 | this.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer; |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 981 | this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; |
Clément Béra | ddd19a1 | 2021-12-09 08:14:53 +0000 | [diff] [blame] | 982 | this.desugaredLibrarySpecification = desugaredLibrarySpecification; |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 983 | this.featureSplitConfiguration = featureSplitConfiguration; |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 984 | this.synthesizedClassPrefix = synthesizedClassPrefix; |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 985 | this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 986 | this.fakeCompilerVersion = fakeCompilerVersion; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 987 | } |
| 988 | |
| 989 | private R8Command(boolean printHelp, boolean printVersion) { |
| 990 | super(printHelp, printVersion); |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 991 | mainDexKeepRules = ImmutableList.of(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 992 | proguardConfiguration = null; |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 993 | enableTreeShaking = false; |
| 994 | enableMinification = false; |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 995 | disableVerticalClassMerging = false; |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 996 | forceProguardCompatibility = false; |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 997 | includeDataResources = null; |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 998 | proguardMapConsumer = null; |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 999 | proguardUsageConsumer = null; |
| 1000 | proguardSeedsConsumer = null; |
| 1001 | proguardConfigurationConsumer = null; |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 1002 | keptGraphConsumer = null; |
| 1003 | mainDexKeptGraphConsumer = null; |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 1004 | syntheticProguardRulesConsumer = null; |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 1005 | desugaredLibraryKeepRuleConsumer = null; |
Clément Béra | ddd19a1 | 2021-12-09 08:14:53 +0000 | [diff] [blame] | 1006 | desugaredLibrarySpecification = null; |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 1007 | featureSplitConfiguration = null; |
Clément Béra | 6cbeb55 | 2020-03-18 09:20:18 +0000 | [diff] [blame] | 1008 | synthesizedClassPrefix = null; |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 1009 | enableMissingLibraryApiModeling = false; |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 1010 | fakeCompilerVersion = null; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1011 | } |
Ian Zerny | 7efd28d | 2017-12-12 09:17:42 +0100 | [diff] [blame] | 1012 | |
Christoffer Quist Adamsen | cf19759 | 2021-12-02 16:03:30 +0100 | [diff] [blame] | 1013 | public DexItemFactory getDexItemFactory() { |
| 1014 | return proguardConfiguration.getDexItemFactory(); |
| 1015 | } |
| 1016 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 1017 | /** Get the enable-tree-shaking state. */ |
| 1018 | public boolean getEnableTreeShaking() { |
| 1019 | return enableTreeShaking; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1020 | } |
| 1021 | |
Ian Zerny | afb08e1 | 2018-01-08 14:22:58 +0100 | [diff] [blame] | 1022 | /** Get the enable-minification state. */ |
| 1023 | public boolean getEnableMinification() { |
| 1024 | return enableMinification; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1025 | } |
| 1026 | |
Ian Zerny | 797abef | 2022-04-01 15:39:02 +0200 | [diff] [blame] | 1027 | /** Get the Proguard compatibility state. */ |
| 1028 | public boolean getProguardCompatibility() { |
| 1029 | return forceProguardCompatibility; |
| 1030 | } |
| 1031 | |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 1032 | @Override |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1033 | InternalOptions getInternalOptions() { |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1034 | InternalOptions internal = new InternalOptions(getMode(), proguardConfiguration, getReporter()); |
Søren Gjesse | 5e984c0 | 2019-05-29 14:40:27 +0200 | [diff] [blame] | 1035 | assert !internal.testing.allowOutlinerInterfaceArrayArguments; // Only allow in tests. |
Ian Zerny | 7efd28d | 2017-12-12 09:17:42 +0100 | [diff] [blame] | 1036 | internal.programConsumer = getProgramConsumer(); |
Christoffer Quist Adamsen | 9780d7a | 2021-11-15 11:14:34 +0100 | [diff] [blame] | 1037 | internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel())); |
Søren Gjesse | 9a7876d | 2020-01-13 14:32:26 +0100 | [diff] [blame] | 1038 | internal.desugarState = getDesugarState(); |
Jinseong Jeon | d42bc0b | 2019-04-03 10:10:28 -0700 | [diff] [blame] | 1039 | assert internal.isShrinking() == getEnableTreeShaking(); |
| 1040 | assert internal.isMinifying() == getEnableMinification(); |
Søren Gjesse | fb882ae | 2017-05-29 09:17:46 +0200 | [diff] [blame] | 1041 | assert !internal.ignoreMissingClasses; |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 1042 | internal.ignoreMissingClasses = |
| 1043 | proguardConfiguration.isIgnoreWarnings() |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 1044 | || (forceProguardCompatibility |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1045 | && !internal.isOptimizing() |
clementbera | 75174ac | 2019-09-04 09:14:44 +0200 | [diff] [blame] | 1046 | && !internal.isShrinking() |
| 1047 | && !internal.isMinifying()); |
Søren Gjesse | 9842870 | 2017-10-26 12:27:38 +0200 | [diff] [blame] | 1048 | |
Søren Gjesse | fb882ae | 2017-05-29 09:17:46 +0200 | [diff] [blame] | 1049 | assert !internal.verbose; |
Yohann Roussel | f820a57 | 2017-05-31 20:25:51 +0200 | [diff] [blame] | 1050 | internal.mainDexKeepRules = mainDexKeepRules; |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1051 | internal.minimalMainDex = internal.debug; |
Mads Ager | d9ae2a8 | 2018-11-29 13:01:55 +0100 | [diff] [blame] | 1052 | internal.mainDexListConsumer = getMainDexListConsumer(); |
Tamas Kenez | 3b5829a | 2017-12-18 11:22:06 +0100 | [diff] [blame] | 1053 | internal.lineNumberOptimization = |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1054 | (internal.isOptimizing() || internal.isMinifying()) |
Tamas Kenez | 8779582 | 2018-08-23 15:42:33 +0200 | [diff] [blame] | 1055 | ? LineNumberOptimization.ON |
| 1056 | : LineNumberOptimization.OFF; |
Ian Zerny | 7c5c4f7 | 2017-12-06 13:37:07 +0100 | [diff] [blame] | 1057 | |
Christoffer Quist Adamsen | 0cc904a | 2021-05-11 11:31:58 +0200 | [diff] [blame] | 1058 | HorizontalClassMergerOptions horizontalClassMergerOptions = |
| 1059 | internal.horizontalClassMergerOptions(); |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1060 | assert internal.isOptimizing() || horizontalClassMergerOptions.isRestrictedToSynthetics(); |
Christoffer Quist Adamsen | 0cc904a | 2021-05-11 11:31:58 +0200 | [diff] [blame] | 1061 | |
Christoffer Quist Adamsen | 60a4ee7 | 2019-05-09 21:43:25 +0200 | [diff] [blame] | 1062 | assert !internal.enableTreeShakingOfLibraryMethodOverrides; |
Ian Zerny | f828e2a | 2021-10-11 13:45:39 +0200 | [diff] [blame] | 1063 | assert internal.enableVerticalClassMerging || !internal.isOptimizing(); |
Christoffer Quist Adamsen | 0cc904a | 2021-05-11 11:31:58 +0200 | [diff] [blame] | 1064 | |
Clément Béra | 9700fa0 | 2020-05-14 07:25:34 +0000 | [diff] [blame] | 1065 | if (!internal.isShrinking()) { |
Christoffer Quist Adamsen | 64b7ae0 | 2020-10-25 07:47:59 +0100 | [diff] [blame] | 1066 | // If R8 is not shrinking, there is no point in running various optimizations since the |
| 1067 | // optimized classes will still remain in the program (the application size could increase). |
Clément Béra | 9700fa0 | 2020-05-14 07:25:34 +0000 | [diff] [blame] | 1068 | internal.enableEnumUnboxing = false; |
Christoffer Quist Adamsen | 64b7ae0 | 2020-10-25 07:47:59 +0100 | [diff] [blame] | 1069 | internal.enableVerticalClassMerging = false; |
Clément Béra | 9700fa0 | 2020-05-14 07:25:34 +0000 | [diff] [blame] | 1070 | } |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 1071 | |
| 1072 | // Amend the proguard-map consumer with options from the proguard configuration. |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 1073 | internal.proguardMapConsumer = |
| 1074 | wrapStringConsumer( |
| 1075 | proguardMapConsumer, |
| 1076 | proguardConfiguration.isPrintMapping(), |
| 1077 | proguardConfiguration.getPrintMappingFile()); |
| 1078 | |
| 1079 | // Amend the usage information consumer with options from the proguard configuration. |
| 1080 | internal.usageInformationConsumer = |
| 1081 | wrapStringConsumer( |
| 1082 | proguardUsageConsumer, |
| 1083 | proguardConfiguration.isPrintUsage(), |
| 1084 | proguardConfiguration.getPrintUsageFile()); |
| 1085 | |
| 1086 | // Amend the pg-seeds consumer with options from the proguard configuration. |
| 1087 | internal.proguardSeedsConsumer = |
| 1088 | wrapStringConsumer( |
| 1089 | proguardSeedsConsumer, |
| 1090 | proguardConfiguration.isPrintSeeds(), |
| 1091 | proguardConfiguration.getSeedFile()); |
| 1092 | |
| 1093 | // Amend the configuration consumer with options from the proguard configuration. |
| 1094 | internal.configurationConsumer = |
| 1095 | wrapStringConsumer( |
| 1096 | proguardConfigurationConsumer, |
| 1097 | proguardConfiguration.isPrintConfiguration(), |
| 1098 | proguardConfiguration.getPrintConfigurationFile()); |
Ian Zerny | b71106a | 2017-12-05 09:57:35 +0100 | [diff] [blame] | 1099 | |
Ian Zerny | 6375a9e | 2018-12-14 13:52:48 +0100 | [diff] [blame] | 1100 | // Set the kept-graph consumer if any. It will only be actively used if the enqueuer triggers. |
| 1101 | internal.keptGraphConsumer = keptGraphConsumer; |
| 1102 | internal.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer; |
| 1103 | |
Søren Gjesse | 7320ce5 | 2018-05-07 15:45:22 +0200 | [diff] [blame] | 1104 | internal.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer(); |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 1105 | |
Rico Wind | b5bb3e9 | 2019-09-23 10:32:10 +0200 | [diff] [blame] | 1106 | internal.featureSplitConfiguration = featureSplitConfiguration; |
| 1107 | |
Søren Gjesse | 430740d | 2019-01-10 16:49:42 +0100 | [diff] [blame] | 1108 | internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer; |
| 1109 | |
Ian Zerny | 8f3ba44 | 2020-03-11 16:32:27 +0100 | [diff] [blame] | 1110 | internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections()); |
| 1111 | |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 1112 | if (!enableMissingLibraryApiModeling) { |
Morten Krogh-Jespersen | 7b02ca8 | 2022-09-05 15:27:50 +0200 | [diff] [blame] | 1113 | internal.apiModelingOptions().disableApiCallerIdentification(); |
| 1114 | internal.apiModelingOptions().disableOutliningAndStubbing(); |
Ian Zerny | 9b30732 | 2022-05-09 14:57:57 +0200 | [diff] [blame] | 1115 | } |
| 1116 | |
Søren Gjesse | 99e8a74 | 2020-01-07 13:59:38 +0100 | [diff] [blame] | 1117 | // Default is to remove all javac generated assertion code when generating dex. |
Søren Gjesse | b9cb6ae | 2019-12-20 12:31:45 +0100 | [diff] [blame] | 1118 | assert internal.assertionsConfiguration == null; |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 1119 | AssertionsConfiguration.Builder builder = AssertionsConfiguration.builder(getReporter()); |
Søren Gjesse | b9cb6ae | 2019-12-20 12:31:45 +0100 | [diff] [blame] | 1120 | internal.assertionsConfiguration = |
Søren Gjesse | 99e8a74 | 2020-01-07 13:59:38 +0100 | [diff] [blame] | 1121 | new AssertionConfigurationWithDefault( |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 1122 | getProgramConsumer() instanceof ClassFileConsumer |
| 1123 | ? AssertionsConfiguration.Builder.passthroughAllAssertions(builder) |
| 1124 | : AssertionsConfiguration.Builder.compileTimeDisableAllAssertions(builder), |
Søren Gjesse | 99e8a74 | 2020-01-07 13:59:38 +0100 | [diff] [blame] | 1125 | getAssertionsConfiguration()); |
Søren Gjesse | e37b6cf | 2018-07-12 11:35:54 +0200 | [diff] [blame] | 1126 | |
Christoffer Quist Adamsen | 904f23c | 2021-06-04 14:42:40 +0200 | [diff] [blame] | 1127 | // TODO(b/171552739): Enable class merging for CF. When compiling libraries, we need to be |
| 1128 | // careful when merging a public member 'm' from a class A into another class B, since B could |
| 1129 | // have a kept subclass, in which case 'm' would leak into the public API. |
Ian Zerny | 6cb65a7 | 2019-10-30 13:30:55 +0100 | [diff] [blame] | 1130 | if (internal.isGeneratingClassFiles()) { |
Christoffer Quist Adamsen | 0cc904a | 2021-05-11 11:31:58 +0200 | [diff] [blame] | 1131 | horizontalClassMergerOptions.disable(); |
Ian Zerny | e861665 | 2022-06-15 09:48:18 +0200 | [diff] [blame] | 1132 | // R8 CF output does not support desugaring so disable it. |
| 1133 | internal.desugarState = DesugarState.OFF; |
Ian Zerny | 6cb65a7 | 2019-10-30 13:30:55 +0100 | [diff] [blame] | 1134 | } |
| 1135 | |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 1136 | // EXPERIMENTAL flags. |
| 1137 | assert !internal.forceProguardCompatibility; |
| 1138 | internal.forceProguardCompatibility = forceProguardCompatibility; |
Christoffer Quist Adamsen | 5bbf7c8 | 2018-09-21 09:36:17 +0200 | [diff] [blame] | 1139 | if (disableVerticalClassMerging) { |
| 1140 | internal.enableVerticalClassMerging = false; |
| 1141 | } |
Søren Gjesse | ff15348 | 2017-10-09 15:21:14 +0200 | [diff] [blame] | 1142 | |
Yohann Roussel | 2284bc8 | 2018-04-26 15:45:35 +0200 | [diff] [blame] | 1143 | internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc(); |
| 1144 | |
Clément Béra | 30b0b05 | 2022-02-28 12:36:49 +0000 | [diff] [blame] | 1145 | internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification); |
Clément Béra | b2b85ea | 2022-08-23 12:56:56 +0200 | [diff] [blame] | 1146 | internal.synthesizedClassPrefix = |
| 1147 | synthesizedClassPrefix.isEmpty() |
| 1148 | ? System.getProperty("com.android.tools.r8.synthesizedClassPrefix", "") |
| 1149 | : synthesizedClassPrefix; |
Clément Béra | b55ec8f | 2022-08-31 12:22:14 +0200 | [diff] [blame] | 1150 | boolean l8Shrinking = !internal.synthesizedClassPrefix.isEmpty(); |
Clément Béra | b2b85ea | 2022-08-23 12:56:56 +0200 | [diff] [blame] | 1151 | // TODO(b/214382176): Enable all the time. |
Clément Béra | eb77dd9 | 2022-04-21 08:38:52 +0000 | [diff] [blame] | 1152 | internal.loadAllClassDefinitions = l8Shrinking; |
| 1153 | if (l8Shrinking) { |
Morten Krogh-Jespersen | 7b02ca8 | 2022-09-05 15:27:50 +0200 | [diff] [blame] | 1154 | internal.apiModelingOptions().disableStubbingOfClasses(); |
Clément Béra | eb77dd9 | 2022-04-21 08:38:52 +0000 | [diff] [blame] | 1155 | } |
clementbera | 2cc5516 | 2019-08-19 12:19:46 +0200 | [diff] [blame] | 1156 | internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; |
| 1157 | |
Ian Zerny | 6ddf0e9 | 2021-11-18 11:29:33 +0100 | [diff] [blame] | 1158 | // Set up the map and source file providers. |
| 1159 | // Note that minify/optimize settings must be set on internal options before doing this. |
| 1160 | internal.mapIdProvider = getMapIdProvider(); |
| 1161 | internal.sourceFileProvider = |
| 1162 | SourceFileRewriter.computeSourceFileProvider( |
| 1163 | getSourceFileProvider(), proguardConfiguration, internal); |
| 1164 | |
Ian Zerny | bbd2392 | 2022-06-07 10:33:31 +0200 | [diff] [blame] | 1165 | internal.configureAndroidPlatformBuild(getAndroidPlatformBuild()); |
| 1166 | |
Christoffer Quist Adamsen | dafb046 | 2022-09-01 12:37:09 +0200 | [diff] [blame] | 1167 | internal.getArtProfileOptions().setArtProfilesForRewriting(getArtProfilesForRewriting()); |
Christoffer Quist Adamsen | 4c6d15d | 2022-09-08 11:30:04 +0200 | [diff] [blame] | 1168 | if (!getStartupProfileProviders().isEmpty()) { |
| 1169 | internal.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders()); |
| 1170 | } |
Christoffer Quist Adamsen | 4b3614b | 2022-08-23 11:14:25 +0200 | [diff] [blame] | 1171 | |
Ian Zerny | 7744006 | 2022-08-26 13:49:08 +0200 | [diff] [blame] | 1172 | internal.programClassConflictResolver = |
| 1173 | ProgramClassCollection.wrappedConflictResolver( |
| 1174 | getClassConflictResolver(), internal.reporter); |
| 1175 | |
Morten Krogh-Jespersen | 4fa5d82 | 2020-03-25 19:28:09 +0100 | [diff] [blame] | 1176 | if (!DETERMINISTIC_DEBUGGING) { |
| 1177 | assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; |
| 1178 | internal.threadCount = getThreadCount(); |
| 1179 | } |
Søren Gjesse | 943389f | 2020-03-13 10:40:25 +0100 | [diff] [blame] | 1180 | |
Morten Krogh-Jespersen | 7d3f78b | 2023-01-31 13:25:58 +0100 | [diff] [blame] | 1181 | internal.tool = Tool.R8; |
| 1182 | |
Christoffer Quist Adamsen | 0626830 | 2022-06-28 12:35:16 +0200 | [diff] [blame] | 1183 | internal.setDumpInputFlags(getDumpInputFlags()); |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 1184 | internal.dumpOptions = dumpOptions(); |
Clément Béra | f470246 | 2020-10-27 14:28:50 +0000 | [diff] [blame] | 1185 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1186 | return internal; |
| 1187 | } |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 1188 | |
| 1189 | private static StringConsumer wrapStringConsumer( |
| 1190 | StringConsumer optionConsumer, boolean optionsFlag, Path optionFile) { |
| 1191 | if (optionsFlag) { |
| 1192 | if (optionFile != null) { |
| 1193 | return new StringConsumer.FileConsumer(optionFile, optionConsumer); |
| 1194 | } else { |
Søren Gjesse | 1af374d | 2019-09-06 10:44:54 +0200 | [diff] [blame] | 1195 | return new StandardOutConsumer(optionConsumer); |
Ian Zerny | f8ca035 | 2019-06-07 11:58:48 +0200 | [diff] [blame] | 1196 | } |
| 1197 | } |
| 1198 | return optionConsumer; |
| 1199 | } |
Søren Gjesse | 1af374d | 2019-09-06 10:44:54 +0200 | [diff] [blame] | 1200 | |
| 1201 | private static class StandardOutConsumer extends StringConsumer.ForwardingConsumer { |
| 1202 | |
| 1203 | public StandardOutConsumer(StringConsumer consumer) { |
| 1204 | super(consumer); |
| 1205 | } |
| 1206 | |
| 1207 | @Override |
| 1208 | public void accept(String string, DiagnosticsHandler handler) { |
| 1209 | super.accept(string, handler); |
| 1210 | System.out.print(string); |
| 1211 | } |
| 1212 | } |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 1213 | |
| 1214 | private DumpOptions dumpOptions() { |
Ian Zerny | 1c6f954 | 2022-03-02 13:46:25 +0100 | [diff] [blame] | 1215 | DumpOptions.Builder builder = DumpOptions.builder(Tool.R8).readCurrentSystemProperties(); |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 1216 | dumpBaseCommandOptions(builder); |
| 1217 | return builder |
| 1218 | .setIncludeDataResources(includeDataResources) |
| 1219 | .setTreeShaking(getEnableTreeShaking()) |
| 1220 | .setMinification(getEnableMinification()) |
| 1221 | .setForceProguardCompatibility(forceProguardCompatibility) |
| 1222 | .setFeatureSplitConfiguration(featureSplitConfiguration) |
| 1223 | .setProguardConfiguration(proguardConfiguration) |
Morten Krogh-Jespersen | db5129f | 2021-03-01 16:04:46 +0100 | [diff] [blame] | 1224 | .setMainDexKeepRules(mainDexKeepRules) |
Clément Béra | ddd19a1 | 2021-12-09 08:14:53 +0000 | [diff] [blame] | 1225 | .setDesugaredLibraryConfiguration(desugaredLibrarySpecification) |
Ian Zerny | e72cb78 | 2022-06-07 12:47:56 +0200 | [diff] [blame] | 1226 | .setEnableMissingLibraryApiModeling(enableMissingLibraryApiModeling) |
Clément Béra | 64a3c4c | 2020-11-10 08:16:17 +0000 | [diff] [blame] | 1227 | .build(); |
| 1228 | } |
Søren Gjesse | 5de6d7d | 2023-02-10 12:37:19 +0100 | [diff] [blame] | 1229 | |
| 1230 | private static class ExtractEmbeddedRules implements DataResourceProvider.Visitor { |
| 1231 | |
| 1232 | private final Supplier<SemanticVersion> compilerVersionSupplier; |
| 1233 | private final Reporter reporter; |
| 1234 | private final List<ProguardConfigurationSource> proguardSources = new ArrayList<>(); |
| 1235 | private final List<ProguardConfigurationSource> r8Sources = new ArrayList<>(); |
| 1236 | private SemanticVersion compilerVersion; |
| 1237 | |
| 1238 | public ExtractEmbeddedRules( |
| 1239 | Reporter reporter, Supplier<SemanticVersion> compilerVersionSupplier) { |
| 1240 | this.compilerVersionSupplier = compilerVersionSupplier; |
| 1241 | this.reporter = reporter; |
| 1242 | } |
| 1243 | |
| 1244 | @Override |
| 1245 | public void visit(DataDirectoryResource directory) { |
| 1246 | // Don't do anything. |
| 1247 | } |
| 1248 | |
| 1249 | @Override |
| 1250 | public void visit(DataEntryResource resource) { |
| 1251 | if (relevantProguardResource(resource)) { |
| 1252 | assert !relevantR8Resource(resource); |
| 1253 | readProguardConfigurationSource(resource, proguardSources::add); |
| 1254 | } else if (relevantR8Resource(resource)) { |
| 1255 | assert !relevantProguardResource(resource); |
| 1256 | readProguardConfigurationSource(resource, r8Sources::add); |
| 1257 | } |
| 1258 | } |
| 1259 | |
| 1260 | private void readProguardConfigurationSource( |
| 1261 | DataEntryResource resource, Consumer<ProguardConfigurationSource> consumer) { |
| 1262 | try (InputStream in = resource.getByteStream()) { |
| 1263 | consumer.accept(new ProguardConfigurationSourceBytes(in, resource.getOrigin())); |
| 1264 | } catch (ResourceException e) { |
| 1265 | reporter.error( |
| 1266 | new StringDiagnostic("Failed to open input: " + e.getMessage(), resource.getOrigin())); |
| 1267 | } catch (Exception e) { |
| 1268 | reporter.error(new ExceptionDiagnostic(e, resource.getOrigin())); |
| 1269 | } |
| 1270 | } |
| 1271 | |
| 1272 | private boolean relevantProguardResource(DataEntryResource resource) { |
| 1273 | // Configurations in META-INF/com.android.tools/proguard/ are ignored. |
| 1274 | final String proguardPrefix = "META-INF/proguard"; |
| 1275 | if (!resource.getName().startsWith(proguardPrefix)) { |
| 1276 | return false; |
| 1277 | } |
| 1278 | String withoutPrefix = resource.getName().substring(proguardPrefix.length()); |
| 1279 | return withoutPrefix.startsWith("/"); |
| 1280 | } |
| 1281 | |
| 1282 | private boolean relevantR8Resource(DataEntryResource resource) { |
| 1283 | final String r8Prefix = "META-INF/com.android.tools/r8"; |
| 1284 | if (!resource.getName().startsWith(r8Prefix)) { |
| 1285 | return false; |
| 1286 | } |
| 1287 | String withoutPrefix = resource.getName().substring(r8Prefix.length()); |
| 1288 | if (withoutPrefix.startsWith("/")) { |
| 1289 | // Everything under META-INF/com.android.tools/r8/ is included (not version specific). |
| 1290 | return true; |
| 1291 | } |
| 1292 | // Expect one of the following patterns: |
| 1293 | // com.android.tools/r8-min-1.5.0/ |
| 1294 | // com.android.tools/r8-max-1.5.99/ |
| 1295 | // com.android.tools/r8-min-1.5.0-max-1.5.99/ |
| 1296 | final String minPrefix = "-min-"; |
| 1297 | final String maxPrefix = "-max-"; |
| 1298 | if (!withoutPrefix.startsWith(minPrefix) && !withoutPrefix.startsWith(maxPrefix)) { |
| 1299 | return false; |
| 1300 | } |
| 1301 | |
| 1302 | SemanticVersion from = SemanticVersion.min(); |
| 1303 | SemanticVersion to = SemanticVersion.max(); |
| 1304 | |
| 1305 | if (withoutPrefix.startsWith(minPrefix)) { |
| 1306 | withoutPrefix = withoutPrefix.substring(minPrefix.length()); |
| 1307 | int versionEnd = StringUtils.indexOf(withoutPrefix, '-', '/'); |
| 1308 | if (versionEnd == -1) { |
| 1309 | return false; |
| 1310 | } |
| 1311 | try { |
| 1312 | from = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd)); |
| 1313 | } catch (IllegalArgumentException e) { |
| 1314 | return false; |
| 1315 | } |
| 1316 | withoutPrefix = withoutPrefix.substring(versionEnd); |
| 1317 | } |
| 1318 | if (withoutPrefix.startsWith(maxPrefix)) { |
| 1319 | withoutPrefix = withoutPrefix.substring(maxPrefix.length()); |
| 1320 | int versionEnd = withoutPrefix.indexOf('/'); |
| 1321 | if (versionEnd == -1) { |
| 1322 | return false; |
| 1323 | } |
| 1324 | try { |
| 1325 | to = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd)); |
| 1326 | } catch (IllegalArgumentException e) { |
| 1327 | return false; |
| 1328 | } |
| 1329 | } |
| 1330 | if (compilerVersion == null) { |
| 1331 | compilerVersion = compilerVersionSupplier.get(); |
| 1332 | } |
| 1333 | return compilerVersion.isNewerOrEqual(from) && to.isNewerOrEqual(compilerVersion); |
| 1334 | } |
| 1335 | |
| 1336 | private void parse( |
| 1337 | List<ProguardConfigurationSource> sources, ProguardConfigurationParser parser) { |
| 1338 | for (ProguardConfigurationSource source : sources) { |
| 1339 | try { |
| 1340 | parser.parse(source); |
| 1341 | } catch (Exception e) { |
| 1342 | reporter.error(new ExceptionDiagnostic(e, source.getOrigin())); |
| 1343 | } |
| 1344 | } |
| 1345 | } |
| 1346 | |
| 1347 | void parseRelevantRules(ProguardConfigurationParser parser) { |
| 1348 | parse(!r8Sources.isEmpty() ? r8Sources : proguardSources, parser); |
| 1349 | } |
| 1350 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1351 | } |