Refactor CommandHelper.java
Extract language specific functions to an interface.
Working towards: https://github.com/bazelbuild/bazel/issues/7503
Closes #9022.
PiperOrigin-RevId: 260910470
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BashCommandConstructor.java b/src/main/java/com/google/devtools/build/lib/analysis/BashCommandConstructor.java
new file mode 100644
index 0000000..41b2488
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BashCommandConstructor.java
@@ -0,0 +1,50 @@
+// Copyright 2019 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.analysis;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/** The class for constructing command line for Bash. */
+public final class BashCommandConstructor implements CommandConstructor {
+
+ private final PathFragment shellPath;
+ private final String scriptNameSuffix;
+
+ BashCommandConstructor(PathFragment shellPath, String scriptNameSuffix) {
+ this.shellPath = shellPath;
+ this.scriptNameSuffix = scriptNameSuffix;
+ }
+
+ @Override
+ public ImmutableList<String> asExecArgv(Artifact scriptFileArtifact) {
+ return ImmutableList.of(shellPath.getPathString(), scriptFileArtifact.getExecPathString());
+ }
+
+ @Override
+ public ImmutableList<String> asExecArgv(String command) {
+ return ImmutableList.of(shellPath.getPathString(), "-c", command);
+ }
+
+ @Override
+ public Artifact commandAsScript(RuleContext ruleContext, String command) {
+ String scriptFileName = ruleContext.getTarget().getName() + scriptNameSuffix;
+ String scriptFileContents = "#!/bin/bash\n" + command;
+ return FileWriteAction.createFile(
+ ruleContext, scriptFileName, scriptFileContents, /*executable=*/ true);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommandConstructor.java b/src/main/java/com/google/devtools/build/lib/analysis/CommandConstructor.java
new file mode 100644
index 0000000..6fd2023
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommandConstructor.java
@@ -0,0 +1,37 @@
+// Copyright 2019 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.analysis;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+
+/**
+ * The interface to construct command line for different shells (Bash, Batch, Powershell). Used in
+ * {@link com.google.devtools.build.lib.analysis.CommandHelper}
+ */
+public interface CommandConstructor {
+
+ /**
+ * Given a string of command, return the arguments to run the command. eg. For Bash command,
+ * asExecArgv("foo bar") -> ["/bin/bash", "-c", "foo bar"]
+ */
+ ImmutableList<String> asExecArgv(String command);
+
+ /** Given an artifact of a script, return the arguments to run this command. */
+ ImmutableList<String> asExecArgv(Artifact scriptFileArtifact);
+
+ /** Write the command to a script and return the artifact of the script. */
+ Artifact commandAsScript(RuleContext ruleContext, String command);
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
index 2cd8184..4572e11 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
@@ -23,7 +23,6 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecutionRequirements;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
-import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -283,38 +282,19 @@
}
private static Pair<List<String>, Artifact> buildCommandLineMaybeWithScriptFile(
- RuleContext ruleContext, String command, String scriptPostFix, PathFragment shellPath) {
+ RuleContext ruleContext, String command, CommandConstructor constructor) {
List<String> argv;
Artifact scriptFileArtifact = null;
if (command.length() <= maxCommandLength) {
- argv = buildCommandLineSimpleArgv(command, shellPath);
+ argv = constructor.asExecArgv(command);
} else {
// Use script file.
- scriptFileArtifact = buildCommandLineArtifact(ruleContext, command, scriptPostFix);
- argv = buildCommandLineArgvWithArtifact(scriptFileArtifact, shellPath);
+ scriptFileArtifact = constructor.commandAsScript(ruleContext, command);
+ argv = constructor.asExecArgv(scriptFileArtifact);
}
return Pair.of(argv, scriptFileArtifact);
}
- private static ImmutableList<String> buildCommandLineArgvWithArtifact(Artifact scriptFileArtifact,
- PathFragment shellPath) {
- return ImmutableList.of(shellPath.getPathString(), scriptFileArtifact.getExecPathString());
- }
-
- private static Artifact buildCommandLineArtifact(RuleContext ruleContext, String command,
- String scriptPostFix) {
- String scriptFileName = ruleContext.getTarget().getName() + scriptPostFix;
- String scriptFileContents = "#!/bin/bash\n" + command;
- Artifact scriptFileArtifact = FileWriteAction.createFile(
- ruleContext, scriptFileName, scriptFileContents, /*executable=*/true);
- return scriptFileArtifact;
- }
-
- private static ImmutableList<String> buildCommandLineSimpleArgv(String command,
- PathFragment shellPath) {
- return ImmutableList.of(shellPath.getPathString(), "-c", command);
- }
-
/**
* If {@code command} is too long, creates a helper shell script that runs that command.
*
@@ -324,49 +304,24 @@
* this method does nothing and returns null.
*/
@Nullable
- public static Artifact shellCommandHelperScriptMaybe(
- RuleContext ruleCtx,
- String command,
- String scriptPostFix,
- Map<String, String> executionInfo) {
+ public static Artifact commandHelperScriptMaybe(
+ RuleContext ruleCtx, String command, CommandConstructor constructor) {
if (command.length() <= maxCommandLength) {
return null;
} else {
- return buildCommandLineArtifact(ruleCtx, command, scriptPostFix);
+ return constructor.commandAsScript(ruleCtx, command);
}
}
/**
- * Builds the set of command-line arguments. Creates a bash script if the command line is longer
- * than the allowed maximum {@link #maxCommandLength}. Fixes up the input artifact list with the
- * created bash script when required.
- */
- public List<String> buildCommandLine(
- PathFragment shExecutable,
- String command,
- NestedSetBuilder<Artifact> inputs,
- String scriptPostFix) {
- return buildCommandLine(
- shExecutable, command, inputs, scriptPostFix, ImmutableMap.<String, String>of());
- }
-
- /**
* Builds the set of command-line arguments using the specified shell path. Creates a bash script
* if the command line is longer than the allowed maximum {@link #maxCommandLength}. Fixes up the
* input artifact list with the created bash script when required.
- *
- * @param executionInfo an execution info map of the action associated with the command line to be
- * built.
*/
public List<String> buildCommandLine(
- PathFragment shExecutable,
- String command,
- NestedSetBuilder<Artifact> inputs,
- String scriptPostFix,
- Map<String, String> executionInfo) {
+ String command, NestedSetBuilder<Artifact> inputs, CommandConstructor constructor) {
Pair<List<String>, Artifact> argvAndScriptFile =
- buildCommandLineMaybeWithScriptFile(
- ruleContext, command, scriptPostFix, shellPath(executionInfo, shExecutable));
+ buildCommandLineMaybeWithScriptFile(ruleContext, command, constructor);
if (argvAndScriptFile.second != null) {
inputs.add(argvAndScriptFile.second);
}
@@ -379,14 +334,9 @@
* created bash script when required.
*/
public List<String> buildCommandLine(
- PathFragment shExecutable,
- String command,
- List<Artifact> inputs,
- String scriptPostFix,
- Map<String, String> executionInfo) {
+ String command, List<Artifact> inputs, CommandConstructor constructor) {
Pair<List<String>, Artifact> argvAndScriptFile =
- buildCommandLineMaybeWithScriptFile(
- ruleContext, command, scriptPostFix, shellPath(executionInfo, shExecutable));
+ buildCommandLineMaybeWithScriptFile(ruleContext, command, constructor);
if (argvAndScriptFile.second != null) {
inputs.add(argvAndScriptFile.second);
}
@@ -394,10 +344,16 @@
}
/** Returns the path to the shell for an action with the given execution requirements. */
- private PathFragment shellPath(Map<String, String> executionInfo, PathFragment shExecutable) {
+ private static PathFragment shellPath(
+ Map<String, String> executionInfo, PathFragment shExecutable) {
// Use vanilla /bin/bash for actions running on mac machines.
return executionInfo.containsKey(ExecutionRequirements.REQUIRES_DARWIN)
? PathFragment.create("/bin/bash")
: shExecutable;
}
+
+ public static BashCommandConstructor buildBashCommandConstructor(
+ Map<String, String> executionInfo, PathFragment shExecutable, String scriptPostFix) {
+ return new BashCommandConstructor(shellPath(executionInfo, shExecutable), scriptPostFix);
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionSpec.java b/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionSpec.java
index 5c17eff..56b1786 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionSpec.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionSpec.java
@@ -23,6 +23,7 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLine;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
+import com.google.devtools.build.lib.analysis.BashCommandConstructor;
import com.google.devtools.build.lib.analysis.CommandHelper;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
@@ -129,13 +130,10 @@
actionToShadow.getPrimaryOutput().getExecPath().getBaseName()
+ "."
+ actionToShadow.getKey(owningRule.getActionKeyContext());
- List<String> argv =
- commandHelper.buildCommandLine(
- shExecutable,
- command,
- extraActionInputs,
- "." + actionUniquifier + ".extra_action_script.sh",
- executionInfo);
+ BashCommandConstructor constructor =
+ CommandHelper.buildBashCommandConstructor(
+ executionInfo, shExecutable, "." + actionUniquifier + ".extra_action_script.sh");
+ List<String> argv = commandHelper.buildCommandLine(command, extraActionInputs, constructor);
String commandMessage = String.format("Executing extra_action %s on %s", label, ownerLabel);
owningRule.registerAction(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
index 16dea75..9cfd02e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkActionFactory.java
@@ -34,6 +34,7 @@
import com.google.devtools.build.lib.actions.SingleStringArgFormatter;
import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
import com.google.devtools.build.lib.actions.extra.SpawnInfo;
+import com.google.devtools.build.lib.analysis.BashCommandConstructor;
import com.google.devtools.build.lib.analysis.CommandHelper;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.PseudoAction;
@@ -324,10 +325,12 @@
ImmutableMap.copyOf(TargetUtils.getExecutionInfo(ruleContext.getRule()));
String helperScriptSuffix = String.format(".run_shell_%d.sh", runShellOutputCounter++);
String command = (String) commandUnchecked;
- Artifact helperScript =
- CommandHelper.shellCommandHelperScriptMaybe(
- ruleContext, command, helperScriptSuffix, executionInfo);
PathFragment shExecutable = ShToolchain.getPathOrError(ruleContext);
+ BashCommandConstructor constructor =
+ CommandHelper.buildBashCommandConstructor(
+ executionInfo, shExecutable, helperScriptSuffix);
+ Artifact helperScript =
+ CommandHelper.commandHelperScriptMaybe(ruleContext, command, constructor);
if (helperScript == null) {
builder.setShellCommand(shExecutable, command);
} else {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
index d9c821e..51e1980 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleContext.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.analysis.ActionsProvider;
import com.google.devtools.build.lib.analysis.AliasProvider;
+import com.google.devtools.build.lib.analysis.BashCommandConstructor;
import com.google.devtools.build.lib.analysis.CommandHelper;
import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
import com.google.devtools.build.lib.analysis.DefaultInfo;
@@ -1067,15 +1068,15 @@
String.class,
"execution_requirements"));
PathFragment shExecutable = ShToolchain.getPathOrError(ruleContext);
- List<String> argv =
- helper.buildCommandLine(
+
+ BashCommandConstructor constructor =
+ CommandHelper.buildBashCommandConstructor(
+ executionRequirements,
shExecutable,
- command,
- inputs,
// Hash the command-line to prevent multiple actions from the same rule invocation
// conflicting with each other.
- "." + Hashing.murmur3_32().hashUnencodedChars(command).toString() + SCRIPT_SUFFIX,
- executionRequirements);
+ "." + Hashing.murmur3_32().hashUnencodedChars(command).toString() + SCRIPT_SUFFIX);
+ List<String> argv = helper.buildCommandLine(command, inputs, constructor);
return Tuple.<Object>of(
MutableList.copyOf(env, inputs),
MutableList.copyOf(env, argv),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
index 37f048c..1557657 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
@@ -23,6 +23,7 @@
import com.google.devtools.build.lib.actions.CompositeRunfilesSupplier;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AliasProvider;
+import com.google.devtools.build.lib.analysis.CommandConstructor;
import com.google.devtools.build.lib.analysis.CommandHelper;
import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
@@ -167,13 +168,12 @@
if (ruleContext.hasErrors()) {
return null;
}
- List<String> argv =
- commandHelper.buildCommandLine(
- shExecutable,
- command,
- inputs,
- ".genrule_script.sh",
- ImmutableMap.copyOf(executionInfo));
+
+ CommandConstructor constructor =
+ CommandHelper.buildBashCommandConstructor(
+ executionInfo, shExecutable, ".genrule_script.sh");
+
+ List<String> argv = commandHelper.buildCommandLine(command, inputs, constructor);
if (isStampingEnabled(ruleContext)) {
inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact());