diff options
author | asteinb <asteinb@google.com> | 2018-04-17 07:09:31 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-04-17 07:11:07 -0700 |
commit | b701530ca8beaf190143b502828f4d91132981b1 (patch) | |
tree | 573ce823952eb297fe4e65aa9ca3511bedc57c9d /src/main/java/com/google/devtools/build/lib/rules | |
parent | 451cfecf840c1148aae557f9f133f6a884d8411a (diff) |
Independently merge assets
Merges assets using the newly created action.
Also, add BusyBoxActionBuilder to collect common boilerplate for spawning an
action that invokes the Android busybox.
RELNOTES: none
PiperOrigin-RevId: 193192621
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules')
7 files changed, 270 insertions, 6 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java index f7e0273694..a20b818cc1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java @@ -128,6 +128,12 @@ public class AndroidAssets { return ParsedAndroidAssets.parseFrom(ruleContext, this); } + /** Convenience method to do all of asset processing - parsing and merging. */ + public MergedAndroidAssets process(RuleContext ruleContext, boolean neverlink) + throws InterruptedException { + return parse(ruleContext).merge(ruleContext, neverlink); + } + @Override public boolean equals(Object object) { if (object == null || getClass() != object.getClass()) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataConverter.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataConverter.java index a7ecccd2e2..c6f2d5cc26 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataConverter.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataConverter.java @@ -32,6 +32,18 @@ import java.util.stream.Collectors; */ public class AndroidDataConverter<T> extends ParametrizedMapFn<T> { + /** + * Converts Android data to the "SerializedAndroidData" format used by the Android data processing + * actions. + */ + static final AndroidDataConverter<MergableAndroidData> MERGABLE_DATA_CONVERTER = + AndroidDataConverter.<MergableAndroidData>builder(JoinerType.SEMICOLON_AMPERSAND) + .withRoots(MergableAndroidData::getResourceRoots) + .withRoots(MergableAndroidData::getAssetRoots) + .withLabel(MergableAndroidData::getLabel) + .withArtifact(MergableAndroidData::getSymbols) + .build(); + /** Indicates the type of joiner between options expected by the command line. */ public enum JoinerType { COLON_COMMA(":", ","), 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 328ba14ed6..c1b1239467 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 @@ -41,12 +41,7 @@ import javax.annotation.Nullable; public class AndroidResourceMergingActionBuilder { private static final AndroidDataConverter<MergableAndroidData> RESOURCE_CONTAINER_TO_ARG = - AndroidDataConverter.<MergableAndroidData>builder(JoinerType.SEMICOLON_AMPERSAND) - .withRoots(MergableAndroidData::getResourceRoots) - .withRoots(MergableAndroidData::getAssetRoots) - .withLabel(MergableAndroidData::getLabel) - .withArtifact(MergableAndroidData::getSymbols) - .build(); + AndroidDataConverter.MERGABLE_DATA_CONVERTER; private static final AndroidDataConverter<CompiledMergableAndroidData> RESOURCE_CONTAINER_TO_ARG_FOR_COMPILED = 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 433fc7f562..b40413e116 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 @@ -111,6 +111,8 @@ public final class AndroidRuleClasses { fromTemplates("%{name}_shrunk.ap_"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_ZIP = fromTemplates("%{name}_files/resource_files.zip"); + public static final SafeImplicitOutputsFunction ANDROID_ASSETS_ZIP = + fromTemplates("%{name}_files/assets.zip"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SHRUNK_ZIP = fromTemplates("%{name}_files/resource_files_shrunk.zip"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCE_SHRINKER_LOG = diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java new file mode 100644 index 0000000000..463022c295 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java @@ -0,0 +1,137 @@ +// Copyright 2018 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.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.ParamFileInfo; +import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.util.OS; +import com.google.errorprone.annotations.CompileTimeConstant; + +/** Builder for actions that invoke the Android BusyBox. */ +public final class BusyBoxActionBuilder { + + // Some flags (e.g. --mainData) may specify lists (or lists of lists) separated by special + // characters (colon, semicolon, hashmark, ampersand) that don't work on Windows, and quoting + // semantics are very complicated (more so than in Bash), so let's just always use a parameter + // file. + // TODO(laszlocsomor), TODO(corysmith): restructure the Android BusyBux's flags by deprecating + // list-type and list-of-list-type flags that use such problematic separators in favor of + // multi-value flags (to remove one level of listing) and by changing all list separators to a + // platform-safe character (= comma). + private static final ParamFileInfo FORCED_PARAM_FILE_INFO = + ParamFileInfo.builder(ParameterFileType.SHELL_QUOTED) + .setUseAlways(OS.getCurrent() == OS.WINDOWS) + .build(); + + private final RuleContext ruleContext; + private final NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder(); + private final ImmutableList.Builder<Artifact> outputs = ImmutableList.builder(); + private final CustomCommandLine.Builder commandLine = CustomCommandLine.builder(); + + public static BusyBoxActionBuilder create( + RuleContext ruleContext, @CompileTimeConstant String toolName) { + BusyBoxActionBuilder builder = new BusyBoxActionBuilder(ruleContext); + builder.commandLine.add("--tool").add(toolName).add("--"); + return builder; + } + + private BusyBoxActionBuilder(RuleContext ruleContext) { + this.ruleContext = ruleContext; + } + + public BusyBoxActionBuilder addInput(@CompileTimeConstant String arg, Artifact value) { + commandLine.addExecPath(arg, value); + inputs.add(value); + return this; + } + + /** + * Adds a series of input artifacts. For efficiency, when adding a NestedSet of artifacts, use + * {@link #addTransitiveFlag(String, NestedSet, AndroidDataConverter)} and {@link + * #addTransitiveInputValues(NestedSet)} instead. + */ + public BusyBoxActionBuilder addInput( + @CompileTimeConstant String arg, String value, Iterable<Artifact> valueArtifacts) { + commandLine.add(arg, value); + inputs.addAll(valueArtifacts); + return this; + } + + public BusyBoxActionBuilder addOutput(@CompileTimeConstant String arg, Artifact value) { + commandLine.addExecPath(arg, value); + outputs.add(value); + return this; + } + + /** + * Adds a series of transitive input artifacts. + * + * <p>These artifacts will not be mentioned on the command line - use {@link + * #addTransitiveFlag(String, NestedSet, AndroidDataConverter)} for that. + */ + public BusyBoxActionBuilder addTransitiveInputValues(NestedSet<Artifact> values) { + inputs.addTransitive(values); + return this; + } + + /** + * Adds an efficient flag based on transitive values. + * + * <p>The values will only be collapsed and turned into a flag at execution time. + * + * <p>The values will not be added as inputs - use {@link #addTransitiveInputValues(NestedSet)} + * for that. + */ + public <T> BusyBoxActionBuilder addTransitiveFlag( + @CompileTimeConstant String arg, + NestedSet<? extends T> transitiveValues, + AndroidDataConverter<T> converter) { + commandLine.addAll(arg, converter.getVectorArg(transitiveValues)); + return this; + } + + public BusyBoxActionBuilder addFlag(@CompileTimeConstant String value) { + commandLine.add(value); + return this; + } + + /** + * Builds and registers this action. + * + * @param message a progress message (visible in Bazel output), for example "Running tool". The + * current label will be appended to this message. + * @param mnemonic a mnemonic used to indicate the tool being run, for example, "BusyBoxTool". + */ + public void buildAndRegister(String message, String mnemonic) { + ruleContext.registerAction( + new SpawnAction.Builder() + .useDefaultShellEnvironment() + .addTransitiveInputs(inputs.build()) + .addOutputs(outputs.build()) + .addCommandLine(commandLine.build(), FORCED_PARAM_FILE_INFO) + .setExecutable( + ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST)) + .setProgressMessage("%s for %s", message, ruleContext.getLabel()) + .setMnemonic(mnemonic) + .build(ruleContext)); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidAssets.java b/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidAssets.java new file mode 100644 index 0000000000..ccffbc3d25 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/MergedAndroidAssets.java @@ -0,0 +1,102 @@ +// Copyright 2018 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.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleContext; +import java.util.Objects; + +/** Android assets that have been merged together with their dependencies. */ +public class MergedAndroidAssets extends ParsedAndroidAssets { + private final Artifact mergedAssets; + private final AssetDependencies assetDependencies; + + public static MergedAndroidAssets mergeFrom( + RuleContext ruleContext, ParsedAndroidAssets parsed, boolean neverlink) + throws InterruptedException { + return mergeFrom(ruleContext, parsed, AssetDependencies.fromRuleDeps(ruleContext, neverlink)); + } + + @VisibleForTesting + static MergedAndroidAssets mergeFrom( + RuleContext ruleContext, ParsedAndroidAssets parsed, AssetDependencies deps) + throws InterruptedException { + + Artifact mergedAssets = + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_ASSETS_ZIP); + + BusyBoxActionBuilder builder = BusyBoxActionBuilder.create(ruleContext, "MERGE_ASSETS"); + if (AndroidCommon.getAndroidConfig(ruleContext).throwOnResourceConflict()) { + builder.addFlag("--throwOnAssetConflict"); + } + + builder + .addOutput("--assetsOutput", mergedAssets) + .addInput("--androidJar", AndroidSdkProvider.fromRuleContext(ruleContext).getAndroidJar()) + .addInput( + "--primaryData", + AndroidDataConverter.MERGABLE_DATA_CONVERTER.map(parsed), + Iterables.concat(parsed.getAssets(), ImmutableList.of(parsed.getSymbols()))) + .addTransitiveFlag( + "--directData", + deps.getDirectParsedAssets(), + AndroidDataConverter.MERGABLE_DATA_CONVERTER) + .addTransitiveFlag( + "--data", + deps.getTransitiveParsedAssets(), + AndroidDataConverter.MERGABLE_DATA_CONVERTER) + .addTransitiveInputValues(deps.getTransitiveAssets()) + .addTransitiveInputValues(deps.getTransitiveSymbols()) + .buildAndRegister("Merging Android assets", "AndroidAssetMerger"); + + return new MergedAndroidAssets(parsed, mergedAssets, deps); + } + + private MergedAndroidAssets( + ParsedAndroidAssets parsed, Artifact mergedAssets, AssetDependencies assetDependencies) { + super(parsed); + this.mergedAssets = mergedAssets; + this.assetDependencies = assetDependencies; + } + + AndroidAssetsInfo toProvider() { + // Create a new object to avoid passing around unwanted merge information to the provider + ParsedAndroidAssets parsed = new ParsedAndroidAssets(this); + return assetDependencies.toInfo(parsed); + } + + public Artifact getMergedAssets() { + return mergedAssets; + } + + @Override + public boolean equals(Object object) { + if (!super.equals(object)) { + return false; + } + + MergedAndroidAssets other = (MergedAndroidAssets) object; + + return mergedAssets.equals(other.mergedAssets) + && assetDependencies.equals(other.assetDependencies); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mergedAssets, assetDependencies); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidAssets.java b/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidAssets.java index 6a83642483..3419ceeca5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidAssets.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidAssets.java @@ -34,12 +34,22 @@ public class ParsedAndroidAssets extends AndroidAssets implements MergableAndroi return new ParsedAndroidAssets(assets, symbols, label); } + ParsedAndroidAssets(ParsedAndroidAssets other) { + this(other, other.symbols, other.label); + } + private ParsedAndroidAssets(AndroidAssets other, Artifact symbols, Label label) { super(other); this.symbols = symbols; this.label = label; } + /** Merges these assets with assets from dependencies. */ + public MergedAndroidAssets merge(RuleContext ruleContext, boolean neverlink) + throws InterruptedException { + return MergedAndroidAssets.mergeFrom(ruleContext, this, neverlink); + } + @Override public Label getLabel() { return label; |