If resources were prefiltered, ignore unavailable resources from dependencies
Filtering resources in analysis allows Bazel to save time by not copying
unwanted resources to the execution phase and by having less resource for
execution to process.
However, analysis-phase resource filtering currently happens only for
android_binary targets. android_library dependencies will contain references to
all of their resources in their R and symbol files, even if those resources are
filtered out and not made available to execution.
Eventually, we want to use dynamic configuration to propogate the filters being
used on android_binary targets to android_library dependencies as well, and
filter those in analysis also. Until then, however, we need a way of ignoring
unwanted resources if they don't exist.
This change adds a flag to the AndroidResourceProcessingAction to indicate that
resources were filtered in analysis. If the flag is passed, if a resource
referred to in a parsed symbols file is not actually visible, it will be
ignored (otherwise, the action would go on to merging and eventually crash when
it tried to use the missing resource).
If the flag is passed, execution-time resource filtering by density will also
be skipped (execution-time filtering by other resource qualifiers happens in
aapt, but is a much simpler process).
--
PiperOrigin-RevId: 150752270
MOS_MIGRATED_REVID=150752270
diff --git a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
index d616839..b3cea45 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
@@ -15,7 +15,6 @@
import com.android.builder.core.VariantType;
import com.android.ide.common.res2.MergingException;
-import com.android.utils.StdLogger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
@@ -112,9 +111,6 @@
checkFlags(options);
- AndroidResourceProcessor resourceProcessor =
- new AndroidResourceProcessor(new StdLogger(com.android.utils.StdLogger.Level.VERBOSE));
-
try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("aar_gen_tmp")) {
Path tmp = scopedTmp.getPath();
Path resourcesOut = tmp.resolve("merged_resources");
@@ -133,7 +129,8 @@
assetsOut,
null,
VariantType.LIBRARY,
- null);
+ null,
+ /* filteredResources= */ ImmutableList.<String>of());
logger.fine(String.format("Merging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS)));
writeAar(options.aarOutput, mergedData, options.manifest, options.rtxt, options.classes);
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
index c42e870..9ea843d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataDeserializer.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.android;
import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.devtools.build.android.ParsedAndroidData.KeyValueConsumer;
import com.google.devtools.build.android.proto.SerializeFormat;
@@ -24,6 +25,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
@@ -33,11 +35,24 @@
public class AndroidDataDeserializer {
private static final Logger logger = Logger.getLogger(AndroidDataDeserializer.class.getName());
- public static AndroidDataDeserializer create() {
- return new AndroidDataDeserializer();
+ private final ImmutableSet<String> filteredResources;
+
+ /**
+ * @param filteredResources resources that were filtered out of this target and should be ignored
+ * if they are referenced in symbols files.
+ */
+ public static AndroidDataDeserializer withFilteredResources(
+ Collection<String> filteredResources) {
+ return new AndroidDataDeserializer(ImmutableSet.copyOf(filteredResources));
}
- private AndroidDataDeserializer() {}
+ public static AndroidDataDeserializer create() {
+ return new AndroidDataDeserializer(ImmutableSet.<String>of());
+ }
+
+ private AndroidDataDeserializer(ImmutableSet<String> filteredResources) {
+ this.filteredResources = filteredResources;
+ }
/**
* Reads the serialized {@link DataKey} and {@link DataValue} to the {@link KeyValueConsumers}.
@@ -95,6 +110,14 @@
for (Entry<DataKey, KeyValueConsumer<DataKey, ?>> entry : keys.entrySet()) {
SerializeFormat.DataValue protoValue = SerializeFormat.DataValue.parseDelimitedFrom(in);
DataSource source = sourceTable.sourceFromId(protoValue.getSourceId());
+ int nameCount = source.getPath().getNameCount();
+ String shortPath = source.getPath().subpath(nameCount - 2, nameCount).toString();
+ if (filteredResources.contains(shortPath)) {
+ // Skip files that were filtered out during analysis.
+ // TODO(asteinb): Properly filter out these files from android_library symbol files during
+ // analysis instead, and remove this list.
+ continue;
+ }
if (protoValue.hasXmlValue()) {
// TODO(corysmith): Figure out why the generics are wrong.
// If I use Map<DataKey, KeyValueConsumer<DataKey, ? extends DataValue>>, I can put
@@ -115,3 +138,4 @@
}
}
}
+
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
index 2e1398e..90b9c74 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.android;
import com.android.ide.common.res2.MergingException;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Iterables;
@@ -137,33 +138,34 @@
private final SourceChecker deDuplicator;
private final ListeningExecutorService executorService;
- private final AndroidDataDeserializer deserializer = AndroidDataDeserializer.create();
+ private final AndroidDataDeserializer deserializer;
/** Creates a merger with no path deduplication and a default {@link ExecutorService}. */
+ @VisibleForTesting
static AndroidDataMerger createWithDefaults() {
return createWithDefaultThreadPool(NoopSourceChecker.create());
}
/** Creates a merger with a custom deduplicator and a default {@link ExecutorService}. */
+ @VisibleForTesting
static AndroidDataMerger createWithDefaultThreadPool(SourceChecker deDuplicator) {
- return new AndroidDataMerger(deDuplicator, MoreExecutors.newDirectExecutorService());
- }
-
- /** Creates a merger with a custom deduplicator and an {@link ExecutorService}. */
- static AndroidDataMerger create(
- SourceChecker deDuplicator, ListeningExecutorService executorService) {
- return new AndroidDataMerger(deDuplicator, executorService);
+ return new AndroidDataMerger(
+ deDuplicator, MoreExecutors.newDirectExecutorService(), AndroidDataDeserializer.create());
}
/** Creates a merger with a file contents hashing deduplicator. */
static AndroidDataMerger createWithPathDeduplictor(
- ListeningExecutorService executorService) {
- return create(ContentComparingChecker.create(), executorService);
+ ListeningExecutorService executorService, AndroidDataDeserializer deserializer) {
+ return new AndroidDataMerger(ContentComparingChecker.create(), executorService, deserializer);
}
- private AndroidDataMerger(SourceChecker deDuplicator, ListeningExecutorService executorService) {
+ private AndroidDataMerger(
+ SourceChecker deDuplicator,
+ ListeningExecutorService executorService,
+ AndroidDataDeserializer deserializer) {
this.deDuplicator = deDuplicator;
this.executorService = executorService;
+ this.deserializer = deserializer;
}
/**
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
index 0ab4c32..6e7d629 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
@@ -43,13 +43,15 @@
@Nullable final PngCruncher cruncher,
final VariantType type,
@Nullable final Path symbolsOut,
- @Nullable AndroidResourceClassWriter rclassWriter)
+ @Nullable AndroidResourceClassWriter rclassWriter,
+ AndroidDataDeserializer deserializer)
throws MergingException {
Stopwatch timer = Stopwatch.createStarted();
final ListeningExecutorService executorService =
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15));
try (Closeable closeable = ExecutorServiceCloser.createWith(executorService)) {
- AndroidDataMerger merger = AndroidDataMerger.createWithPathDeduplictor(executorService);
+ AndroidDataMerger merger =
+ AndroidDataMerger.createWithPathDeduplictor(executorService, deserializer);
UnwrittenMergedAndroidData merged =
merger.loadAndMerge(
transitive, direct, primary, primaryManifest, type != VariantType.LIBRARY);
@@ -94,7 +96,8 @@
final Path assetsOut,
@Nullable final PngCruncher cruncher,
final VariantType type,
- @Nullable final Path symbolsOut)
+ @Nullable final Path symbolsOut,
+ final List<String> filteredResources)
throws MergingException {
try {
final ParsedAndroidData parsedPrimary = ParsedAndroidData.from(primary);
@@ -108,7 +111,8 @@
cruncher,
type,
symbolsOut,
- null /* rclassWriter */);
+ null /* rclassWriter */,
+ AndroidDataDeserializer.withFilteredResources(filteredResources));
} catch (IOException e) {
throw MergingException.wrapException(e).build();
}
@@ -144,6 +148,8 @@
cruncher,
type,
symbolsOut,
- rclassWriter);
+ rclassWriter,
+ deserializer);
}
}
+
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index 07ddb40..eb9ffa3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.android.Converters.UnvalidatedAndroidDataConverter;
import com.google.devtools.build.android.Converters.VariantTypeConverter;
import com.google.devtools.build.android.SplitConfigurationFilter.UnrecognizedSplitsException;
+import com.google.devtools.common.options.Converters;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
@@ -38,6 +39,7 @@
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
@@ -203,6 +205,13 @@
category = "config",
help = "Version code to stamp into the packaged manifest.")
public int versionCode;
+
+ @Option(name = "prefilteredResources",
+ defaultValue = "",
+ converter = Converters.CommaSeparatedOptionListConverter.class,
+ category = "config",
+ help = "A list of resources that were filtered out in analysis.")
+ public List<String> prefilteredResources;
}
private static AaptConfigOptions aaptConfigOptions;
@@ -253,14 +262,20 @@
mergedAssets,
selectPngCruncher(),
options.packageType,
- options.symbolsOut);
+ options.symbolsOut,
+ options.prefilteredResources);
logger.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ final List<String> densitiesToFilter =
+ options.prefilteredResources.isEmpty()
+ ? options.densities
+ : Collections.<String>emptyList();
+
final DensityFilteredAndroidData filteredData =
mergedData.filter(
new DensitySpecificResourceFilter(
- options.densities, filteredResources, mergedResources),
+ densitiesToFilter, filteredResources, mergedResources),
new DensitySpecificManifestProcessor(options.densities, densityManifest));
logger.fine(