aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java24
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java50
2 files changed, 67 insertions, 7 deletions
diff --git a/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java b/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java
index c2354ca134..29cd4c1645 100644
--- a/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java
+++ b/src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java
@@ -99,6 +99,7 @@ public class DexFileSplitterTest {
ImmutableList<Path> outputArchives =
runDexSplitter(
200,
+ /*inclusionFilterJar=*/ null,
"main_dex_list",
MAIN_DEX_LIST_FILE,
/*minimalMainDex=*/ false,
@@ -117,6 +118,7 @@ public class DexFileSplitterTest {
ImmutableList<Path> outputArchives =
runDexSplitter(
256 * 256,
+ /*inclusionFilterJar=*/ null,
"minimal_main_dex",
MAIN_DEX_LIST_FILE,
/*minimalMainDex=*/ true,
@@ -129,6 +131,24 @@ public class DexFileSplitterTest {
assertExpectedEntries(outputArchives, expectedEntries);
}
+ @Test
+ public void testInclusionFilterJar() throws Exception {
+ Path dexArchive = buildDexArchive();
+ Path dexArchive2 = buildDexArchive(INPUT_JAR2, "jar2.dex.zip");
+ ImmutableList<Path> outputArchives =
+ runDexSplitter(
+ 256 * 256,
+ INPUT_JAR2,
+ "filtered",
+ /*mainDexList=*/ null,
+ /*minimalMainDex=*/ false,
+ dexArchive,
+ dexArchive2);
+
+ // Only expect entries from the Jar we filtered by
+ assertExpectedEntries(outputArchives, dexEntries(dexArchive2));
+ }
+
private static Iterable<String> expectedMainDexEntries() throws IOException {
return Iterables.transform(
Files.readAllLines(MAIN_DEX_LIST_FILE),
@@ -146,6 +166,7 @@ public class DexFileSplitterTest {
try {
runDexSplitter(
200,
+ /*inclusionFilterJar=*/ null,
"should_fail",
/*mainDexList=*/ null,
/*minimalMainDex=*/ true,
@@ -183,6 +204,7 @@ public class DexFileSplitterTest {
Path... dexArchives) throws IOException {
return runDexSplitter(
maxNumberOfIdxPerDex,
+ /*inclusionFilterJar=*/ null,
outputRoot,
/*mainDexList=*/ null,
/*minimalMainDex=*/ false,
@@ -191,6 +213,7 @@ public class DexFileSplitterTest {
private ImmutableList<Path> runDexSplitter(
int maxNumberOfIdxPerDex,
+ @Nullable Path inclusionFilterJar,
String outputRoot,
@Nullable Path mainDexList,
boolean minimalMainDex,
@@ -203,6 +226,7 @@ public class DexFileSplitterTest {
options.maxNumberOfIdxPerDex = maxNumberOfIdxPerDex;
options.mainDexListFile = mainDexList;
options.minimalMainDex = minimalMainDex;
+ options.inclusionFilterJar = inclusionFilterJar;
DexFileSplitter.splitIntoShards(options);
assertThat(options.outputDirectory.toFile().exists()).isTrue();
ImmutableSet<Path> files =
diff --git a/src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java b/src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java
index 0ab70b46de..ffe3674b09 100644
--- a/src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java
@@ -20,10 +20,10 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.android.dex.Dex;
import com.android.dex.DexFormat;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
@@ -46,6 +46,8 @@ import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
@@ -118,6 +120,17 @@ class DexFileSplitter implements Closeable {
help = "Limit on fields and methods in a single dex file."
)
public int maxNumberOfIdxPerDex;
+
+ @Option(
+ name = "inclusion_filter_jar",
+ defaultValue = "null",
+ category = "input",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ converter = ExistingPathConverter.class,
+ help = "If given, only classes in the given Jar are included in outputs."
+ )
+ public Path inclusionFilterJar;
}
public static void main(String[] args) throws Exception {
@@ -141,9 +154,12 @@ class DexFileSplitter implements Closeable {
Files.createDirectories(options.outputDirectory);
}
- ImmutableSet<String> classesInMainDex = options.mainDexListFile != null
- ? ImmutableSet.copyOf(Files.readAllLines(options.mainDexListFile, UTF_8))
- : null;
+ ImmutableSet<String> classesInMainDex =
+ options.mainDexListFile != null
+ ? ImmutableSet.copyOf(Files.readAllLines(options.mainDexListFile, UTF_8))
+ : null;
+ ImmutableSet<String> expected =
+ options.inclusionFilterJar != null ? expectedEntries(options.inclusionFilterJar) : null;
try (Closer closer = Closer.create();
DexFileSplitter out =
new DexFileSplitter(options.outputDirectory, options.maxNumberOfIdxPerDex)) {
@@ -152,11 +168,15 @@ class DexFileSplitter implements Closeable {
// if presented with a single jar containing all the given inputs.
// TODO(kmb): Abandon alphabetic sorting to process each input fully before moving on (still
// requires scanning inputs twice for main dex list).
+ Predicate<ZipEntry> inclusionFilter = ZipEntryPredicates.suffixes(".dex", ".class");
+ if (expected != null) {
+ inclusionFilter = inclusionFilter.and(e -> expected.contains(e.getName()));
+ }
LinkedHashMap<String, ZipFile> deduped = new LinkedHashMap<>();
for (Path inputArchive : options.inputArchives) {
ZipFile zip = closer.register(new ZipFile(inputArchive.toFile()));
zip.stream()
- .filter(ZipEntryPredicates.suffixes(".dex", ".class"))
+ .filter(inclusionFilter)
.forEach(e -> deduped.putIfAbsent(e.getName(), zip));
}
ImmutableList<Map.Entry<String, ZipFile>> files =
@@ -165,6 +185,13 @@ class DexFileSplitter implements Closeable {
.stream()
.sorted(Comparator.comparing(e -> e.getKey(), ZipEntryComparator::compareClassNames))
.collect(ImmutableList.toImmutableList());
+ if (expected != null) {
+ ImmutableSet<String> actual =
+ files.stream().map(e -> e.getKey()).collect(ImmutableSet.toImmutableSet());
+ Set<String> difference = Sets.difference(expected, actual);
+ checkState(difference.isEmpty(),
+ "--inclusion_filter_jar given but didn't find: %s", difference);
+ }
// 2. Process each class in desired order, rolling from shard to shard as needed.
if (classesInMainDex == null || classesInMainDex.isEmpty()) {
@@ -181,11 +208,20 @@ class DexFileSplitter implements Closeable {
if (options.minimalMainDex) {
out.nextShard(); // Start new .dex file if requested
}
- out.processDexFiles(files, Predicates.not(mainDexFilter));
+ out.processDexFiles(files, mainDexFilter.negate());
}
}
}
+ private static ImmutableSet<String> expectedEntries(Path filterJar) throws IOException {
+ try (ZipFile zip = new ZipFile(filterJar.toFile())) {
+ return zip.stream()
+ .filter(ZipEntryPredicates.suffixes(".class"))
+ .map(e -> e.getName() + ".dex")
+ .collect(ImmutableSet.toImmutableSet());
+ }
+ }
+
private final int maxNumberOfIdxPerDex;
private final Path outputDirectory;
@@ -235,7 +271,7 @@ class DexFileSplitter implements Closeable {
throws IOException {
for (Map.Entry<String, ZipFile> entry : filesToProcess) {
String filename = entry.getKey();
- if (filter.apply(filename)) {
+ if (filter.test(filename)) {
ZipFile zipFile = entry.getValue();
processDexEntry(zipFile, zipFile.getEntry(filename));
}