diff options
-rw-r--r-- | src/test/java/com/google/devtools/build/android/dexer/DexFileSplitterTest.java | 24 | ||||
-rw-r--r-- | src/tools/android/java/com/google/devtools/build/android/dexer/DexFileSplitter.java | 50 |
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)); } |