aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java188
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java77
4 files changed, 264 insertions, 7 deletions
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
new file mode 100644
index 0000000000..8a5310331b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
@@ -0,0 +1,188 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.rules.android;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.analysis.RuleContext;
+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 com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
+import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
+import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.Builder.SeparatorType;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Builder for creating $android_resource_merger action. The action merges resources and generates
+ * the merged R classes for an android_library to hand off to java compilation of the library
+ * sources. It also generates a merged resources zip file to pass on to the
+ * $android_resource_validator action. For android_binary, see {@link
+ * AndroidResourcesProcessorBuilder}.
+ */
+class AndroidResourceMergingActionBuilder {
+
+ private static final ResourceContainerConverter.ToArtifacts RESOURCE_CONTAINER_TO_ARTIFACTS =
+ ResourceContainerConverter.builder()
+ .includeResourceRoots()
+ .includeSymbolsBin()
+ .toArtifactConverter();
+ private static final ResourceContainerConverter.ToArg RESOURCE_CONTAINER_TO_ARG =
+ ResourceContainerConverter.builder()
+ .includeResourceRoots()
+ .includeLabel()
+ .includeSymbolsBin()
+ .withSeparator(SeparatorType.SEMICOLON_AMPERSAND)
+ .toArgConverter();
+
+ private final RuleContext ruleContext;
+ private final AndroidSdkProvider sdk;
+
+ // Inputs
+ private ResourceContainer primary;
+ private ResourceDependencies dependencies;
+
+ // Outputs
+ private Artifact mergedResourcesOut;
+ private Artifact classJarOut;
+ private Artifact manifestOut;
+
+ // Flags
+ private String customJavaPackage;
+
+ /** @param ruleContext The RuleContext that was used to create the SpawnAction.Builder. */
+ public AndroidResourceMergingActionBuilder(RuleContext ruleContext) {
+ this.ruleContext = ruleContext;
+ this.sdk = AndroidSdkProvider.fromRuleContext(ruleContext);
+ }
+
+ /**
+ * The primary resource for merging. This resource will overwrite any resource or data value in
+ * the transitive closure.
+ */
+ public AndroidResourceMergingActionBuilder withPrimary(ResourceContainer primary) {
+ this.primary = primary;
+ return this;
+ }
+
+ public AndroidResourceMergingActionBuilder withDependencies(ResourceDependencies resourceDeps) {
+ this.dependencies = resourceDeps;
+ return this;
+ }
+
+ public AndroidResourceMergingActionBuilder setMergedResourcesOut(Artifact mergedResourcesOut) {
+ this.mergedResourcesOut = mergedResourcesOut;
+ return this;
+ }
+
+ public AndroidResourceMergingActionBuilder setClassJarOut(Artifact classJarOut) {
+ this.classJarOut = classJarOut;
+ return this;
+ }
+
+ public AndroidResourceMergingActionBuilder setManifestOut(Artifact manifestOut) {
+ this.manifestOut = manifestOut;
+ return this;
+ }
+
+ public AndroidResourceMergingActionBuilder setJavaPackage(String customJavaPackage) {
+ this.customJavaPackage = customJavaPackage;
+ return this;
+ }
+
+ public ResourceContainer build(ActionConstructionContext context) {
+ CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
+
+ // Use a FluentIterable to avoid flattening the NestedSets
+ NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder();
+ inputs.addAll(
+ ruleContext
+ .getExecutablePrerequisite("$android_resource_merger", Mode.HOST)
+ .getRunfilesSupport()
+ .getRunfilesArtifactsWithoutMiddlemen());
+
+ builder.addExecPath("--androidJar", sdk.getAndroidJar());
+ inputs.add(sdk.getAndroidJar());
+
+ Preconditions.checkNotNull(primary);
+ builder.add("--primaryData").add(RESOURCE_CONTAINER_TO_ARG.apply(primary));
+ inputs.addTransitive(RESOURCE_CONTAINER_TO_ARTIFACTS.apply(primary));
+
+ Preconditions.checkNotNull(primary.getManifest());
+ builder.addExecPath("--primaryManifest", primary.getManifest());
+ inputs.add(primary.getManifest());
+
+ ResourceContainerConverter.convertDependencies(
+ dependencies, builder, inputs, RESOURCE_CONTAINER_TO_ARG, RESOURCE_CONTAINER_TO_ARTIFACTS);
+
+ Preconditions.checkNotNull(classJarOut);
+ List<Artifact> outs = new ArrayList<>();
+ builder.addExecPath("--classJarOutput", classJarOut);
+ outs.add(classJarOut);
+
+ if (mergedResourcesOut != null) {
+ builder.addExecPath("--resourcesOutput", mergedResourcesOut);
+ outs.add(mergedResourcesOut);
+ }
+
+ // For now, do manifest processing to remove placeholders that aren't handled by the legacy
+ // manifest merger. Remove this once enough users migrate over to the new manifest merger.
+ if (manifestOut != null) {
+ builder.addExecPath("--manifestOutput", manifestOut);
+ outs.add(manifestOut);
+ }
+
+ 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);
+ }
+
+ SpawnAction.Builder spawnActionBuilder = new SpawnAction.Builder();
+ // Create the spawn action.
+ ruleContext.registerAction(
+ spawnActionBuilder
+ .addTransitiveInputs(inputs.build())
+ .addOutputs(ImmutableList.copyOf(outs))
+ .setCommandLine(builder.build())
+ .setExecutable(
+ ruleContext.getExecutablePrerequisite("$android_resource_merger", Mode.HOST))
+ .setProgressMessage("Merging Android resources for " + ruleContext.getLabel())
+ .setMnemonic("AndroidResourceMerger")
+ .build(context));
+
+ // Return the full set of processed transitive dependencies.
+ // TODO(jvoung): pass the classJar out -- once that is a field of ResourceContainer.
+ return new ResourceContainer(
+ primary.getLabel(),
+ primary.getJavaPackage(),
+ primary.getRenameManifestPackage(),
+ primary.getConstantsInlined(),
+ primary.getApk(),
+ manifestOut != null ? manifestOut : primary.getManifest(),
+ primary.getJavaSourceJar(),
+ primary.getArtifacts(ResourceType.ASSETS),
+ primary.getArtifacts(ResourceType.RESOURCES),
+ primary.getRoots(ResourceType.ASSETS),
+ primary.getRoots(ResourceType.RESOURCES),
+ primary.isManifestExported(),
+ primary.getRTxt(),
+ primary.getSymbolsTxt());
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
index 3c2b915920..789c3dcacf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
@@ -24,6 +24,7 @@ import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
+import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.Builder.SeparatorType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -50,6 +51,7 @@ public class AndroidResourcesProcessorBuilder {
ResourceContainerConverter.builder()
.includeResourceRoots()
.includeManifest()
+ .withSeparator(SeparatorType.COLON_COMMA)
.toArgConverter();
private static final ResourceContainerConverter.ToArg RESOURCE_DEP_TO_ARG =
@@ -58,6 +60,7 @@ public class AndroidResourcesProcessorBuilder {
.includeManifest()
.includeRTxt()
.includeSymbolsBin()
+ .withSeparator(SeparatorType.COLON_COMMA)
.toArgConverter();
private ResourceContainer primary;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index dc1312662e..6e14804023 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -169,6 +169,7 @@ public final class AndroidRuleClasses {
public static final String DEFAULT_MANIFEST_MERGER = "//tools/android:manifest_merger";
public static final String DEFAULT_RCLASS_GENERATOR = "//tools/android:rclass_generator";
public static final String DEFAULT_RESOURCES_PROCESSOR = "//tools/android:resources_processor";
+ public static final String DEFAULT_RESOURCE_MERGER = "//tools/android:resource_merger";
public static final String DEFAULT_RESOURCE_PARSER = "//tools/android:resource_parser";
public static final String DEFAULT_RESOURCE_SHRINKER = "//tools/android:resource_shrinker";
public static final String DEFAULT_SDK = "//tools/android:sdk";
@@ -408,6 +409,8 @@ public final class AndroidRuleClasses {
env.getToolsLabel(DEFAULT_RCLASS_GENERATOR)))
.add(attr("$android_resources_processor", LABEL).cfg(HOST).exec().value(
env.getToolsLabel(DEFAULT_RESOURCES_PROCESSOR)))
+ .add(attr("$android_resource_merger", LABEL).cfg(HOST).exec().value(
+ env.getToolsLabel(DEFAULT_RESOURCE_MERGER)))
.add(attr("$android_resource_parser", LABEL).cfg(HOST).exec().value(
env.getToolsLabel(DEFAULT_RESOURCE_PARSER)))
.add(attr("$android_resource_shrinker", LABEL).cfg(HOST).exec().value(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java
index b271e90861..d86ada91c5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceContainerConverter.java
@@ -17,6 +17,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -42,24 +43,44 @@ public class ResourceContainerConverter {
return new Builder();
}
- interface ToArg extends Function<ResourceContainer, String> {}
+ interface ToArg extends Function<ResourceContainer, String> {
- interface ToArtifacts extends Function<ResourceContainer, NestedSet<Artifact>> {}
+ String listSeparator();
+ }
+
+ interface ToArtifacts extends Function<ResourceContainer, NestedSet<Artifact>> {
+
+ }
static class Builder {
private boolean includeResourceRoots;
+ private boolean includeLabel;
private boolean includeManifest;
private boolean includeRTxt;
private boolean includeSymbolsBin;
+ private SeparatorType separatorType;
+ private Joiner argJoiner;
+ private Function<String, String> escaper = Functions.identity();
+
+ enum SeparatorType {
+ COLON_COMMA,
+ SEMICOLON_AMPERSAND
+ }
- Builder() {}
+ Builder() {
+ }
Builder includeResourceRoots() {
includeResourceRoots = true;
return this;
}
+ Builder includeLabel() {
+ includeLabel = true;
+ return this;
+ }
+
Builder includeManifest() {
includeManifest = true;
return this;
@@ -75,9 +96,35 @@ public class ResourceContainerConverter {
return this;
}
- private static final Joiner ARG_JOINER = Joiner.on(":");
+ Builder withSeparator(SeparatorType type) {
+ separatorType = type;
+ return this;
+ }
ToArg toArgConverter() {
+ switch (separatorType) {
+ case COLON_COMMA:
+ argJoiner = Joiner.on(":");
+ // We currently use ":" to separate components of an argument and "," to separate
+ // arguments in a list of arguments. Those characters require escaping if used in a label
+ // (part of the set of allowed characters in a label).
+ if (includeLabel) {
+ escaper = new Function<String, String>() {
+ @Override
+ public String apply(String input) {
+ return input.replace(":", "\\:").replace(",", "\\,");
+ }
+ };
+ }
+ break;
+ case SEMICOLON_AMPERSAND:
+ argJoiner = Joiner.on(";");
+ break;
+ default:
+ Preconditions.checkState(false, "Unknown separator type " + separatorType);
+ break;
+ }
+
return new ToArg() {
@Override
public String apply(ResourceContainer container) {
@@ -86,6 +133,9 @@ public class ResourceContainerConverter {
cmdPieces.add(convertRoots(container, ResourceType.RESOURCES));
cmdPieces.add(convertRoots(container, ResourceType.ASSETS));
}
+ if (includeLabel) {
+ cmdPieces.add(escaper.apply(container.getLabel().toString()));
+ }
if (includeManifest) {
cmdPieces.add(container.getManifest().getExecPathString());
}
@@ -99,7 +149,20 @@ public class ResourceContainerConverter {
? ""
: container.getSymbolsTxt().getExecPathString());
}
- return ARG_JOINER.join(cmdPieces.build());
+ return argJoiner.join(cmdPieces.build());
+ }
+
+ @Override
+ public String listSeparator() {
+ switch (separatorType) {
+ case COLON_COMMA:
+ return ",";
+ case SEMICOLON_AMPERSAND:
+ return "&";
+ default:
+ Preconditions.checkState(false, "Unknown separator type " + separatorType);
+ return null;
+ }
}
};
}
@@ -161,7 +224,7 @@ public class ResourceContainerConverter {
if (!dependencies.getTransitiveResources().isEmpty()) {
cmdBuilder.addJoinStrings(
"--data",
- ",",
+ toArg.listSeparator(),
Iterables.unmodifiableIterable(
Iterables.transform(dependencies.getTransitiveResources(), toArg)));
}
@@ -170,7 +233,7 @@ public class ResourceContainerConverter {
if (!dependencies.getDirectResources().isEmpty()) {
cmdBuilder.addJoinStrings(
"--directData",
- ",",
+ toArg.listSeparator(),
Iterables.unmodifiableIterable(
Iterables.transform(dependencies.getDirectResources(), toArg)));
}