Hive
fix(graph): stabilize windows rustc response handling
GitHub issue · Closed
What changed
rustc-response argument files now write each argument exactly as the graph action declared it. The Windows-specific escaping layer was removed because normal rustc @path response files should be owned by the compiler-facing materializer, not by shell quoting rules.
Rust feature flags now lower from features = ["default"] to compiler arguments such as --cfg=feature="default", with Windows placing those arguments in a generated response file.
Windows host tool lookup now skips extensionless PATHEXT lookups when the caller asks for a bare tool name such as rustc or cargo. This prevents the graph analyzer from returning mise shell shims that Windows cannot spawn directly through Command.
Build-script crates no longer invoke native Windows rustc through the Unix shell wrapper. On Windows, the Rust prelude now generates a small PowerShell wrapper action that reads build-script stdout, appends cargo:rustc-* directives to an argument array, and invokes rustc directly.
once-frontend now declares the embedded prelude/ tree as a Cargo rebuild input. The walk is sorted, follows directory symlinks only inside the canonical prelude root, and tracks visited directories so traversal is deterministic and bounded.
The pull request workflow includes a Windows release graph build check. It vendors the Rust dependency graph, writes the release graph manifests, and builds the x86_64-pc-windows-msvc Once binary through Once without packaging or publishing release assets.
Why
The Release workflow on main is failing for the Windows package jobs while compiling Rust dependencies through Once’s graph actions. The failing command reaches rustc with incorrectly encoded feature configuration values.
The new pull request validation also exposed two adjacent Windows graph issues: host_which("rustc") could resolve to an extensionless mise shim, and crates with build scripts routed native Windows rustc through a Unix shell wrapper.
A later validation run showed that source bootstrapping could still reuse cached once-frontend output after Starlark prelude changes, so the Windows job could run a stale embedded prelude even when the branch contained the corrected feature lowering.
Root cause
The original response-file materializer treated rustc response files like shell-escaped command lines on Windows. That wrote backslash-escaped quotes, so rustc received backslash tokens and rejected the configuration argument.
After removing that escaping, the Windows release graph check showed the generated response files were correct, for example --cfg=feature="default". The remaining failure only appeared on crates with build scripts. Those actions wrapped rustc through sh so Once could append cargo:rustc-* lines from build-script stdout, and that Windows shell path changed how the native compiler consumed the response file.
The host lookup helper also included the extensionless tool name before checking PATHEXT candidates on Windows. That let shell-specific shims shadow executable candidates such as rustc.exe.
Finally, once-frontend embeds the Starlark prelude at compile time, but Cargo did not know that prelude/ was an input to the package build. With restored Rust cache outputs, a source bootstrap could reuse a stale once-frontend artifact after only prelude files changed.
Approach
Move response-file responsibility to the Rust materializer: cmd_args(format = "rustc-response") validates that arguments are one line each, then writes them verbatim.
Move feature-flag responsibility into the Rust prelude: rule authors keep passing features = ["default"], and Once lowers that to compiler-ready --cfg=feature="default" arguments before deciding whether to place them inline or in a response file.
Keep build-script stdout handling in the Rust target kind, but avoid the Unix shell when the host is Windows. The generated PowerShell wrapper receives the original argument vector positionally, reads the build-script stdout file, appends compiler directives as array elements, and launches rustc without routing the response file through Git Bash.
Make host lookup return spawnable Windows tools by honoring PATHEXT candidates for bare tool names and skipping the extensionless path. Target kinds can keep asking for host_which("rustc") and get a command the graph can execute directly.
Make the embedded prelude a declared Cargo rebuild input through a small build script. This keeps future prelude-only changes from relying on incidental Rust source changes to invalidate cached bootstrap binaries.
Add a Windows release graph build to pull request validation so this failure mode is caught before merge without invoking the release packaging task.
Impact
Windows release packaging should pass feature configuration values to rustc without stripping quotes or adding backslashes.
Windows graph analysis should no longer fail when mise shims appear before Rustup executables on PATH. Non-Windows host lookup is unchanged.
Crates with build scripts now avoid the Git Bash wrapper path when invoking native Windows rustc.
Prelude-only changes will now rebuild once-frontend reliably when Cargo restores cached outputs.
The pull request workflow gets one additional Windows job when Rust files change. The job builds like release but does not package, upload, publish, or pass a GitHub token to the build command.
Validation
mise exec -- cargo test -p once-frontend --test preludemise exec -- cargo test -p once-frontend windows_host_which_candidatesmise exec -- cargo test -p once-cli rustc_response_args_keep_arguments_verbatimmise exec -- cargo test -p once-cli materialize_declared_arg_files_writes_rustc_response_argsmise exec -- cargo test -p once-climise exec -- cargo test -p once-frontendmise exec -- cargo fmt --all -- --checkgit diff --checkmise exec -- cargo clippy -p once-cli -p once-frontend --all-targets -- -D warningsmise exec -- cargo clippy -p once-frontend --all-targets -- -D warningsmise exec -- cargo build --locked --release --package once-cliruby -e 'require "yaml"; YAML.load_file(".github/workflows/once.yml"); puts "yaml ok"'
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.