install(windows-shim): handle `env -S` in bin shebang parser

Closed
RO
1mo ago
+284-4

Fixes #31387
Fixes #22422
Fixes #27924

Problem

Installing a package whose bin uses #!/usr/bin/env -S <prog> <args> on Windows produces a broken .bunx shim. For ocx@2.0.11 (#!/usr/bin/env -S bun --no-env-file) the shim launches with:

error: interpreter executable "-S" not found in %PATH%

Bun failed to remap this bin to its proper location within node_modules.

Root cause

Shebang::parse in src/install/windows-shim/BinLinkingShim.rs tokenizes the shebang. After consuming /usr/bin/env it reads the next token as the interpreter program — but for -S shebangs that token is literally -S, so the encoded launcher becomes -S bun --no-env-file and the shim tries to spawn -S at runtime.

env -S / --split-string is the documented way to forward flags to the real interpreter from a shebang on Linux/BSD (because the kernel collapses everything after the first space into a single arg). Node uses it, Bun-built CLIs like ocx use it, many tools use it.

Fix

In the /usr/bin/env / /bin/env branch of Shebang::parse, if the next token is -S, skip it and re-read the real program token. The remainder of the line stays as the launcher args, so flags like --no-env-file still reach Bun.

It also handles the joined short-option form -Sbun (env accepts -SSTRING as -S STRING). The -u VAR / -C DIR ambiguity where subsequent tokens are flag arguments is deliberately out of scope.

Verification

  • cargo check --workspace --target x86_64-pc-windows-msvc compiles cleanly (via bun run rust:check-all).
  • Rust unit tests in BinLinkingShim.rs cover the parse surface: env bun, env node, env -S bun --no-env-file, env -S node --experimental-vm-modules, env -S python3 -u, env -S alone, joined -Sbun/-Snode, multi-space variants, CRLF, /bin/env alias.
  • test/cli/install/shebang-normalize.test.ts drives Shebang::parse directly on all platforms via a bun:internal-for-testing binding (parseBinShebang) for the spaced and joined -S forms, and on Windows installs a package and decodes the generated .bunx to verify the encoded launcher is bun --no-env-file, not -S bun .... The shim parser only runs on Windows at install time, so the binding is how the Linux lanes exercise it.

Prior PRs

#27925 and #28595 target this same bug but only edit BinLinkingShim.zig — which is no longer compiled (the runtime is BinLinkingShim.rs now). Both are effectively no-ops against current main. This PR supersedes them.