// 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 inputs = NestedSetBuilder.naiveLinkOrder(); private final ImmutableList.Builder 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 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. * *

These artifacts will not be mentioned on the command line - use {@link * #addTransitiveFlag(String, NestedSet, AndroidDataConverter)} for that. */ public BusyBoxActionBuilder addTransitiveInputValues(NestedSet values) { inputs.addTransitive(values); return this; } /** * Adds an efficient flag based on transitive values. * *

The values will only be collapsed and turned into a flag at execution time. * *

The values will not be added as inputs - use {@link #addTransitiveInputValues(NestedSet)} * for that. */ public BusyBoxActionBuilder addTransitiveFlag( @CompileTimeConstant String arg, NestedSet transitiveValues, AndroidDataConverter 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)); } }