Skip to content

fix(cli): reject overlapping output paths before writing - #1975

Closed
EpsilonPhoenix wants to merge 2 commits into
bendlang:mainfrom
EpsilonPhoenix:fix/cli-output-collisions
Closed

EpsilonPhoenix wants to merge 2 commits into
bendlang:mainfrom
EpsilonPhoenix:fix/cli-output-collisions

Conversation

@EpsilonPhoenix

Copy link
Copy Markdown
Contributor

A build with multiple -o paths can exit successfully while one output silently overwrites another. For example, if result.mjs links to result.js, requesting both replaces the runnable JS with the module output.

Preflight all requested outputs before writing, resolving symlinks (including dangling links and linked parent directories) and comparing hard-link identities. Include GPU sidecars only when the generated program and platform/toolkit detection select a GPU build; CPU-only -o program -o program.gpu remains valid. Reuse generated C when preflight needs it.

Validation on Linux:

  • Extended output_input_links regression fails on upstream main and passes interpreted and as emitted JavaScript.
  • Covers duplicate paths, symlinks, hard links, simulated CUDA sidecar collisions, two CPU binaries, and the no-CUDA ! fallback. The native CPU binaries print 42.
  • Mixed native, JS, and C output build passes; native and JS print 42.
  • Repository gate: 54/54. git diff --check passes.
  • Independent GPT-6.1-Terra medium review using review-agent: no findings after correcting its initial CPU-sidecar finding.

Actual Metal/CUDA execution and the mini-cluster gates were not run. The collision guard is a preflight; it does not protect against concurrent filesystem changes after the check.

@Lorenzobattistela

Copy link
Copy Markdown
Collaborator

Thanks for digging into this. We're closing this one, though: the fix costs more than the problem is worth.

Most of these collisions come from the user asking for two outputs at the same file (a symlink between two -o paths, hard links, linked parent dirs). That's a misuse we don't need to detect: it costs +463 ttok in main.ts, a second path resolver, the C compiled early and held in memory during the emit loop, and a test that runs clang -O3 inside the gate's 5 s alarm. It also still leaves holes, like case-only differences on APFS and a single -o whose <bin>.gpu collides with <bin>.

The one case worth guarding is the one a user can hit without meaning to. We'd take a new PR with just two checks, run before anything is written:

  1. Two -o paths that resolve to the same file (path.resolve plus realpath of the parent dir) are rejected.
  2. -o X.gpu is rejected when another -o X is a binary, because the binary may write X.gpu. Always reject this pair, with no GPU detection and no compiling first.

That should be a few dozen ttok, with no changes to compile_book or cli_build. A test of only the rejections stays fast because it builds nothing. Please include which output the conflict is with in the error message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants