diff options
Diffstat (limited to 'src/main/java')
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))); } |