Enable aapt2 support in library actions.
RELNOTES: None
PiperOrigin-RevId: 160445869
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
index dc84ee5..e86cae2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
@@ -118,17 +118,12 @@
public ResourceContainer build(ActionConstructionContext context) {
CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
-
+
// Set the busybox tool.
builder.add("--tool").add("MERGE").add("--");
// Use a FluentIterable to avoid flattening the NestedSets
NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder();
- inputs.addAll(
- ruleContext
- .getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)
- .getRunfilesSupport()
- .getRunfilesArtifactsWithoutMiddlemen());
builder.addExecPath("--androidJar", sdk.getAndroidJar());
inputs.add(sdk.getAndroidJar());
@@ -168,6 +163,8 @@
builder.add("--packageForR").add(customJavaPackage);
}
+ // TODO(corysmith): Move the data binding parsing out of the merging pass to enable faster
+ // aapt2 builds.
if (dataBindingInfoZip != null) {
builder.addExecPath("--dataBindingInfoOut", dataBindingInfoZip);
outs.add(dataBindingInfoZip);
@@ -190,7 +187,8 @@
// Return the full set of processed transitive dependencies.
ResourceContainer.Builder result = primary.toBuilder();
if (classJarOut != null) {
- // ensure the classJar is propgated if it exists. Otherwise, AndroidCommon tries to make it.
+ // ensure the classJar is propagated if it exists. Otherwise, AndroidCommon tries to make it.
+ // TODO(corysmith): Centralize the class jar generation.
result.setJavaClassJar(classJarOut);
}
if (manifestOut != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java
index 4a926fd..8a49985 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java
@@ -17,6 +17,7 @@
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
@@ -31,6 +32,7 @@
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.List;
+import javax.annotation.Nullable;
/** Builder for creating $android_resource_parser action. */
public class AndroidResourceParsingActionBuilder {
@@ -46,6 +48,8 @@
private Artifact output;
private ResourceContainer resourceContainer;
+ private Artifact compiledSymbols;
+ private Artifact dataBindingInfoZip;
/**
* @param ruleContext The RuleContext that was used to create the SpawnAction.Builder.
@@ -76,6 +80,17 @@
return this;
}
+ public AndroidResourceParsingActionBuilder setCompiledSymbolsOutput(
+ @Nullable Artifact compiledSymbols) {
+ this.compiledSymbols = compiledSymbols;
+ return this;
+ }
+
+ public AndroidResourceParsingActionBuilder setDataBindingInfoZip(Artifact dataBindingInfoZip) {
+ this.dataBindingInfoZip = dataBindingInfoZip;
+ return this;
+ }
+
private static class ResourceContainerToArg implements Function<LocalResourceContainer, String> {
public ResourceContainerToArg() {
@@ -112,40 +127,74 @@
public ResourceContainer build(ActionConstructionContext context) {
CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
-
+
// Set the busybox tool.
builder.add("--tool").add("PARSE").add("--");
NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder();
- inputs.addAll(
- ruleContext
- .getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)
- .getRunfilesSupport()
- .getRunfilesArtifactsWithoutMiddlemen());
Preconditions.checkNotNull(primary);
- builder.add("--primaryData").add(RESOURCE_CONTAINER_TO_ARG.apply(primary));
+ String resourceDirectories = RESOURCE_CONTAINER_TO_ARG.apply(primary);
+ builder.add("--primaryData").add(resourceDirectories);
inputs.addTransitive(RESOURCE_CONTAINER_TO_ARTIFACTS.apply(primary));
- List<Artifact> outs = new ArrayList<>();
Preconditions.checkNotNull(output);
builder.addExecPath("--output", output);
- outs.add(output);
- SpawnAction.Builder spawnActionBuilder = new SpawnAction.Builder();
// Create the spawn action.
ruleContext.registerAction(
- spawnActionBuilder
+ new SpawnAction.Builder()
.useParameterFile(ParameterFileType.UNQUOTED)
.addTransitiveInputs(inputs.build())
- .addOutputs(ImmutableList.copyOf(outs))
+ .addOutputs(ImmutableList.of(output))
.setCommandLine(builder.build())
.setExecutable(
ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST))
.setProgressMessage("Parsing Android resources for " + ruleContext.getLabel())
.setMnemonic("AndroidResourceParser")
.build(context));
-
- return resourceContainer.toBuilder().setSymbols(output).build();
+
+ if (compiledSymbols != null) {
+ List<Artifact> outs = new ArrayList<>();
+ CustomCommandLine.Builder flatFileBuilder = new CustomCommandLine.Builder();
+ flatFileBuilder
+ .add("--tool")
+ .add("COMPILE_LIBRARY_RESOURCES")
+ .add("--")
+ .add("--resources")
+ .add(resourceDirectories)
+ .addExecPath("--output", compiledSymbols);
+ outs.add(compiledSymbols);
+
+ // The databinding needs to be processed before compilation, so the stripping happens here.
+ if (dataBindingInfoZip != null) {
+ flatFileBuilder.addExecPath("--manifest", resourceContainer.getManifest());
+ inputs.add(resourceContainer.getManifest());
+ if (!Strings.isNullOrEmpty(resourceContainer.getJavaPackage())) {
+ flatFileBuilder.add("--packagePath").add(resourceContainer.getJavaPackage());
+ }
+ builder.addExecPath("--dataBindingInfoOut", dataBindingInfoZip);
+ outs.add(dataBindingInfoZip);
+ }
+ // Create the spawn action.
+ ruleContext.registerAction(
+ new SpawnAction.Builder()
+ .useParameterFile(ParameterFileType.UNQUOTED)
+ .addTransitiveInputs(inputs.build())
+ .addOutputs(ImmutableList.copyOf(outs))
+ .setCommandLine(flatFileBuilder.build())
+ .setExecutable(
+ ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST))
+ .setProgressMessage("Compiling Android resources for " + ruleContext.getLabel())
+ .setMnemonic("AndroidResourceCompiler")
+ .build(context));
+ return resourceContainer
+ .toBuilder()
+ .setCompiledSymbols(compiledSymbols)
+ .setSymbols(output)
+ .build();
+ } else {
+ return resourceContainer.toBuilder().setSymbols(output).build();
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceValidatorActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceValidatorActionBuilder.java
index 66e8b8b..ee77eaa 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceValidatorActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceValidatorActionBuilder.java
@@ -13,8 +13,11 @@
// limitations under the License.
package com.google.devtools.build.lib.rules.android;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
@@ -23,9 +26,9 @@
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
-import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import java.util.ArrayList;
import java.util.List;
+import javax.annotation.Nullable;
/**
* Builder for creating $android_resource_validator action. This action validates merged resources
@@ -50,6 +53,18 @@
// Flags
private String customJavaPackage;
private boolean debug;
+ private Artifact staticLibraryOut;
+ private ResourceDependencies resourceDeps;
+ private Artifact aapt2SourceJarOut;
+ private Artifact aapt2RTxtOut;
+ private static final Function<ResourceContainer, Artifact> TO_STATIC_LIBRARY_ARTIFACT =
+ new Function<ResourceContainer, Artifact>() {
+ @Override
+ public Artifact apply(@Nullable ResourceContainer resourceContainer) {
+ return resourceContainer.getStaticLibrary();
+ }
+ };
+ private Artifact compiledSymbols;
/** @param ruleContext The RuleContext that was used to create the SpawnAction.Builder. */
public AndroidResourceValidatorActionBuilder(RuleContext ruleContext) {
@@ -57,6 +72,11 @@
this.sdk = AndroidSdkProvider.fromRuleContext(ruleContext);
}
+ public AndroidResourceValidatorActionBuilder setStaticLibraryOut(Artifact staticLibraryOut) {
+ this.staticLibraryOut = staticLibraryOut;
+ return this;
+ }
+
/** The primary resource container. We mostly propagate its values, but update the R.txt. */
public AndroidResourceValidatorActionBuilder withPrimary(ResourceContainer primary) {
this.primary = primary;
@@ -88,7 +108,110 @@
return this;
}
+ /** Used to add the static library from the dependencies. */
+ public AndroidResourceValidatorActionBuilder withDependencies(ResourceDependencies resourceDeps) {
+ this.resourceDeps = resourceDeps;
+ return this;
+ }
+
+ public AndroidResourceValidatorActionBuilder setAapt2RTxtOut(Artifact aapt2RTxtOut) {
+ this.aapt2RTxtOut = aapt2RTxtOut;
+ return this;
+ }
+
+ public AndroidResourceValidatorActionBuilder setAapt2SourceJarOut(Artifact aapt2SourceJarOut) {
+ this.aapt2SourceJarOut = aapt2SourceJarOut;
+ return this;
+ }
+
public ResourceContainer build(ActionConstructionContext context) {
+ ResourceContainer container = createValidateAction(context);
+ if (compiledSymbols == null) {
+ return container;
+ } else {
+ return createLinkStaticLibraryAction(container, context);
+ }
+ }
+
+ public AndroidResourceValidatorActionBuilder setCompiledSymbols(Artifact compiledSymbols) {
+ this.compiledSymbols = compiledSymbols;
+ return this;
+ }
+
+ /**
+ * This creates a static library using aapt2. It also generates a source jar and R.txt from aapt.
+ *
+ * <p>This allows the link action to replace the validate action for builds that use aapt2, as
+ * opposed to executing both actions.
+ */
+ private ResourceContainer createLinkStaticLibraryAction(
+ ResourceContainer validated, ActionConstructionContext context) {
+ Preconditions.checkNotNull(staticLibraryOut);
+ Preconditions.checkNotNull(aapt2SourceJarOut);
+ Preconditions.checkNotNull(aapt2RTxtOut);
+ Preconditions.checkNotNull(resourceDeps);
+
+ CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
+ ImmutableList.Builder<Artifact> inputs = ImmutableList.builder();
+ ImmutableList.Builder<Artifact> outs = ImmutableList.builder();
+
+ // Set the busybox tool.
+ builder.add("--tool").add("LINK_STATIC_LIBRARY").add("--");
+
+ builder.addExecPath("--aapt2", sdk.getAapt2().getExecutable());
+
+ FluentIterable<Artifact> libraries =
+ FluentIterable.from(resourceDeps.getResources()).transform(TO_STATIC_LIBRARY_ARTIFACT);
+
+ builder
+ .add("--libraries")
+ .add(libraries.join(Joiner.on(context.getConfiguration().getHostPathSeparator())));
+ inputs.addAll(libraries);
+
+ builder.addExecPath("--compiled", compiledSymbols);
+ inputs.add(compiledSymbols);
+
+ builder.addExecPath("--manifest", primary.getManifest());
+ inputs.add(validated.getManifest());
+
+ if (!Strings.isNullOrEmpty(customJavaPackage)) {
+ // Sets an alternative java package for the generated R.java
+ // this allows android rules to generate resources outside of the java{,tests} tree.
+ builder.add("--packageForR").add(customJavaPackage);
+ }
+
+ builder.addExecPath("--sourceJarOut", aapt2SourceJarOut);
+ outs.add(aapt2SourceJarOut);
+
+ builder.addExecPath("--rTxtOut", aapt2RTxtOut);
+ outs.add(aapt2RTxtOut);
+
+ builder.addExecPath("--staticLibraryOut", staticLibraryOut);
+ outs.add(staticLibraryOut);
+
+ ruleContext.registerAction(
+ new SpawnAction.Builder()
+ .useParameterFile(ParameterFileType.UNQUOTED)
+ .addTool(sdk.getAapt2())
+ .addInputs(inputs.build())
+ .addOutputs(outs.build())
+ .setCommandLine(builder.build())
+ .setExecutable(
+ ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST))
+ .setProgressMessage(
+ "Linking static android resource library for " + ruleContext.getLabel())
+ .setMnemonic("AndroidResourceLink")
+ .build(context));
+
+ return validated
+ .toBuilder()
+ .setAapt2JavaSourceJar(aapt2SourceJarOut)
+ .setAapt2RTxt(aapt2RTxtOut)
+ .setStaticLibrary(staticLibraryOut)
+ .build();
+ }
+
+ private ResourceContainer createValidateAction(ActionConstructionContext context) {
CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
// Set the busybox tool.
@@ -100,13 +223,7 @@
builder.addExecPath("--aapt", sdk.getAapt().getExecutable());
- // Use a FluentIterable to avoid flattening the NestedSets
- NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder();
- inputs.addAll(
- ruleContext
- .getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)
- .getRunfilesSupport()
- .getRunfilesArtifactsWithoutMiddlemen());
+ ImmutableList.Builder<Artifact> inputs = ImmutableList.builder();
builder.addExecPath("--annotationJar", sdk.getAnnotationsJar());
inputs.add(sdk.getAnnotationsJar());
@@ -145,7 +262,7 @@
spawnActionBuilder
.useParameterFile(ParameterFileType.UNQUOTED)
.addTool(sdk.getAapt())
- .addTransitiveInputs(inputs.build())
+ .addInputs(inputs.build())
.addOutputs(ImmutableList.copyOf(outs))
.setCommandLine(builder.build())
.setExecutable(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
index 152548b..81e9000 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
@@ -438,7 +438,7 @@
@Nullable Artifact mainDexProguardCfg,
Artifact manifestOut,
Artifact mergedResources,
- Artifact dataBindingInfoZip,
+ @Nullable Artifact dataBindingInfoZip,
@Nullable Artifact featureOf,
@Nullable Artifact featureAfter)
throws InterruptedException {
@@ -511,7 +511,7 @@
true /* isLibrary */,
resourceDeps,
resourceFilter,
- ImmutableList.<String>of(),
+ ImmutableList.<String>of() /* uncompressedExtensions */,
false /* crunchPng */,
false /* incremental */,
ResourceContainer.builderFromRule(ruleContext)
@@ -572,11 +572,24 @@
AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR);
if (resourceContainer.getSymbols() != null) {
- new AndroidResourceParsingActionBuilder(ruleContext)
- .withPrimary(resourceContainer)
- .setParse(data)
- .setOutput(resourceContainer.getSymbols())
- .build(ruleContext);
+ AndroidResourceParsingActionBuilder parsingBuilder =
+ new AndroidResourceParsingActionBuilder(ruleContext)
+ .withPrimary(resourceContainer)
+ .setParse(data)
+ .setOutput(resourceContainer.getSymbols())
+ .setCompiledSymbolsOutput(resourceContainer.getCompiledSymbols());
+
+ if (dataBindingInfoZip != null && resourceContainer.getCompiledSymbols() != null) {
+ PathFragment unusedInfo = dataBindingInfoZip.getRootRelativePath();
+ // TODO(corysmith): Centralize the data binding processing and zipping into a single
+ // action. Data binding processing needs to be triggered here as well as the merger to
+ // avoid aapt2 from throwing an error during compilation.
+ parsingBuilder.setDataBindingInfoZip(
+ ruleContext.getDerivedArtifact(
+ unusedInfo.replaceName(unusedInfo.getBaseName() + "_unused.zip"),
+ dataBindingInfoZip.getRoot()));
+ }
+ resourceContainer = parsingBuilder.build(ruleContext);
}
AndroidResourceMergingActionBuilder resourcesMergerBuilder =
@@ -590,16 +603,21 @@
.setDataBindingInfoZip(dataBindingInfoZip);
ResourceContainer merged = resourcesMergerBuilder.build(ruleContext);
- AndroidResourceValidatorActionBuilder validatorBuilder =
+ processed =
new AndroidResourceValidatorActionBuilder(ruleContext)
.setJavaPackage(merged.getJavaPackage())
- .setDebug(
- ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT)
+ .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT)
.setMergedResources(mergedResources)
.withPrimary(merged)
+ .setRTxtOut(merged.getRTxt())
.setSourceJarOut(merged.getJavaSourceJar())
- .setRTxtOut(merged.getRTxt());
- processed = validatorBuilder.build(ruleContext);
+ // aapt2 related artifacts. Will be generated if the targetAaptVersion is AAPT2.
+ .withDependencies(resourceDeps)
+ .setCompiledSymbols(merged.getCompiledSymbols())
+ .setAapt2RTxtOut(merged.getAapt2RTxt())
+ .setAapt2SourceJarOut(merged.getAapt2JavaSourceJar())
+ .setStaticLibraryOut(merged.getStaticLibrary())
+ .build(ruleContext);
} else {
AndroidResourcesProcessorBuilder builder =
new AndroidResourcesProcessorBuilder(ruleContext)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
index a97d52a..4d7e5b2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainer.java
@@ -98,6 +98,18 @@
@Nullable
public abstract Artifact getSymbols();
+ @Nullable
+ public abstract Artifact getCompiledSymbols();
+
+ @Nullable
+ public abstract Artifact getStaticLibrary();
+
+ @Nullable
+ public abstract Artifact getAapt2RTxt();
+
+ @Nullable
+ public abstract Artifact getAapt2JavaSourceJar();
+
// The limited hashCode and equals behavior is necessary to avoid duplication when building with
// fat_apk_cpu set. Artifacts generated in different configurations will naturally be different
// and non-equal objects, causing the ResourceContainer not to be automatically deduplicated at
@@ -284,6 +296,14 @@
public abstract Builder setSymbols(@Nullable Artifact symbols);
+ public abstract Builder setCompiledSymbols(@Nullable Artifact compiledSymbols);
+
+ public abstract Builder setStaticLibrary(@Nullable Artifact staticLibrary);
+
+ public abstract Builder setAapt2JavaSourceJar(@Nullable Artifact javaSourceJar);
+
+ public abstract Builder setAapt2RTxt(@Nullable Artifact rTxt);
+
abstract ResourceContainer autoBuild();
/**