Extend dump to support feature splits and min-api
Bug: 156436523
Change-Id: I152ee4852552c70d1af6a45c5f9e5a984b3f374d
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 8890a4d..ede2193 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -74,11 +74,11 @@
this.inputApp = inputApp;
}
- public DexApplication read() throws IOException, ExecutionException {
+ public DexApplication read() throws IOException {
return read((StringResource) null);
}
- public DexApplication read(StringResource proguardMap) throws IOException, ExecutionException {
+ public DexApplication read(StringResource proguardMap) throws IOException {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
return read(proguardMap, executor);
@@ -87,14 +87,13 @@
}
}
- public final DexApplication read(ExecutorService executorService)
- throws IOException, ExecutionException {
+ public final DexApplication read(ExecutorService executorService) throws IOException {
return read(
null, executorService, ProgramClassCollection.defaultConflictResolver(options.reporter));
}
public final DexApplication read(StringResource proguardMap, ExecutorService executorService)
- throws IOException, ExecutionException {
+ throws IOException {
return read(
proguardMap,
executorService,
@@ -105,7 +104,7 @@
StringResource proguardMap,
ExecutorService executorService,
ProgramClassConflictResolver resolver)
- throws IOException, ExecutionException {
+ throws IOException {
assert verifyMainDexOptionsCompatible(inputApp, options);
Path dumpOutput = null;
boolean cleanDump = false;
@@ -167,7 +166,7 @@
}
private static void dumpInputToFile(AndroidApp app, Path output, InternalOptions options) {
- app.dump(output, options.getProguardConfiguration(), options.reporter);
+ app.dump(output, options);
}
private static boolean verifyMainDexOptionsCompatible(
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index 4a4db11..610104a 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -108,14 +108,12 @@
}
public boolean inBaseOrSameFeatureAs(DexProgramClass clazz, DexProgramClass context) {
- FeatureSplit split = javaTypeToFeatureSplitMapping.get(clazz.type.toSourceString());
- return split == null
- || split == javaTypeToFeatureSplitMapping.get(context.type.toSourceString());
+ FeatureSplit split = getFeatureSplit(clazz.type);
+ return split == null || split == getFeatureSplit(context.type);
}
public boolean isInFeature(DexProgramClass clazz) {
- return javaTypeToFeatureSplitMapping.containsKey(
- DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
+ return getFeatureSplit(clazz.type) != null;
}
public boolean isInBase(DexProgramClass clazz) {
@@ -132,13 +130,19 @@
return true;
}
// TODO(141451259): Consider doing the mapping from DexType to Feature (with support in mapper)
- return javaTypeToFeatureSplitMapping.get(
- DescriptorUtils.descriptorToJavaType(a.toDescriptorString()))
- == javaTypeToFeatureSplitMapping.get(
- DescriptorUtils.descriptorToJavaType(b.toDescriptorString()));
+ return getFeatureSplit(a) == getFeatureSplit(b);
}
public List<FeatureSplit> getFeatureSplits() {
return featureSplits;
}
+
+ public FeatureSplit getFeatureSplitFromClassDescriptor(String classDescriptor) {
+ return javaTypeToFeatureSplitMapping.get(DescriptorUtils.descriptorToJavaType(classDescriptor));
+ }
+
+ private FeatureSplit getFeatureSplit(DexType type) {
+ assert type.isClassType();
+ return javaTypeToFeatureSplitMapping.get(type.toSourceString());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index e059db8..c0cdc54 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
@@ -99,29 +98,25 @@
public static List<DexMethod> generateListOfBackportedMethods(
AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
- try {
- List<DexMethod> methods = new ArrayList<>();
- PrefixRewritingMapper rewritePrefix =
- options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
- AppInfo appInfo = null;
- if (androidApp != null) {
- DexApplication app =
- new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
- appInfo = new AppInfo(app);
- }
- AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
- BackportedMethodRewriter.RewritableMethods rewritableMethods =
- new BackportedMethodRewriter.RewritableMethods(options, appView);
- rewritableMethods.visit(methods::add);
- if (appInfo != null) {
- DesugaredLibraryRetargeter desugaredLibraryRetargeter =
- new DesugaredLibraryRetargeter(appView);
- desugaredLibraryRetargeter.visit(methods::add);
- }
- return methods;
- } catch (ExecutionException e) {
- throw unwrapExecutionException(e);
+ List<DexMethod> methods = new ArrayList<>();
+ PrefixRewritingMapper rewritePrefix =
+ options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
+ AppInfo appInfo = null;
+ if (androidApp != null) {
+ DexApplication app =
+ new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
+ appInfo = new AppInfo(app);
}
+ AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
+ BackportedMethodRewriter.RewritableMethods rewritableMethods =
+ new BackportedMethodRewriter.RewritableMethods(options, appView);
+ rewritableMethods.visit(methods::add);
+ if (appInfo != null) {
+ DesugaredLibraryRetargeter desugaredLibraryRetargeter =
+ new DesugaredLibraryRetargeter(appView);
+ desugaredLibraryRetargeter.visit(methods::add);
+ }
+ return methods;
}
public static void registerAssumedLibraryTypes(InternalOptions options) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 2c12a44..00853e0 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
+import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
@@ -31,11 +32,11 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.FilteredClassPath;
-import com.android.tools.r8.shaking.ProguardConfiguration;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
@@ -45,6 +46,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -57,11 +59,13 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -75,11 +79,25 @@
public class AndroidApp {
private static final String dumpVersionFileName = "r8-version";
+ private static final String dumpBuildPropertiesFileName = "build.properties";
private static final String dumpProgramFileName = "program.jar";
private static final String dumpClasspathFileName = "classpath.jar";
private static final String dumpLibraryFileName = "library.jar";
private static final String dumpConfigFileName = "proguard.config";
+ private static Map<FeatureSplit, String> dumpFeatureSplitFileNames(
+ FeatureSplitConfiguration featureSplitConfiguration) {
+ Map<FeatureSplit, String> featureSplitFileNames = new IdentityHashMap<>();
+ if (featureSplitConfiguration != null) {
+ int i = 1;
+ for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
+ featureSplitFileNames.put(featureSplit, "feature-" + i + ".jar");
+ i++;
+ }
+ }
+ return featureSplitFileNames;
+ }
+
private final ImmutableList<ProgramResourceProvider> programResourceProviders;
private final ImmutableMap<Resource, String> programResourcesMainDescriptor;
private final ImmutableList<ClassFileResourceProvider> classpathResourceProviders;
@@ -422,25 +440,40 @@
return programResourcesMainDescriptor.get(resource);
}
- public void dump(Path output, ProguardConfiguration configuration, Reporter reporter) {
+ public void dump(Path output, InternalOptions options) {
int nextDexIndex = 0;
OpenOption[] openOptions =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(output, openOptions))) {
writeToZipStream(
out, dumpVersionFileName, Version.getVersionString().getBytes(), ZipEntry.DEFLATED);
- if (configuration != null) {
- String proguardConfig = configuration.getParsedConfiguration();
+ writeToZipStream(
+ out,
+ dumpBuildPropertiesFileName,
+ getBuildPropertiesContents(options).getBytes(),
+ ZipEntry.DEFLATED);
+ if (options.getProguardConfiguration() != null) {
+ String proguardConfig = options.getProguardConfiguration().getParsedConfiguration();
writeToZipStream(out, dumpConfigFileName, proguardConfig.getBytes(), ZipEntry.DEFLATED);
}
- nextDexIndex = dumpProgramResources(dumpProgramFileName, nextDexIndex, out);
+ nextDexIndex =
+ dumpProgramResources(
+ dumpProgramFileName,
+ dumpFeatureSplitFileNames(options.featureSplitConfiguration),
+ nextDexIndex,
+ out,
+ options.featureSplitConfiguration);
nextDexIndex = dumpClasspathResources(nextDexIndex, out);
nextDexIndex = dumpLibraryResources(nextDexIndex, out);
} catch (IOException | ResourceException e) {
- throw reporter.fatalError(new ExceptionDiagnostic(e));
+ throw options.reporter.fatalError(new ExceptionDiagnostic(e));
}
}
+ private String getBuildPropertiesContents(InternalOptions options) {
+ return "min-api=" + options.minApiLevel;
+ }
+
private int dumpLibraryResources(int nextDexIndex, ZipOutputStream out)
throws IOException, ResourceException {
nextDexIndex =
@@ -480,31 +513,96 @@
};
}
- private int dumpProgramResources(String archiveName, int nextDexIndex, ZipOutputStream out)
+ private int dumpProgramResources(
+ String archiveName,
+ Map<FeatureSplit, String> featureSplitArchiveNames,
+ int nextDexIndex,
+ ZipOutputStream out,
+ FeatureSplitConfiguration featureSplitConfiguration)
throws IOException, ResourceException {
- try (ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream()) {
- try (ZipOutputStream archiveOutputStream = new ZipOutputStream(archiveByteStream)) {
- Object2IntMap<String> seen = new Object2IntOpenHashMap<>();
- Set<DataEntryResource> dataEntries = getDataEntryResourcesForTesting();
- for (DataEntryResource dataResource : dataEntries) {
- String entryName = dataResource.getName();
- try (InputStream dataStream = dataResource.getByteStream()) {
- byte[] bytes = ByteStreams.toByteArray(dataStream);
- writeToZipStream(archiveOutputStream, entryName, bytes, ZipEntry.DEFLATED);
+ Map<FeatureSplit, ByteArrayOutputStream> featureSplitArchiveByteStreams =
+ new IdentityHashMap<>();
+ Map<FeatureSplit, ZipOutputStream> featureSplitArchiveOutputStreams = new IdentityHashMap<>();
+ try {
+ if (featureSplitConfiguration != null) {
+ for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
+ ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();
+ featureSplitArchiveByteStreams.put(featureSplit, archiveByteStream);
+ featureSplitArchiveOutputStreams.put(
+ featureSplit, new ZipOutputStream(archiveByteStream));
+ }
+ }
+ try (ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream()) {
+ try (ZipOutputStream archiveOutputStream = new ZipOutputStream(archiveByteStream)) {
+ Object2IntMap<String> seen = new Object2IntOpenHashMap<>();
+ Set<DataEntryResource> dataEntries = getDataEntryResourcesForTesting();
+ for (DataEntryResource dataResource : dataEntries) {
+ String entryName = dataResource.getName();
+ try (InputStream dataStream = dataResource.getByteStream()) {
+ byte[] bytes = ByteStreams.toByteArray(dataStream);
+ writeToZipStream(archiveOutputStream, entryName, bytes, ZipEntry.DEFLATED);
+ }
+ }
+ for (ProgramResourceProvider provider : programResourceProviders) {
+ for (ProgramResource programResource : provider.getProgramResources()) {
+ nextDexIndex =
+ dumpProgramResource(
+ seen,
+ nextDexIndex,
+ classDescriptor -> {
+ if (featureSplitConfiguration != null) {
+ FeatureSplit featureSplit =
+ featureSplitConfiguration.getFeatureSplitFromClassDescriptor(
+ classDescriptor);
+ if (featureSplit != null) {
+ return featureSplitArchiveOutputStreams.get(featureSplit);
+ }
+ }
+ return archiveOutputStream;
+ },
+ archiveOutputStream,
+ programResource);
+ }
}
}
- for (ProgramResourceProvider provider : programResourceProviders) {
- for (ProgramResource programResource : provider.getProgramResources()) {
- nextDexIndex =
- dumpProgramResource(seen, nextDexIndex, archiveOutputStream, programResource);
+ writeToZipStream(out, archiveName, archiveByteStream.toByteArray(), ZipEntry.DEFLATED);
+ if (featureSplitConfiguration != null) {
+ for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
+ featureSplitArchiveOutputStreams.remove(featureSplit).close();
+ writeToZipStream(
+ out,
+ featureSplitArchiveNames.get(featureSplit),
+ featureSplitArchiveByteStreams.get(featureSplit).toByteArray(),
+ ZipEntry.DEFLATED);
}
}
}
- writeToZipStream(out, archiveName, archiveByteStream.toByteArray(), ZipEntry.DEFLATED);
+ } finally {
+ closeOutputStreams(featureSplitArchiveOutputStreams.values());
}
return nextDexIndex;
}
+ private void closeOutputStreams(Collection<ZipOutputStream> outputStreams) throws IOException {
+ IOException exception = null;
+ RuntimeException runtimeException = null;
+ for (OutputStream outputStream : outputStreams) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ exception = e;
+ } catch (RuntimeException e) {
+ runtimeException = e;
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ if (runtimeException != null) {
+ throw runtimeException;
+ }
+ }
+
private static int dumpClassFileResources(
String archiveName,
int nextDexIndex,
@@ -519,7 +617,12 @@
ProgramResource programResource = provider.getProgramResource(descriptor);
int oldDexIndex = nextDexIndex;
nextDexIndex =
- dumpProgramResource(seen, nextDexIndex, archiveOutputStream, programResource);
+ dumpProgramResource(
+ seen,
+ nextDexIndex,
+ ignore -> archiveOutputStream,
+ archiveOutputStream,
+ programResource);
assert nextDexIndex == oldDexIndex;
}
}
@@ -532,11 +635,11 @@
private static int dumpProgramResource(
Object2IntMap<String> seen,
int nextDexIndex,
- ZipOutputStream archiveOutputStream,
+ Function<String, ZipOutputStream> cfArchiveOutputStream,
+ ZipOutputStream dexArchiveOutputStream,
ProgramResource programResource)
throws ResourceException, IOException {
byte[] bytes = ByteStreams.toByteArray(programResource.getByteStream());
- String entryName;
if (programResource.getKind() == Kind.CF) {
Set<String> classDescriptors = programResource.getClassDescriptors();
String classDescriptor =
@@ -546,12 +649,14 @@
String classFileName = DescriptorUtils.getClassFileName(classDescriptor);
int dupCount = seen.getOrDefault(classDescriptor, 0);
seen.put(classDescriptor, dupCount + 1);
- entryName = dupCount == 0 ? classFileName : (classFileName + "." + dupCount + ".dup");
+ String entryName = dupCount == 0 ? classFileName : (classFileName + "." + dupCount + ".dup");
+ writeToZipStream(
+ cfArchiveOutputStream.apply(classDescriptor), entryName, bytes, ZipEntry.DEFLATED);
} else {
assert programResource.getKind() == Kind.DEX;
- entryName = "classes" + nextDexIndex++ + ".dex";
+ String entryName = "classes" + nextDexIndex++ + ".dex";
+ writeToZipStream(dexArchiveOutputStream, entryName, bytes, ZipEntry.DEFLATED);
}
- writeToZipStream(archiveOutputStream, entryName, bytes, ZipEntry.DEFLATED);
return nextDexIndex;
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 21a5123..38cd73e 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.android.tools.r8.utils.FileUtils.isArchive;
+
import com.android.tools.r8.CompatProguardCommandBuilder;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command.Builder;
@@ -13,7 +16,9 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* Wrapper to make it easy to call R8 in compat mode when compiling a dump file.
@@ -30,7 +35,7 @@
private static final List<String> VALID_OPTIONS =
Arrays.asList("--classfile", "--compat", "--debug", "--release");
- private static final List<String> VALID_OPTIONS_WITH_OPERAND =
+ private static final List<String> VALID_OPTIONS_WITH_SINGLE_OPERAND =
Arrays.asList(
"--output",
"--lib",
@@ -43,6 +48,9 @@
"--pg-map-output",
"--desugared-lib");
+ private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
+ Arrays.asList("--feature-jar");
+
public static void main(String[] args) throws CompilationFailedException {
boolean isCompatMode = false;
OutputMode outputMode = OutputMode.DexIndexed;
@@ -50,6 +58,7 @@
Path pgMapOutput = null;
CompilationMode compilationMode = CompilationMode.RELEASE;
List<Path> program = new ArrayList<>();
+ Map<Path, Path> features = new LinkedHashMap<>();
List<Path> library = new ArrayList<>();
List<Path> classpath = new ArrayList<>();
List<Path> config = new ArrayList<>();
@@ -81,7 +90,7 @@
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
- } else if (VALID_OPTIONS_WITH_OPERAND.contains(option)) {
+ } else if (VALID_OPTIONS_WITH_SINGLE_OPERAND.contains(option)) {
String operand = args[++i];
switch (option) {
case "--output":
@@ -117,11 +126,29 @@
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
+ } else if (VALID_OPTIONS_WITH_TWO_OPERANDS.contains(option)) {
+ String firstOperand = args[++i];
+ String secondOperand = args[++i];
+ switch (option) {
+ case "--feature-jar":
+ {
+ Path featureIn = Paths.get(firstOperand);
+ Path featureOut = Paths.get(secondOperand);
+ if (!isArchive(featureIn)) {
+ throw new IllegalArgumentException(
+ "Expected an archive, got `" + featureIn.toString() + "`.");
+ }
+ features.put(featureIn, featureOut);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unimplemented option: " + option);
+ }
} else {
program.add(Paths.get(option));
}
}
- Builder builder =
+ Builder commandBuilder =
new CompatProguardCommandBuilder(isCompatMode)
.addProgramFiles(program)
.addLibraryFiles(library)
@@ -130,9 +157,17 @@
.setOutput(outputPath, outputMode)
.setMode(compilationMode)
.setMinApiLevel(minApi);
+ features.forEach(
+ (in, out) ->
+ commandBuilder.addFeatureSplit(
+ featureBuilder ->
+ featureBuilder
+ .addProgramResourceProvider(ArchiveResourceProvider.fromArchive(in, true))
+ .setProgramConsumer(new ArchiveConsumer(out))
+ .build()));
if (pgMapOutput != null) {
- builder.setProguardMapOutputPath(pgMapOutput);
+ commandBuilder.setProguardMapOutputPath(pgMapOutput);
}
- R8.run(builder.build());
+ R8.run(commandBuilder.build());
}
}
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 04c4bf2..441be7c 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.InternalOptions;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -41,7 +41,7 @@
@Test
public void test() throws Exception {
- Reporter reporter = new Reporter();
+ InternalOptions options = new InternalOptions();
String dataResourceName = "my-resource.bin";
byte[] dataResourceData = new byte[] {1, 2, 3};
@@ -50,7 +50,7 @@
testForD8().addProgramClasses(B.class).setMinApi(AndroidApiLevel.B).compile().writeToZip();
AndroidApp appIn =
- AndroidApp.builder(reporter)
+ AndroidApp.builder(options.reporter)
.addClassProgramData(ToolHelper.getClassAsBytes(A.class), origin("A"))
.addClassProgramData(ToolHelper.getClassAsBytes(A.class), origin("A"))
.addClassProgramData(ToolHelper.getClassAsBytes(A.class), origin("A"))
@@ -67,9 +67,9 @@
.build();
Path dumpFile = temp.newFolder().toPath().resolve("dump.zip");
- appIn.dump(dumpFile, null, reporter);
+ appIn.dump(dumpFile, options);
- AndroidApp appOut = AndroidApp.builder(reporter).addDump(dumpFile).build();
+ AndroidApp appOut = AndroidApp.builder(options.reporter).addDump(dumpFile).build();
assertEquals(1, appOut.getClassProgramResourcesForTesting().size());
assertEquals(
DescriptorUtils.javaTypeToDescriptor(A.class.getTypeName()),
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 5aa5f97..33a7e50 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -85,12 +85,26 @@
def program_jar(self):
return self.if_exists('program.jar')
+ def feature_jars(self):
+ feature_jars = []
+ i = 1
+ while True:
+ feature_jar = self.if_exists('feature-%s.jar' % i)
+ if feature_jar:
+ feature_jars.append(feature_jar)
+ i = i + 1
+ else:
+ return feature_jars
+
def library_jar(self):
return self.if_exists('library.jar')
def classpath_jar(self):
return self.if_exists('classpath.jar')
+ def build_properties_file(self):
+ return self.if_exists('build.properties')
+
def config_file(self):
return self.if_exists('proguard.config')
@@ -106,11 +120,24 @@
def read_dump(args, temp):
if args.dump is None:
error("A dump file must be specified")
- dump_file = zipfile.ZipFile(args.dump, 'r')
+ dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
with utils.ChangedWorkingDirectory(temp):
dump_file.extractall()
return Dump(temp)
+def determine_build_properties(args, dump):
+ build_properties = {}
+ build_properties_file = dump.build_properties_file()
+ if build_properties_file:
+ with open(build_properties_file) as f:
+ build_properties_contents = f.readlines()
+ for line in build_properties_contents:
+ stripped = line.strip()
+ if stripped:
+ pair = stripped.split('=')
+ build_properties[pair[0]] = pair[1]
+ return build_properties
+
def determine_version(args, dump):
if args.version is None:
return dump.version()
@@ -126,6 +153,9 @@
def determine_output(args, temp):
return os.path.join(temp, 'out.jar')
+def determine_feature_output(feature_jar, temp):
+ return os.path.join(temp, os.path.basename(feature_jar)[:-4] + ".out.jar")
+
def download_distribution(args, version, temp):
if version == 'master':
return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
@@ -157,6 +187,7 @@
if not os.path.exists(temp):
os.makedirs(temp)
dump = read_dump(args, temp)
+ build_properties = determine_build_properties(args, dump)
version = determine_version(args, dump)
compiler = determine_compiler(args, dump)
out = determine_output(args, temp)
@@ -181,6 +212,9 @@
cmd.append('--compat')
cmd.append(dump.program_jar())
cmd.extend(['--output', out])
+ for feature_jar in dump.feature_jars():
+ cmd.extend(['--feature-jar', feature_jar,
+ determine_feature_output(feature_jar, temp)])
if dump.library_jar():
cmd.extend(['--lib', dump.library_jar()])
if dump.classpath_jar():
@@ -189,6 +223,8 @@
cmd.extend(['--pg-conf', dump.config_file()])
if compiler != 'd8':
cmd.extend(['--pg-map-output', '%s.map' % out])
+ if 'min-api' in build_properties:
+ cmd.extend(['--min-api', build_properties.get('min-api')])
cmd.extend(otherargs)
utils.PrintCmd(cmd)
try: