Skip to content

sh_binary fails on Windows with #!/usr/bin/env bash shebang when WSL is installed #59

Description

@peakschris

On Windows with MSYS2 and WSL, sh_binary targets fail at runtime when the source script uses #!/usr/bin/env bash.

Environment: Windows, MSYS2 (BAZEL_SH=C:\msys64\usr\bin\bash.exe), WSL installed, Bazel 9.x, rules_shell 0.8.0

Reproduction

git clone https://github.com/bazelbuild/rules_jvm_external.git
cd rules_jvm_external
echo 9.1.0 > .bazelversion
bazel run @duplicate_version_warning//:pin
/bin/bash: D:/.../external/+maven+duplicate_version_warning/pin: No such file or directory

Any sh_binary with a #!/usr/bin/env bash shebang will reproduce this.

Cause

The .exe launcher invokes bash.exe -c "path/to/script args...". MSYS bash execs the script, and the MSYS runtime reads the shebang. #!/usr/bin/env bash causes env to search the MSYS PATH, where /c/WINDOWS/system32/bash (WSL) appears before /usr/bin/bash (MSYS):

$ C:\msys64\usr\bin\bash.exe -c "which -a bash"
/c/WINDOWS/system32/bash    ← WSL (found first)
/usr/bin/bash                ← MSYS (correct, but second)

WSL bash cannot resolve Windows-style paths → "No such file or directory".

Both use_bash_launcher code paths are affected:

  • False (default): source is symlinked as-is, preserving the shebang.
  • True: wrapper has shebang = "" but calls exec "$(rlocation ...)" "$@", which triggers shebang processing of the underlying script.

Why Bazel 9 only

sh_binary.bzl uses getattr(native, "sh_binary", _sh_binary). In Bazel 8, the native implementation was used (no .exe launcher). In Bazel 9, native rules were removed (#23043), so the Starlark implementation creates the .exe launcher, exposing this interaction.

Suggested fix

The shebang is redundant on Windows — the .exe launcher already resolves bash via bash_bin_path from the sh_toolchain.

  1. use_bash_launcher=False on Windows: Instead of symlinking, copy the script and strip #!/usr/bin/env shebangs.
  2. use_bash_launcher=True on Windows: Replace exec "$(rlocation ...)" "$@" with . "$(rlocation ...)" or bash "$(rlocation ...)" "$@" to avoid shebang re-processing.

Alternatively, changing bash_launcher.cc to invoke bash script args instead of bash -c "script args" would fix this at the Bazel core level (bash doesn't process shebangs for file arguments).

Workaround

Change shebangs from #!/usr/bin/env bash to #!/bin/bash. MSYS2 resolves /bin/bash directly to C:\msys64\usr\bin\bash.exe via its filesystem mount, bypassing PATH.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions