aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java122
1 files changed, 108 insertions, 14 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
index 8920cfbd90..6d5a36ff13 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
@@ -18,9 +18,17 @@ import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTran
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.BuildType.TRISTATE;
+import static com.google.devtools.build.lib.rules.android.AndroidCommon.getAndroidConfig;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ParameterFile;
import com.google.devtools.build.lib.analysis.ConfiguredAspect;
@@ -34,16 +42,38 @@ import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.NativeAspectClass;
+import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.TriState;
import com.google.devtools.build.lib.rules.java.JavaCommon;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
import com.google.devtools.build.lib.rules.java.JavaRuntimeJarProvider;
+import java.util.Set;
+import java.util.TreeSet;
+
/**
* Aspect to {@link DexArchiveProvider build .dex Archives} from Jars.
*/
public final class DexArchiveAspect extends NativeAspectClass implements ConfiguredAspectFactory {
public static final String NAME = "DexArchiveAspect";
+ /**
+ * Function that returns a {@link Rule}'s {@code incremental_dexing} attribute for use by this
+ * aspect. Must be provided when attaching this aspect to a rule.
+ */
+ static final Function<Rule, AspectParameters> PARAM_EXTRACTOR =
+ new Function<Rule, AspectParameters>() {
+ @Override
+ public AspectParameters apply(Rule rule) {
+ AttributeMap attributes = NonconfigurableAttributeMapper.of(rule);
+ AspectParameters.Builder result = new AspectParameters.Builder();
+ TriState incrementalAttr = attributes.get("incremental_dexing", TRISTATE);
+ result.addAttribute("incremental_dexing", incrementalAttr.name());
+ return result.build();
+ }
+ };
private static final String ASPECT_DEXBUILDER_PREREQ = "$dex_archive_dexbuilder";
private static final ImmutableList<String> TRANSITIVE_ATTRIBUTES =
ImmutableList.of("deps", "exports", "runtime_deps");
@@ -71,6 +101,14 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
@Override
public ConfiguredAspect create(ConfiguredTarget base, RuleContext ruleContext,
AspectParameters params) throws InterruptedException {
+ TriState incrementalAttr =
+ TriState.valueOf(params.getOnlyValueOfAttribute("incremental_dexing"));
+ if (incrementalAttr == TriState.NO
+ || (getAndroidConfig(ruleContext).getIncrementalDexingBinaries().isEmpty()
+ && incrementalAttr != TriState.YES)) {
+ // Dex archives will never be used, so don't bother setting them up.
+ return new ConfiguredAspect.Builder(NAME, ruleContext).build();
+ }
checkState(base.getProvider(DexArchiveProvider.class) == null,
"dex archive natively generated: %s", ruleContext.getLabel());
@@ -83,9 +121,12 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
DexArchiveProvider.Builder result = createArchiveProviderBuilderFromDeps(ruleContext);
JavaRuntimeJarProvider jarProvider = base.getProvider(JavaRuntimeJarProvider.class);
if (jarProvider != null) {
+ Set<Set<String>> aspectDexopts = aspectDexopts(ruleContext);
for (Artifact jar : jarProvider.getRuntimeJars()) {
- Artifact dexArchive = createDexArchiveAction(ruleContext, jar);
- result.addDexArchive(dexArchive, jar);
+ for (Set<String> incrementalDexopts : aspectDexopts) {
+ Artifact dexArchive = createDexArchiveAction(ruleContext, jar, incrementalDexopts);
+ result.addDexArchive(incrementalDexopts, dexArchive, jar);
+ }
}
}
return new ConfiguredAspect.Builder(NAME, ruleContext)
@@ -105,8 +146,13 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
return result;
}
- private static Artifact createDexArchiveAction(RuleContext ruleContext, Artifact jar) {
- Artifact result = AndroidBinary.getDxArtifact(ruleContext, jar.getFilename() + ".dex.zip");
+ private static Artifact createDexArchiveAction(RuleContext ruleContext, Artifact jar,
+ Set<String> incrementalDexopts) {
+ // Since we're potentially dexing the same jar multiple times with different flags, we need to
+ // write out unique artifacts for each flag combinations. Here, it is convenient to distinguish
+ // them by putting the flags that were used for creating the artifacts into their filenames.
+ String filename = jar.getFilename() + Joiner.on("").join(incrementalDexopts) + ".dex.zip";
+ Artifact result = AndroidBinary.getDxArtifact(ruleContext, filename);
// Aspect must use attribute name for dexbuilder prereq that's different from the prerequisite
// declared on AndroidBinaryBaseRule because the two prereq's can otherwise name-clash when
// android_binary targets are built as part of an android_test: building android_test causes
@@ -115,7 +161,7 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
// RuleContext.getExecutablePrerequisite would fail with "$dexbuilder produces multiple prereqs"
// (note they both resolve to the same artifact but that doesn't seem to prevent the exception
// from being thrown).
- createDexArchiveAction(ruleContext, ASPECT_DEXBUILDER_PREREQ, jar, result);
+ createDexArchiveAction(ruleContext, ASPECT_DEXBUILDER_PREREQ, jar, result, incrementalDexopts);
return result;
}
@@ -125,20 +171,22 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
* {@link #getDefinition} does it for {@link DexArchiveAspect} under a different name.
*/
// Package-private method for use in AndroidBinary
- static void createDexArchiveAction(RuleContext ruleContext, Artifact jar, Artifact dexArchive) {
- createDexArchiveAction(ruleContext, "$dexbuilder", jar, dexArchive);
+ static void createDexArchiveAction(RuleContext ruleContext, Artifact jar, Artifact dexArchive,
+ Set<String> tokenizedDexopts) {
+ createDexArchiveAction(ruleContext, "$dexbuilder", jar, dexArchive, tokenizedDexopts);
}
+ /**
+ * Creates a dexbuilder action with the given input, output, and flags. Flags must have been
+ * filtered and normalized to a set that the dexbuilder tool can understand.
+ */
private static void createDexArchiveAction(RuleContext ruleContext, String dexbuilderPrereq,
- Artifact jar, Artifact dexArchive) {
+ Artifact jar, Artifact dexArchive, Set<String> incrementalDexopts) {
// Write command line arguments into a params file for compatibility with WorkerSpawnStrategy
CustomCommandLine.Builder args = new CustomCommandLine.Builder()
.addExecPath("--input_jar", jar)
- .addExecPath("--output_zip", dexArchive);
- if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
- // Match what we do in AndroidCommon.createDexAction
- args.add("--nolocals"); // TODO(bazel-team): Still needed? See createDexAction
- }
+ .addExecPath("--output_zip", dexArchive)
+ .add(incrementalDexopts);
Artifact paramFile =
ruleContext.getDerivedArtifact(
ParameterFile.derivePath(dexArchive.getRootRelativePath()), dexArchive.getRoot());
@@ -157,7 +205,53 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
.addInput(paramFile)
.addOutput(dexArchive)
.setMnemonic("DexBuilder")
- .setProgressMessage("Dexing " + jar.prettyPrint());
+ .setProgressMessage(
+ "Dexing " + jar.prettyPrint() + " with applicable dexopts " + incrementalDexopts);
ruleContext.registerAction(dexbuilder.build(ruleContext));
}
+
+ private static Set<Set<String>> aspectDexopts(RuleContext ruleContext) {
+ return Sets.powerSet(
+ normalizeDexopts(
+ ruleContext,
+ getAndroidConfig(ruleContext).getDexoptsSupportedInIncrementalDexing()));
+ }
+
+ /**
+ * Derives options to use in incremental dexing actions from the given context and dx flags, where
+ * the latter typically come from a {@code dexopts} attribute on a top-level target. This method
+ * only works reliably if the given dexopts were tokenized, e.g., using
+ * {@link RuleContext#getTokenizedStringListAttr}.
+ */
+ static ImmutableSet<String> incrementalDexopts(RuleContext ruleContext,
+ Iterable<String> tokenizedDexopts) {
+ return normalizeDexopts(
+ ruleContext,
+ Iterables.filter(
+ tokenizedDexopts,
+ Predicates.in(getAndroidConfig(ruleContext).getDexoptsSupportedInIncrementalDexing())));
+ }
+
+ private static ImmutableSet<String> normalizeDexopts(
+ RuleContext ruleContext, Iterable<String> tokenizedDexopts) {
+ // Use TreeSet to drop duplicates and get fixed (sorted) order. Fixed order is important so
+ // we generate one dex archive per set of flag in create() method, regardless of how those flags
+ // are listed in all the top-level targets being built.
+ Set<String> args = new TreeSet<>();
+ if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
+ // Match what we do in AndroidCommon.createDexAction
+ args.add("--nolocals"); // TODO(bazel-team): Still needed? See createDexAction
+ }
+ Iterables.addAll(args, Iterables.transform(tokenizedDexopts, FlagConverter.DX_TO_DEXBUILDER));
+ return ImmutableSet.copyOf(args);
+ }
+
+ private enum FlagConverter implements Function<String, String> {
+ DX_TO_DEXBUILDER;
+
+ @Override
+ public String apply(String input) {
+ return input.replace("--no-", "--no");
+ }
+ }
}