Hive Hive
Sign in

fix(graph): route full rustc invocation through response file on Windows

GitHub issue · Closed

Metadata
Source
tuist/once #143
Updated
Jul 5, 2026
Domains
Once
Details

Motivation

CI on main has been failing on the Windows release jobs (Build x86_64-pc-windows-msvc, Build SDK libraries x86_64-pc-windows-msvc, and release graph build (windows-latest)). The failure surfaces while compiling once-core:

once: executing action 0 for crates/once-core/once_core_x86_64_pc_windows_msvc (...:rustc):
failed to spawn rustc.exe: The filename or extension is too long. (os error 206)

os error 206 is Windows telling us the command line handed to CreateProcess is over its limit (roughly 32 KB). The rustc invocation for a crate carries one --extern=name=path flag per dependency plus a -L dependency=path search path per dependency directory, and on Windows those paths are long and absolute. For a crate with a dependency graph as large as once-core, the combined length blows past the limit before rustc even starts.

Context

We had already started moving arguments off the command line in earlier changes, but only the feature cfgs were being routed through a rustc response file. That left the actual bulk of the invocation, the --extern and -L flags, still inline, so the limit was still being hit. The previous attempts treated the symptom (feature cfgs) rather than where the size actually comes from (the dependency flags).

Note

What is a response file? Most compilers, rustc included, accept an argument of the form @path/to/file. Instead of reading that one token literally, the compiler opens the file and reads its contents as if those arguments had been typed on the command line. rustc’s response files are simple: one argument per line, taken literally, no shell-style quoting or escaping. This lets us keep the command line tiny (just the toolchain path and @response-file) while passing an effectively unbounded number of arguments through the file, which is the standard way to sidestep the operating system command line length limit on Windows.

Approach and tradeoffs

I considered keeping the surgical approach and only moving the dependency flags into the response file, but that would leave the same class of bug latent for any other argument category that grows over time (linker flags, user flags, and so on). Instead I route the entire post-rustc argument list through a single response file on Windows for both compile actions (the build script compile and the main crate compile). The only things left on the command line are the toolchain path and the @response-file reference.

On non-Windows hosts the arguments stay inline, because their command line limits are generous and there is no reason to pay for an extra file write and read on every compile.

This is safe because outputs and inputs are declared explicitly to the action (run_action(outputs=..., inputs=...)) rather than parsed out of the argv, the argv is handed to the executor verbatim with no path rewriting, the response file contents are folded into the action digest so caching stays correct, and rustc resolves the relative paths inside the file against the same working directory it uses for the command line.

Verification

  • cargo test -p once-frontend --test prelude rust_ (18 passing, including the Windows response file coverage)
  • cargo test -p once-frontend --test prelude cargo_metadata_windows
  • cargo test -p once-cli arg_file

The existing Windows tests were updated to assert that the whole invocation (not just feature cfgs) lands in a single rustc.rsp, and that with no features the invocation is still routed through the response file because the crate metadata, source, and dependency flags are always present.

🤖 Generated with Claude Code

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.