Repository: bazelbuild/bazel
Issue: `bazel run` before `bazel test` fails with `Could not chdir` on Windows
Labels: type: bug
P2
area-Windows
team-OSS

### Description of the problem / feature request:

On Windows, if you run a test target with `bazel run` before you run that same test with `bazel test` (after a clean) then the test will fail with `Could not chdir` on Windows.

This was originally reported in #7387 and marked as resolved so it may have regressed as I'm seeing this in Bazel 2.0.0.

The culprit is that under Windows with runfiles not enabled by default, `bazel run` does not create the root workspace directory in the runfiles directory (`/path/to/test.runfiles/my_workspace_name`) while `bazel test` does. If you run `bazel test` first then `bazel run` will succeed for that test from then on as that directory is present under the test runfiles.

The error happens with both the default `--incompatible_windows_native_test_wrapper` and `--noincompatible_windows_native_test_wrapper` (although the error output is slightly different):

```
-----------------------------------------------------------------------------
ERROR(tools/test/windows/tw.cc:462) value: 2 (0x00000002), arg: c:\users\gmago\_bazel~1\wpufrwit\execroot\build_~1\bazel-out\x64_windows-fastbuild\bin\internal\linker\test\unit_tests.bat.runfiles\build_bazel_rules_nodejs: Could not chdir
```
in the first case, and

```
external/bazel_tools/tools/test/test-setup.sh: line 147: cd: /c/users/gmago/_bazel~1/wpufrwit/execroot/build_~1/bazel-out/x64_windows-fastbuild/bin/internal/linker/test/unit_tests.bat.runfiles/build_bazel_rules_nodejs: No such file or directory
Could not chdir /c/users/gmago/_bazel~1/wpufrwit/execroot/build_~1/bazel-out/x64_windows-fastbuild/bin/internal/linker/test/unit_tests.bat.runfiles/build_bazel_rules_nodejs
```

### Bugs: what's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

On Windows with Bazel 2.0.0, run any test target with `bazel run` on a clean clone or after a bazel clean before running that test target with `bazel test`.

### What operating system are you running Bazel on?

Windows 10 Pro

### What's the output of `bazel info release`?

```
C:\Users\gmago\google\rules_nodejs>bazel info release
INFO: Invocation ID: 1ecaee15-b295-461c-9656-0c5fe8418002
release 2.0.0
```

### Any other information, logs, or outputs that you want to share?

The work-around is simply to run `bazel test` first but it would be convenient if the root workspace directory was created in the runfiles folder under `bazel run` as well. 

//cc @devversion

Issue comments:
Thanks for the report.
I can repro locally, with a trivial cc_test.

foo/BUILD:
```
cc_test(name = "x", srcs = ["x.cc"])
```

foo/x.cc:
```
int main() { return 0; }
```

```
bazel run //foo:x
(...)
Executing tests from //foo:x
-----------------------------------------------------------------------------
ERROR(tools/test/windows/tw.cc:462) value: 2 (0x00000002), arg: c:\_bazel\ansasmbz\execroot\__main__\bazel-out\x64_windows-fastbuild\bin\foo\x.exe.runfiles\__main__: Could not chdir
```

Your root cause analysis seems correct.
I have a fix for this.

The bug affects Linux too when using `--enable_runfiles=no`.
Thanks for the quick fix @laszlocsomor !
You're welcome! ETA for the fix is Bazel 2.1.0.
@laszlocsomor Just curious, did #10646 make it to Bazel 2.1.0?  I was having this problem today on 2.1.0 and was wondering if it is a new issue or simply need to wait until 2.2.0 or something.

@coryan : It didn't; the baseline for 2.1.0 was 41ec5a28fb30a8d6c5c60194c4bb29528352cf78 from Jan 8 while #10646 was merged as 02ce5bd3d987b0a3474c1aa9e2450c70e71ab5d6 on Jan 28.

It will be in 2.2.0, whose baseline is 78055efad0917b848078bf8d97b3adfddf91128d from Feb 13.

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
index 3fa05a91f638bd..6be15b3c5b205a 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
@@ -567,11 +567,23 @@ private static Path ensureRunfilesBuilt(
       workingDir = workingDir.getRelative(runfilesSupport.getRunfiles().getSuffix());
     }
 
+    // Always create runfiles directory and the workspace-named directory underneath, even if we
+    // run with --enable_runfiles=no (which is the default on Windows as of 2020-01-24).
+    // If the binary we run is in fact a test, it will expect to be able to chdir into the runfiles
+    // directory. See https://github.com/bazelbuild/bazel/issues/10621
+    try {
+      runfilesSupport
+          .getRunfilesDirectory()
+          .getRelative(runfilesSupport.getWorkspaceName())
+          .createDirectoryAndParents();
+    } catch (IOException e) {
+      throw new EnvironmentalExecException(e);
+    }
+
     // When runfiles are not generated, getManifest() returns the
     // .runfiles_manifest file, otherwise it returns the MANIFEST file. This is
     // a handy way to check whether runfiles were built or not.
     if (!RUNFILES_MANIFEST.matches(manifest.getFilename())) {
-      // Runfiles already built, nothing to do.
       return workingDir;
     }
 


Test patch:
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 1a2efa50a50123..98dccd0264d092 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -1230,3 +1230,10 @@ sh_test(
     data = [":test-deps"],
     deps = ["@bazel_tools//tools/bash/runfiles"],
 )
+
+sh_test(
+    name = "run_test",
+    srcs = ["run_test.sh"],
+    data = [":test-deps"],
+    deps = ["@bazel_tools//tools/bash/runfiles"],
+)
diff --git a/src/test/shell/bazel/run_test.sh b/src/test/shell/bazel/run_test.sh
new file mode 100755
index 00000000000000..c43135411bceec
--- /dev/null
+++ b/src/test/shell/bazel/run_test.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+# --- begin runfiles.bash initialization ---
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+    if [[ -f "$0.runfiles_manifest" ]]; then
+      export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+    elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+      export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+    elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+      export RUNFILES_DIR="$0.runfiles"
+    fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+  source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+            "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+  echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+  exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+source "$(rlocation "io_bazel/src/test/shell/integration_test_setup.sh")" \
+  || { echo "integration_test_setup.sh not found!" >&2; exit 1; }
+
+case "$(uname -s | tr [:upper:] [:lower:])" in
+msys*)
+  declare -r is_windows=true
+  ;;
+*)
+  declare -r is_windows=false
+  ;;
+esac
+
+if "$is_windows"; then
+  export MSYS_NO_PATHCONV=1
+  export MSYS2_ARG_CONV_EXCL="*"
+fi
+
+# ------------------------------------------------------------------------------
+# TESTS
+# ------------------------------------------------------------------------------
+
+# Regression test for https://github.com/bazelbuild/bazel/issues/10621
+function test_running_test_target_with_runfiles_disabled() {
+  local -r pkg="pkg${LINENO}"
+  mkdir $pkg
+  cat >$pkg/BUILD <<eof
+load(":my_test.bzl", "my_test")
+my_test(name = "x")
+eof
+  if "$is_windows"; then
+    local -r IsWindows=True
+  else
+    local -r IsWindows=False
+  fi
+  cat >$pkg/my_test.bzl <<eof
+def _impl(ctx):
+    # Extension needs to be ".bat" on Windows, so that Bazel can run the test's
+    # binary (the script) as a direct child process.
+    # Extension doesn't matter on other platforms.
+    out = ctx.actions.declare_file("%s.bat" % ctx.label.name)
+    ctx.actions.write(
+        output = out,
+        content = "@echo my_test" if $IsWindows else "#!/bin/bash\necho my_test\n",
+        is_executable = True,
+    )
+    return [DefaultInfo(executable = out,
+                        files = depset([out]),
+                        default_runfiles = ctx.runfiles([out]))]
+
+my_test = rule(
+    implementation = _impl,
+    test = True,
+)
+eof
+  bazel run --enable_runfiles=no $pkg:x >&$TEST_log || fail "expected success"
+  bazel run --enable_runfiles=yes $pkg:x >&$TEST_log || fail "expected success"
+}
diff --git a/src/test/shell/integration/run_test.sh b/src/test/shell/integration/run_test.sh
index db64d7881e3992..cfe0e5b38340b9 100755
--- a/src/test/shell/integration/run_test.sh
+++ b/src/test/shell/integration/run_test.sh
@@ -115,10 +115,6 @@ function test_run_py_binary() {
 }
 
 function test_run_py_test() {
-  if "$is_windows"; then
-    # TODO(laszlocsomor): fix this test on Windows, and enable it.
-    return
-  fi
   write_py_files
   bazel run //py:test >& $TEST_log || fail "Expected success"
   expect_log_once 'Hello, Python World'