blob: e3753a782494d7c3aad5a12be52c8744aaa4000f [file] [log] [blame]
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dexsplitter;
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DexSplitterHelper;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.utils.FeatureClassMapping;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class DexSplitter {
private static final String DEFAULT_OUTPUT_ARCHIVE_FILENAME = "split";
private static final boolean PRINT_ARGS = false;
public static class Options {
private List<String> inputArchives = new ArrayList<>();
private List<String> featureJars = new ArrayList<>();
private String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
private String featureSplitMapping;
private String proguardMap;
public String getSplitBaseName() {
return splitBaseName;
}
public void setSplitBaseName(String splitBaseName) {
this.splitBaseName = splitBaseName;
}
public String getFeatureSplitMapping() {
return featureSplitMapping;
}
public void setFeatureSplitMapping(String featureSplitMapping) {
this.featureSplitMapping = featureSplitMapping;
}
public String getProguardMap() {
return proguardMap;
}
public void setProguardMap(String proguardMap) {
this.proguardMap = proguardMap;
}
public void addInputArchive(String inputArchive) {
inputArchives.add(inputArchive);
}
public void addFeatureJar(String featureJar) {
featureJars.add(featureJar);
}
public ImmutableList<String> getInputArchives() {
return ImmutableList.copyOf(inputArchives);
}
public ImmutableList<String> getFeatureJars() {
return ImmutableList.copyOf(featureJars);
}
}
private static Options parseArguments(String[] args) throws IOException {
Options options = new Options();
ParseContext context = new ParseContext(args);
while (context.head() != null) {
List<String> inputs = OptionsParsing.tryParseMulti(context, "--input");
if (inputs != null) {
inputs.stream().forEach(options::addInputArchive);
continue;
}
List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar");
if (featureJars != null) {
featureJars.stream().forEach(options::addFeatureJar);
continue;
}
String output = OptionsParsing.tryParseSingle(context, "--output", "-o");
if (output != null) {
options.setSplitBaseName(output);
continue;
}
String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null);
if (proguardMap != null) {
options.setProguardMap(proguardMap);
continue;
}
String featureSplit = OptionsParsing.tryParseSingle(context, "--feature-splits", null);
if (featureSplit != null) {
options.setFeatureSplitMapping(featureSplit);
continue;
}
throw new RuntimeException(String.format("Unknown options: '%s'.", context.head()));
}
return options;
}
private static FeatureClassMapping createFeatureClassMapping(Options options)
throws IOException, FeatureMappingException, ResourceException {
if (options.getFeatureSplitMapping() != null) {
return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
}
assert !options.featureJars.isEmpty();
return FeatureClassMapping.fromJarFiles(options.featureJars);
}
private static void run(String[] args)
throws CompilationFailedException, IOException, CompilationException, ExecutionException,
ResourceException, FeatureMappingException {
Options options = parseArguments(args);
run(options);
}
public static void run(Options options)
throws IOException, FeatureMappingException, ResourceException, CompilationException,
ExecutionException, CompilationFailedException {
if (options.getInputArchives().isEmpty()) {
throw new RuntimeException("Need at least one --input");
}
if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
throw new RuntimeException("You must supply a feature split mapping or feature jars");
}
if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
}
D8Command.Builder builder = D8Command.builder();
for (String s : options.inputArchives) {
builder.addProgramFiles(Paths.get(s));
}
// We set the actual consumer on the ApplicationWriter when we have calculated the distribution
// since we don't yet know the distribution.
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
FeatureClassMapping featureClassMapping = createFeatureClassMapping(options);
DexSplitterHelper.run(
builder.build(), featureClassMapping, options.getSplitBaseName(), options.getProguardMap());
}
public static void main(String[] args) {
try {
if (PRINT_ARGS) {
printArgs(args);
}
run(args);
} catch (CompilationFailedException
| IOException
| CompilationException
| ExecutionException
| ResourceException
| FeatureMappingException e) {
System.err.println("Splitting failed: " + e.getMessage());
System.exit(1);
}
}
private static void printArgs(String[] args) {
System.err.printf("r8.DexSplitter");
for (String s : args) {
System.err.printf(" %s", s);
}
System.err.println("");
}
}