Use backslashes in executable paths when remotely executing on Windows
`.bat` scripts can only be executed on Windows when their paths use backslashes, not forward slashes. For this reason, local execution carefully replaces forward slashes with backslashes in the executable of a `Spawn` when executing on Windows.
This commit adds equivalent logic for the case of remote execution on Windows from any host:
* Remote execution replaces forward slashes with backslashes in the first argument when executing on Windows.
* Most calls to `PathFragment#getCallablePathString` are replaced with the new execution platform aware `getCallablePathStringForOs`. This affects test actions, in which various wrappers execute different executables (e.g. `--run_under` targets) and thus can have Bazel-controlled executable path strings in locations other than the `argv[0]`.
Fixes #11636
Closes #23986.
PiperOrigin-RevId: 689357323
Change-Id: Ifb842babc02c6d741d3b45914a5bf5c032204e2b
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java
index 362c31f..0cd7dcc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintConstants.java
@@ -13,7 +13,8 @@
// limitations under the License.
package com.google.devtools.build.lib.analysis.constraints;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.devtools.build.lib.analysis.platform.ConstraintCollection;
import com.google.devtools.build.lib.analysis.platform.ConstraintSettingInfo;
import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
import com.google.devtools.build.lib.cmdline.Label;
@@ -29,8 +30,12 @@
Label.parseCanonicalUnchecked("@platforms//os:os"));
// Standard mapping between OS and the corresponding platform constraints.
- public static final ImmutableMap<OS, ConstraintValueInfo> OS_TO_CONSTRAINTS =
- ImmutableMap.<OS, ConstraintValueInfo>builder()
+ public static final ImmutableBiMap<OS, ConstraintValueInfo> OS_TO_CONSTRAINTS =
+ ImmutableBiMap.<OS, ConstraintValueInfo>builder()
+ .put(
+ OS.LINUX,
+ ConstraintValueInfo.create(
+ OS_CONSTRAINT_SETTING, Label.parseCanonicalUnchecked("@platforms//os:linux")))
.put(
OS.DARWIN,
ConstraintValueInfo.create(
@@ -58,6 +63,19 @@
Label.parseCanonicalUnchecked("@platforms//os:none")))
.buildOrThrow();
+ /**
+ * Returns the OS corresponding to the given constraint collection based on the contained platform
+ * constraint.
+ */
+ public static OS getOsFromConstraints(ConstraintCollection constraintCollection) {
+ if (!constraintCollection.has(OS_CONSTRAINT_SETTING)) {
+ return OS.getCurrent();
+ }
+ return OS_TO_CONSTRAINTS
+ .inverse()
+ .getOrDefault(constraintCollection.get(OS_CONSTRAINT_SETTING), OS.getCurrent());
+ }
+
// No-op constructor to keep this from being instantiated.
private ConstraintConstants() {}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
index 7040015..cf9fcd5 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
@@ -433,8 +433,7 @@
run,
config,
ruleContext.getWorkspaceName(),
- (!isUsingTestWrapperInsteadOfTestSetupScript
- || executionSettings.needsShell(ruleContext.isExecutedOnWindows()))
+ (!isUsingTestWrapperInsteadOfTestSetupScript || executionSettings.needsShell())
? ShToolchain.getPathForPlatform(
ruleContext.getConfiguration(), ruleContext.getExecutionPlatform())
: null,
@@ -447,8 +446,7 @@
MoreObjects.firstNonNull(
Allowlist.fetchPackageSpecificationProviderOrNull(
ruleContext, "external_network"),
- PackageSpecificationProvider.EMPTY),
- ruleContext.isExecutedOnWindows());
+ PackageSpecificationProvider.EMPTY));
testOutputs.addAll(testRunnerAction.getSpawnOutputs());
testOutputs.addAll(testRunnerAction.getOutputs());
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index 99e4d10..1575a8e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -147,8 +147,6 @@
private final int runNumber;
private final String workspaceName;
- private final boolean isExecutedOnWindows;
-
/**
* Cached test result status used to minimize disk accesses. This field is set when test status is
* retrieved from disk or saved to disk. This field is null if it has not been set yet. This field
@@ -216,8 +214,7 @@
boolean splitCoveragePostProcessing,
NestedSetBuilder<Artifact> lcovMergerFilesToRun,
@Nullable Artifact lcovMergerRunfilesMiddleman,
- PackageSpecificationProvider networkAllowlist,
- boolean isExecutedOnWindows) {
+ PackageSpecificationProvider networkAllowlist) {
super(
owner,
inputs,
@@ -308,8 +305,6 @@
getUndeclaredOutputsDir(),
undeclaredOutputsAnnotationsDir,
baseDir.getRelative("test_attempts"));
-
- this.isExecutedOnWindows = isExecutedOnWindows;
}
public boolean allowLocalTests() {
@@ -326,10 +321,6 @@
return true;
}
- public boolean isExecutedOnWindows() {
- return isExecutedOnWindows;
- }
-
public Artifact getRunfilesMiddleman() {
return runfilesMiddleman;
}
@@ -728,7 +719,10 @@
env.put("TEST_WORKSPACE", getRunfilesPrefix());
env.put(
"TEST_BINARY",
- getExecutionSettings().getExecutable().getRunfilesPath().getCallablePathString());
+ getExecutionSettings()
+ .getExecutable()
+ .getRunfilesPath()
+ .getCallablePathStringForOs(executionSettings.getExecutionOs()));
// When we run test multiple times, set different TEST_RANDOM_SEED values for each run.
// Don't override any previous setting.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
index aebd288..e7fb709 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
@@ -45,6 +45,7 @@
import com.google.devtools.build.lib.server.FailureDetails.TestAction.Code;
import com.google.devtools.build.lib.shell.TerminationStatus;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -211,12 +212,17 @@
public static ImmutableList<String> expandedArgsFromAction(TestRunnerAction testAction)
throws CommandLineExpansionException, InterruptedException {
List<String> args = Lists.newArrayList();
+ OS executionOs = testAction.getExecutionSettings().getExecutionOs();
Artifact testSetup = testAction.getTestSetupScript();
- args.add(testSetup.getExecPath().getCallablePathString());
+ args.add(testSetup.getExecPath().getCallablePathStringForOs(executionOs));
if (testAction.isCoverageMode()) {
- args.add(testAction.getCollectCoverageScript().getExecPathString());
+ args.add(
+ testAction
+ .getCollectCoverageScript()
+ .getExecPath()
+ .getCallablePathStringForOs(executionOs));
}
TestTargetExecutionSettings execSettings = testAction.getExecutionSettings();
@@ -227,6 +233,7 @@
}
// Execute the test using the alias in the runfiles tree, as mandated by the Test Encyclopedia.
+ // Do not use getCallablePathStringForOs as tw.exe expects a path with forward slashes.
args.add(execSettings.getExecutable().getRunfilesPath().getCallablePathString());
Iterables.addAll(args, execSettings.getArgs().arguments());
return ImmutableList.copyOf(args);
@@ -234,14 +241,20 @@
private static void addRunUnderArgs(TestRunnerAction testAction, List<String> args) {
TestTargetExecutionSettings execSettings = testAction.getExecutionSettings();
+ OS executionOs = execSettings.getExecutionOs();
if (execSettings.getRunUnderExecutable() != null) {
- args.add(execSettings.getRunUnderExecutable().getRunfilesPath().getCallablePathString());
+ args.add(
+ execSettings
+ .getRunUnderExecutable()
+ .getRunfilesPath()
+ .getCallablePathStringForOs(executionOs));
} else {
- if (execSettings.needsShell(testAction.isExecutedOnWindows())) {
+ if (execSettings.needsShell()) {
// TestActionBuilder constructs TestRunnerAction with a 'null' shell only when none is
// required. Something clearly went wrong.
Preconditions.checkNotNull(testAction.getShExecutableMaybe(), "%s", testAction);
- String shellExecutable = testAction.getShExecutableMaybe().getPathString();
+ String shellExecutable =
+ testAction.getShExecutableMaybe().getCallablePathStringForOs(executionOs);
args.add(shellExecutable);
args.add("-c");
args.add("\"$@\"");
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetExecutionSettings.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetExecutionSettings.java
index e5f69dd..1ba2ebe 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetExecutionSettings.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetExecutionSettings.java
@@ -25,7 +25,9 @@
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.analysis.config.RunUnder;
+import com.google.devtools.build.lib.analysis.constraints.ConstraintConstants;
import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.vfs.Path;
import javax.annotation.Nullable;
@@ -48,6 +50,7 @@
private final Artifact runfilesInputManifest;
private final Artifact instrumentedFileManifest;
private final boolean testRunnerFailFast;
+ private final OS executionOs;
TestTargetExecutionSettings(
RuleContext ruleContext,
@@ -79,6 +82,8 @@
this.runfiles = runfilesSupport.getRunfiles();
this.runfilesInputManifest = runfilesSupport.getRunfilesInputManifest();
this.instrumentedFileManifest = instrumentedFileManifest;
+ this.executionOs =
+ ConstraintConstants.getOsFromConstraints(ruleContext.getExecutionPlatform().constraints());
}
@Nullable
@@ -156,7 +161,11 @@
return instrumentedFileManifest;
}
- public boolean needsShell(boolean executedOnWindows) {
+ public OS getExecutionOs() {
+ return executionOs;
+ }
+
+ public boolean needsShell() {
RunUnder r = getRunUnder();
if (r == null) {
return false;
@@ -168,6 +177,6 @@
// --run_under commands that do not contain '/' are either shell built-ins or need to be
// located on the PATH env, so we wrap them in a shell invocation. Note that we shell-tokenize
// the --run_under parameter and getCommand only returns the first such token.
- return !command.contains("/") && (!executedOnWindows || !command.contains("\\"));
+ return !command.contains("/") && (!executionOs.equals(OS.WINDOWS) || !command.contains("\\"));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
index 1d3dff4..455f422 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
@@ -443,7 +443,10 @@
TestRunnerAction action, ImmutableMap<String, String> testEnv, SpawnResult result) {
ImmutableList<String> args =
ImmutableList.of(
- action.getTestXmlGeneratorScript().getExecPath().getCallablePathString(),
+ action
+ .getTestXmlGeneratorScript()
+ .getExecPath()
+ .getCallablePathStringForOs(action.getExecutionSettings().getExecutionOs()),
action.getTestLog().getExecPathString(),
action.getXmlOutputPath().getPathString(),
Integer.toString(result.getWallTimeInMs() / 1000),
diff --git a/src/main/java/com/google/devtools/build/lib/remote/BUILD b/src/main/java/com/google/devtools/build/lib/remote/BUILD
index 5447ffd..cef5c7b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/remote/BUILD
@@ -72,6 +72,8 @@
"//src/main/java/com/google/devtools/build/lib/analysis:config/build_options",
"//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
"//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
+ "//src/main/java/com/google/devtools/build/lib/analysis:constraints/constraint_constants",
+ "//src/main/java/com/google/devtools/build/lib/analysis/platform",
"//src/main/java/com/google/devtools/build/lib/analysis/platform:platform_utils",
"//src/main/java/com/google/devtools/build/lib/authandtls",
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper",
@@ -114,11 +116,13 @@
"//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception",
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib/util:exit_code",
+ "//src/main/java/com/google/devtools/build/lib/util:os",
"//src/main/java/com/google/devtools/build/lib/util:string",
"//src/main/java/com/google/devtools/build/lib/util:temp_path_generator",
"//src/main/java/com/google/devtools/build/lib/util/io",
"//src/main/java/com/google/devtools/build/lib/util/io:out-err",
"//src/main/java/com/google/devtools/build/lib/vfs",
+ "//src/main/java/com/google/devtools/build/lib/vfs:ospathpolicy",
"//src/main/java/com/google/devtools/build/lib/vfs:output_service",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
index 15b442f..ff6012b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
@@ -74,6 +74,8 @@
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.Spawns;
+import com.google.devtools.build.lib.analysis.constraints.ConstraintConstants;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.analysis.platform.PlatformUtils;
import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent;
@@ -109,10 +111,12 @@
import com.google.devtools.build.lib.remote.util.Utils.InMemoryOutput;
import com.google.devtools.build.lib.server.FailureDetails.RemoteExecution;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.TempPathGenerator;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.OsPathPolicy;
import com.google.devtools.build.lib.vfs.OutputService;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -252,7 +256,8 @@
ImmutableMap<String, String> env,
@Nullable Platform platform,
RemotePathResolver remotePathResolver,
- @Nullable SpawnScrubber spawnScrubber) {
+ @Nullable SpawnScrubber spawnScrubber,
+ @Nullable PlatformInfo executionPlatform) {
Command.Builder command = Command.newBuilder();
if (useOutputPaths) {
var outputPaths = new ArrayList<String>();
@@ -283,10 +288,16 @@
if (platform != null) {
command.setPlatform(platform);
}
+ boolean first = true;
for (String arg : arguments) {
if (spawnScrubber != null) {
arg = spawnScrubber.transformArgument(arg);
}
+ if (first && executionPlatform != null) {
+ first = false;
+ OS executionOs = ConstraintConstants.getOsFromConstraints(executionPlatform.constraints());
+ arg = OsPathPolicy.of(executionOs).postProcessPathStringForExecution(arg);
+ }
command.addArguments(reencodeInternalToExternal(arg));
}
// Sorting the environment pairs by variable name.
@@ -646,7 +657,8 @@
spawn.getEnvironment(),
platform,
remotePathResolver,
- spawnScrubber);
+ spawnScrubber,
+ spawn.getExecutionPlatform());
Digest commandHash = digestUtil.compute(command);
Action action =
Utils.buildAction(
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
index afa7432..5c0af34 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/vfs/BUILD
@@ -48,6 +48,7 @@
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
"//src/main/java/com/google/devtools/build/lib/util:filetype",
+ "//src/main/java/com/google/devtools/build/lib/util:os",
"//third_party:error_prone_annotations",
"//third_party:guava",
"//third_party:jsr305",
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java
index 5e843ac..cf575d9 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java
@@ -86,9 +86,17 @@
boolean isCaseSensitive();
+ /**
+ * Modifies the given string to be suitable for execution on the OS represented by this policy.
+ */
+ String postProcessPathStringForExecution(String callablePathString);
+
+ static OsPathPolicy of(OS os) {
+ return os == OS.WINDOWS ? WindowsOsPathPolicy.INSTANCE : UnixOsPathPolicy.INSTANCE;
+ }
+
// We *should* use a case-insensitive policy for OS.DARWIN, but we currently don't handle this.
- OsPathPolicy HOST_POLICY =
- OS.getCurrent() == OS.WINDOWS ? WindowsOsPathPolicy.INSTANCE : UnixOsPathPolicy.INSTANCE;
+ OsPathPolicy HOST_POLICY = of(OS.getCurrent());
static OsPathPolicy getFilePathOs() {
return HOST_POLICY;
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
index f5191af..90e7acd 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -622,6 +622,8 @@
*
* <p>In this way, a shell will always interpret such a string as path (absolute or relative to
* the working directory) and not as command to be searched for in the search path.
+ *
+ * <p>Prefer {@link #getCallablePathStringForOs} if the execution OS is available.
*/
public String getCallablePathString() {
if (isAbsolute()) {
@@ -636,6 +638,18 @@
}
/**
+ * Returns the path string using the native name-separator for the given OS, but does so in a way
+ * unambiguously recognizable as path. In other words, return "." for relative and empty paths,
+ * and prefix relative paths with an additional "." segment.
+ *
+ * <p>In this way, a shell will always interpret such a string as path (absolute or relative to
+ * the working directory) and not as command to be searched for in the search path.
+ */
+ public String getCallablePathStringForOs(com.google.devtools.build.lib.util.OS executionOs) {
+ return OsPathPolicy.of(executionOs).postProcessPathStringForExecution(getCallablePathString());
+ }
+
+ /**
* Returns the file extension of this path, excluding the period, or "" if there is no extension.
*/
public String getFileExtension() {
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixOsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixOsPathPolicy.java
index 45f1c55..8c7ce1e 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixOsPathPolicy.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixOsPathPolicy.java
@@ -139,4 +139,9 @@
public boolean isCaseSensitive() {
return true;
}
+
+ @Override
+ public String postProcessPathStringForExecution(String callablePathString) {
+ return callablePathString;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java
index 86908cf..8e25116 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/WindowsOsPathPolicy.java
@@ -16,6 +16,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.windows.WindowsFileOperations;
import com.google.devtools.build.lib.windows.WindowsShortPath;
import java.io.IOException;
@@ -39,6 +40,10 @@
static class DefaultShortPathResolver implements ShortPathResolver {
@Override
public String resolveShortPath(String path) {
+ if (!OS.getCurrent().equals(OS.WINDOWS)) {
+ // Short path resolution only makes sense on a Windows host.
+ return path;
+ }
try {
return WindowsFileOperations.getLongPath(path);
} catch (IOException e) {
@@ -240,4 +245,12 @@
public boolean isCaseSensitive() {
return false;
}
+
+ @Override
+ public String postProcessPathStringForExecution(String callablePathString) {
+ // On Windows, .bat scripts (and possibly others) cannot be executed with forward slashes in
+ // the path. Since backslashes are the standard path separator on Windows, we replace all
+ // forward slashes with backslashes instead of trying to enumerate these special cases.
+ return callablePathString.replace('/', '\\');
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/BUILD b/src/test/java/com/google/devtools/build/lib/remote/BUILD
index 53d8963..865e62b 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/remote/BUILD
@@ -84,8 +84,10 @@
"//src/main/java/com/google/devtools/build/lib/analysis:blaze_version_info",
"//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration",
"//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
+ "//src/main/java/com/google/devtools/build/lib/analysis:constraints/constraint_constants",
"//src/main/java/com/google/devtools/build/lib/analysis:server_directories",
"//src/main/java/com/google/devtools/build/lib/analysis:symlink_entry",
+ "//src/main/java/com/google/devtools/build/lib/analysis/platform",
"//src/main/java/com/google/devtools/build/lib/authandtls",
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper",
"//src/main/java/com/google/devtools/build/lib/authandtls/credentialhelper:credential_module",
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
index a62f9ae..61f08d3 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
@@ -83,6 +83,8 @@
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.SymlinkEntry;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue.RunfileSymlinksMode;
+import com.google.devtools.build.lib.analysis.constraints.ConstraintConstants;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.clock.JavaClock;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -111,6 +113,7 @@
import com.google.devtools.build.lib.remote.util.RxNoGlobalErrorsRule;
import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
import com.google.devtools.build.lib.remote.util.Utils.InMemoryOutput;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.TempPathGenerator;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
@@ -2516,6 +2519,29 @@
.containsExactly("output_dir");
}
+ @Test
+ public void buildRemoteAction_executablePathConformsToPlatform(@TestParameter OS executionOs)
+ throws Exception {
+ Spawn spawn =
+ new SpawnBuilder("path/to/pkg/script.bat", "some/other/arg")
+ .withOutputs("out")
+ .withPlatform(
+ PlatformInfo.builder()
+ .addConstraint(ConstraintConstants.OS_TO_CONSTRAINTS.get(executionOs))
+ .build())
+ .build();
+ FakeSpawnExecutionContext context = newSpawnExecutionContext(spawn);
+ RemoteExecutionService service = newRemoteExecutionService(remoteOptions);
+
+ var remoteAction = service.buildRemoteAction(spawn, context);
+
+ String expectedFirstArg =
+ executionOs == OS.WINDOWS ? "path\\to\\pkg\\script.bat" : "path/to/pkg/script.bat";
+ assertThat(remoteAction.getCommand().getArgumentsList())
+ .containsExactly(expectedFirstArg, "some/other/arg")
+ .inOrder();
+ }
+
private Spawn newSpawnFromResult(RemoteActionResult result) {
return newSpawnFromResult(ImmutableMap.of(), result);
}