1418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// for details. All rights reserved. Use of this source code is governed by a
3418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager// BSD-style license that can be found in the LICENSE file.
4418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerpackage com.android.tools.r8;
5418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
6418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.graph.DexItemFactory;
7418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.shaking.ProguardConfiguration;
8418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.shaking.ProguardConfigurationParser;
9f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Rousselimport com.android.tools.r8.shaking.ProguardConfigurationRule;
10418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.shaking.ProguardRuleParserException;
11418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.AndroidApp;
12418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.FileUtils;
13418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.android.tools.r8.utils.InternalOptions;
14f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukovimport com.android.tools.r8.utils.OutputMode;
15418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport com.google.common.collect.ImmutableList;
16418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.io.IOException;
17418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.nio.file.Path;
18418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.nio.file.Paths;
19418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.ArrayList;
20418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Collections;
21418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.List;
22418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerimport java.util.Optional;
23418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
24418d1ca139ea11316113beafbb3b3dd3fd5587aMads Agerpublic class R8Command extends BaseCommand {
25418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
26418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public static class Builder extends BaseCommand.Builder<R8Command, Builder> {
27418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
28f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    private final List<Path> mainDexRules = new ArrayList<>();
29854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    private boolean minimalMainDex = false;
30418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private final List<Path> proguardConfigFiles = new ArrayList<>();
31418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private Optional<Boolean> treeShaking = Optional.empty();
32418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private Optional<Boolean> minification = Optional.empty();
33418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private boolean ignoreMissingClasses = false;
34418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
35418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private Builder() {
36418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      super(CompilationMode.RELEASE);
37418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
38418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
39418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    private Builder(AndroidApp app) {
40418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      super(app, CompilationMode.RELEASE);
41418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
42418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
43418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
44418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    Builder self() {
45418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
46418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
47418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
482ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
492ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Enable/disable tree shaking. This overrides any settings in proguard configuration files.
502ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
51418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder setTreeShaking(boolean useTreeShaking) {
52418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      treeShaking = Optional.of(useTreeShaking);
53418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
54418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
55418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
562ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
572ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Enable/disable minification. This overrides any settings in proguard configuration files.
582ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
59418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder setMinification(boolean useMinification) {
60418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      minification = Optional.of(useMinification);
61418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
62418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
63418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
642ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
652ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Add proguard configuration file resources for automatic main dex list calculation.
662ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
67f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    public Builder addMainDexRules(Path... paths) {
68f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      Collections.addAll(mainDexRules, paths);
69f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      return this;
70f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    }
71f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel
722ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
732ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Add proguard configuration file resources for automatic main dex list calculation.
742ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
75f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    public Builder addMainDexRules(List<Path> paths) {
76f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      mainDexRules.addAll(paths);
77f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      return this;
78f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    }
79f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel
802ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
81854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse     * Request minimal main dex generated when main dex rules are used.
82854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse     *
83854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse     * The main purpose of this is to verify that the main dex rules are sufficient
84854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse     * for running on a platform without native multi dex support.
85854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse     */
86854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    public Builder setMinimalMainDex(boolean value) {
87854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      minimalMainDex = value;
88854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      return this;
89854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    }
90854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    /**
912ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Add proguard configuration file resources.
922ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
93418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder addProguardConfigurationFiles(Path... paths) {
94418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      Collections.addAll(proguardConfigFiles, paths);
95418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
96418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
97418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
982ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
992ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Add proguard configuration file resources.
1002ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
101418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder addProguardConfigurationFiles(List<Path> paths) {
102418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      proguardConfigFiles.addAll(paths);
103418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
104418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
105418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
1062ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
1072ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Set a proguard mapping file resource.
1082ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
109418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder setProguardMapFile(Path path) {
110418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      getAppBuilder().setProguardMapFile(path);
111418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
112418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
113418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
1142ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut    /**
1152ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     * Set a package distribution file resource.
1162ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut     */
117418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public Builder setPackageDistributionFile(Path path) {
118418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      getAppBuilder().setPackageDistributionFile(path);
119418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
120418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
121418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
122418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    /**
123418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * Deprecated flag to avoid failing if classes are missing during compilation.
124418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     *
125418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     * <p>TODO: Make compilation safely assume this flag to be true and remove the flag.
126418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager     */
127418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    Builder setIgnoreMissingClasses(boolean ignoreMissingClasses) {
128418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      this.ignoreMissingClasses = ignoreMissingClasses;
129418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return this;
130418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
131418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
132418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    @Override
133418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    public R8Command build() throws CompilationException, IOException {
134418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      // If printing versions ignore everything else.
135418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (isPrintHelp() || isPrintVersion()) {
136418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        return new R8Command(isPrintHelp(), isPrintVersion());
137418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
138418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
13978be58456a12d4a51ad5fb0c19c292c01746f97bYohann Roussel      validate();
140418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      DexItemFactory factory = new DexItemFactory();
141f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
142f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      if (this.mainDexRules.isEmpty()) {
143f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        mainDexKeepRules = ImmutableList.of();
144f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      } else {
145f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
146f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        try {
147f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel          parser.parse(mainDexRules);
148f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        } catch (ProguardRuleParserException e) {
149f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel          throw new CompilationException(e.getMessage(), e.getCause());
150f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        }
151f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        mainDexKeepRules = parser.getConfig().getRules();
152f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      }
153418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      ProguardConfiguration configuration;
154418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      if (proguardConfigFiles.isEmpty()) {
155418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        configuration = ProguardConfiguration.defaultConfiguration(factory);
156418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else {
157418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
158418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        try {
159418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          parser.parse(proguardConfigFiles);
160418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        } catch (ProguardRuleParserException e) {
161418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException(e.getMessage(), e.getCause());
162418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
163418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        configuration = parser.getConfig();
1648842c41758db478ebc54437f583367750cd18378Alan Leung        addProgramFiles(configuration.getInjars(), false);
165418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        addLibraryFiles(configuration.getLibraryjars());
166418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
167418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
168418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      boolean useTreeShaking = treeShaking.orElse(configuration.isShrinking());
169418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      boolean useMinification = minification.orElse(configuration.isObfuscating());
170418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
171418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      return new R8Command(
172418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          getAppBuilder().build(),
173418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          getOutputPath(),
174f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov          getOutputMode(),
175f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel          mainDexKeepRules,
176854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse          minimalMainDex,
177418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          configuration,
178418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          getMode(),
179418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          getMinApiLevel(),
180418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          useTreeShaking,
181418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          useMinification,
182341bbdae4c7b6fe0f5dd95c7158488af6768e7baIan Zerny          ignoreMissingClasses);
183418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
184418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
185418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
186418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  // Internal state to verify parsing properties not enforced by the builder.
187418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static class ParseState {
1882ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut
189418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    CompilationMode mode = null;
190418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
191418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
192418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
193418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      "Usage: r8 [options] <input-files>",
194418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
195418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      " and options are:",
196877c186b70e433f6b4b85a6f16d7dbcb1f9c2f0fIan Zerny      "  --release               # Compile without debugging information (default).",
197877c186b70e433f6b4b85a6f16d7dbcb1f9c2f0fIan Zerny      "  --debug                 # Compile with debugging information.",
198f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --output <file>         # Output result in <file>.",
199341bbdae4c7b6fe0f5dd95c7158488af6768e7baIan Zerny      "                          # <file> must be an existing directory or a zip file.",
200f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --lib <file>            # Add <file> as a library resource.",
201877c186b70e433f6b4b85a6f16d7dbcb1f9c2f0fIan Zerny      "  --min-api               # Minimum Android API level compatibility.",
202f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --pg-conf <file>        # Proguard configuration <file> (implies tree shaking/minification).",
203f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --pg-map <file>         # Proguard map <file>.",
204f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --no-tree-shaking       # Force disable tree shaking of unreachable classes.",
205f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --no-minification       # Force disable minification of names.",
206f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --multidex-rules <file> # Enable automatic classes partitioning for legacy multidex.",
207f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "                          # <file> is a Proguard configuration file (with only keep rules).",
208f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --version               # Print the version of r8.",
209f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      "  --help                  # Print this message."));
210418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
211f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel  private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
212854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse  private final boolean minimalMainDex;
213418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final ProguardConfiguration proguardConfiguration;
214418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final boolean useTreeShaking;
215418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final boolean useMinification;
216418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private final boolean ignoreMissingClasses;
217418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
218418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public static Builder builder() {
219418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return new Builder();
220418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
221418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
222418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  // Internal builder to start from an existing AndroidApp.
223418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  static Builder builder(AndroidApp app) {
224418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return new Builder(app);
225418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
226418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
227418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public static Builder parse(String[] args) throws CompilationException, IOException {
228418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    Builder builder = builder();
229418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    parse(args, builder, new ParseState());
230418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return builder;
231418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
232418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
233418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private static ParseState parse(String[] args, Builder builder, ParseState state)
234418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      throws CompilationException, IOException {
235418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    for (int i = 0; i < args.length; i++) {
236b28d1cc2188a131786ec148631d13f4ce0e59f1dSøren Gjesse      String arg = args[i].trim();
237b28d1cc2188a131786ec148631d13f4ce0e59f1dSøren Gjesse      if (arg.length() == 0) {
238b28d1cc2188a131786ec148631d13f4ce0e59f1dSøren Gjesse        continue;
239b28d1cc2188a131786ec148631d13f4ce0e59f1dSøren Gjesse      } else if (arg.equals("--help")) {
240418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setPrintHelp(true);
241418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--version")) {
242418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setPrintVersion(true);
243418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--debug")) {
244418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (state.mode == CompilationMode.RELEASE) {
245418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException("Cannot compile in both --debug and --release mode.");
246418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
247418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        state.mode = CompilationMode.DEBUG;
248418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setMode(state.mode);
249418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--release")) {
250418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (state.mode == CompilationMode.DEBUG) {
251418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException("Cannot compile in both --debug and --release mode.");
252418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
253418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        state.mode = CompilationMode.RELEASE;
254418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setMode(state.mode);
255418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--output")) {
256418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        String outputPath = args[++i];
257418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (builder.getOutputPath() != null) {
258418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException(
259418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              "Cannot output both to '"
260418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                  + builder.getOutputPath().toString()
261418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                  + "' and '"
262418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                  + outputPath
263418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                  + "'");
264418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
265418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setOutputPath(Paths.get(outputPath));
266418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--lib")) {
267418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.addLibraryFiles(Paths.get(args[++i]));
268877c186b70e433f6b4b85a6f16d7dbcb1f9c2f0fIan Zerny      } else if (arg.equals("--min-api")) {
269418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setMinApiLevel(Integer.valueOf(args[++i]));
270418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--no-tree-shaking")) {
271418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setTreeShaking(false);
272418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--no-minification")) {
273418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setMinification(false);
274f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      } else if (arg.equals("--multidex-rules")) {
275f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel        builder.addMainDexRules(Paths.get(args[++i]));
276854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      } else if (arg.equals("--minimal-maindex")) {
277854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse        builder.setMinimalMainDex(true);
278418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--pg-conf")) {
279418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.addProguardConfigurationFiles(Paths.get(args[++i]));
280418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--pg-map")) {
281418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setProguardMapFile(Paths.get(args[++i]));
282418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.equals("--ignore-missing-classes")) {
283418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.setIgnoreMissingClasses(true);
284418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else if (arg.startsWith("@")) {
285418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        // TODO(zerny): Replace this with pipe reading.
286418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        String argsFile = arg.substring(1);
287418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        try {
288418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          List<String> linesInFile = FileUtils.readTextFile(Paths.get(argsFile));
289418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          List<String> argsInFile = new ArrayList<>();
290418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          for (String line : linesInFile) {
291418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            for (String word : line.split("\\s")) {
292418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              String trimmed = word.trim();
293418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              if (!trimmed.isEmpty()) {
294418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager                argsInFile.add(trimmed);
295418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              }
296418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager            }
297418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          }
298418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          // TODO(zerny): We need to define what CWD should be for files referenced in an args file.
299418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          state = parse(argsInFile.toArray(new String[argsInFile.size()]), builder, state);
300418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        } catch (IOException | CompilationException e) {
301418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException(
302418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager              "Failed to read arguments from file " + argsFile + ": " + e.getMessage());
303418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
304418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      } else {
305418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        if (arg.startsWith("--")) {
306418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager          throw new CompilationException("Unknown option: " + arg);
307418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        }
308418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager        builder.addProgramFiles(Paths.get(arg));
309418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      }
310418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
311418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return state;
312418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
313418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
314418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private R8Command(
315418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      AndroidApp inputApp,
316418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      Path outputPath,
317f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov      OutputMode outputMode,
318f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel      ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
319854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse      boolean minimalMainDex,
320418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      ProguardConfiguration proguardConfiguration,
321418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      CompilationMode mode,
322418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      int minApiLevel,
323418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      boolean useTreeShaking,
324418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      boolean useMinification,
325341bbdae4c7b6fe0f5dd95c7158488af6768e7baIan Zerny      boolean ignoreMissingClasses) {
326341bbdae4c7b6fe0f5dd95c7158488af6768e7baIan Zerny    super(inputApp, outputPath, outputMode, mode, minApiLevel);
327418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    assert proguardConfiguration != null;
328f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    assert mainDexKeepRules != null;
329f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov    assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
330f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    this.mainDexKeepRules = mainDexKeepRules;
331854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    this.minimalMainDex = minimalMainDex;
332418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.proguardConfiguration = proguardConfiguration;
333418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.useTreeShaking = useTreeShaking;
334418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.useMinification = useMinification;
335418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    this.ignoreMissingClasses = ignoreMissingClasses;
336418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
337418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
338418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  private R8Command(boolean printHelp, boolean printVersion) {
339418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    super(printHelp, printVersion);
340f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    mainDexKeepRules = ImmutableList.of();
341854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    minimalMainDex = false;
342418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    proguardConfiguration = null;
343418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    useTreeShaking = false;
344418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    useMinification = false;
345418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    ignoreMissingClasses = false;
346418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
347418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
348418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public boolean useTreeShaking() {
349418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return useTreeShaking;
350418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
351418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
352418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  public boolean useMinification() {
353418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return useMinification;
354418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
355418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
3562ecfe14809f915219856ef7387f04f2721fdccfcStephan Herhut  @Override
357418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  InternalOptions getInternalOptions() {
358418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    InternalOptions internal = new InternalOptions(proguardConfiguration.getDexItemFactory());
359fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert !internal.debug;
360418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.debug = getMode() == CompilationMode.DEBUG;
361418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.minApiLevel = getMinApiLevel();
362fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert !internal.skipMinification;
363418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.skipMinification = !useMinification();
364fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert internal.useTreeShaking;
365418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.useTreeShaking = useTreeShaking();
36689379978605ff75677cb5a25cca79728b7d98b14Jinseong Jeon    assert !internal.printUsage;
36789379978605ff75677cb5a25cca79728b7d98b14Jinseong Jeon    internal.printUsage = proguardConfiguration.isPrintUsage();
36889379978605ff75677cb5a25cca79728b7d98b14Jinseong Jeon    internal.printUsageFile = proguardConfiguration.getPrintUsageFile();
369fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert !internal.ignoreMissingClasses;
370418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.ignoreMissingClasses = ignoreMissingClasses;
371418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager
372418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    // TODO(zerny): Consider which other proguard options should be given flags.
373fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert internal.packagePrefix.length() == 0;
374418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.packagePrefix = proguardConfiguration.getPackagePrefix();
375fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert internal.allowAccessModification;
376418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.allowAccessModification = proguardConfiguration.getAllowAccessModification();
377418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    for (String pattern : proguardConfiguration.getAttributesRemovalPatterns()) {
378418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      internal.attributeRemoval.applyPattern(pattern);
379418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
380418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (proguardConfiguration.isIgnoreWarnings()) {
381418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      internal.ignoreMissingClasses = true;
382418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
383fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert internal.seedsFile == null;
384418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (proguardConfiguration.getSeedFile() != null) {
385418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      internal.seedsFile = proguardConfiguration.getSeedFile();
386418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
387fb882ae717d46db2b8d3685bd74c4beb6df39801Søren Gjesse    assert !internal.verbose;
388418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (proguardConfiguration.isVerbose()) {
389418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      internal.verbose = true;
390418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
391418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    if (!proguardConfiguration.isObfuscating()) {
392418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager      internal.skipMinification = true;
393418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    }
394418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.printSeeds |= proguardConfiguration.getPrintSeeds();
395418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.printMapping |= proguardConfiguration.isPrintingMapping();
396418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.printMappingFile = proguardConfiguration.getPrintMappingOutput();
397418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.classObfuscationDictionary = proguardConfiguration.getClassObfuscationDictionary();
398418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.obfuscationDictionary = proguardConfiguration.getObfuscationDictionary();
399f820a57655a3b5712825de91bf9d95b9d88e84bfYohann Roussel    internal.mainDexKeepRules = mainDexKeepRules;
400854a7d4617a50c95bdbd7efc266c7ba6335b8d0cSøren Gjesse    internal.minimalMainDex = minimalMainDex;
401418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.keepRules = proguardConfiguration.getRules();
402418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    internal.dontWarnPatterns = proguardConfiguration.getDontWarnPatterns();
403f19dfabc704b3975d66019c0817d65fc48921284Denis Vnukov    internal.outputMode = getOutputMode();
404522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny    if (internal.debug) {
405522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny      // TODO(zerny): Should we support removeSwitchMaps in debug mode? b/62936642
406522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny      internal.removeSwitchMaps = false;
407522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny      // TODO(zerny): Should we support inlining in debug mode? b/62937285
408522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny      internal.inlineAccessors = false;
409522bb04a225c6dd2585fcc60b5babb97bcc267b7Ian Zerny    }
410418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager    return internal;
411418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager  }
412418d1ca139ea11316113beafbb3b3dd3fd5587aMads Ager}
413