Sync platform npm binaries via dune promotion - #8560
Merged
Conversation
The compiler binaries in packages/@rescript/<platform>/bin - what cli/*.js, the test harnesses, and the runtime build actually run - were copied there by mtime-based Makefile rules that only fired when make itself ran, plus a separate scripts/copyExes.js step in CI. A bare `dune build` left them stale, and because some test suites read _build directly while others resolve through the npm package, the tree could silently disagree with itself about which compiler was under test. Replace both copy mechanisms with dune promotion rules (compiler/sync/dune): one rule per platform, covering bsc, rescript-editor-analysis, and rescript-tools. Promotion runs on every `dune build`, compares content so unchanged binaries are not rewritten (no timestamp churn for make's downstream stamps), strips on Unix like the packaging step always did, and copies unstripped on Windows. The browser profile is excluded so a playground build can never overwrite the native binaries. The promotion rules are now the only producer of these files: - The Makefile copy rules and both mtime-compensation touch loops are deleted. `make compiler` verifies each binary against the dune build output (cmp, with one forced re-promotion retry that also self-heals a damaged copy), restores a lost executable bit, and fails with a pointer to compiler/sync/dune when promotion did not produce it - e.g. when a platform predicate is wrong - rather than silently accepting a missing or stale binary. - scripts/copyExes.js loses its compiler mode (the rewatch mode remains), and the CI step that used it is replaced by scripts/checkCompilerExes.js, which asserts on every platform in the matrix that the promoted binaries match the build instead of quietly re-creating them. The coverage flow still deliberately swaps in unstripped instrumented binaries after its build; the next regular `dune build` restores the clean ones automatically. Signed-Off-By: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cristianoc
force-pushed
the
dune-promote-platform-binaries
branch
from
August 18, 2026 11:33
8f3692f to
3d23f5a
Compare
rescript
@rescript/darwin-arm64
@rescript/darwin-x64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-8560 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rationale
The compiler binaries in
packages/@rescript/<platform>/binare what actually runs in most workflows:cli/bsc.jsand friends resolve them via the platform npm package, and so do the mocha/build tests (scripts/test.js), the tools tests, the rewatch integration tests, and the runtime build. But those binaries were only refreshed by copy steps — mtime-based Makefile rules locally, and a separatescripts/copyExes.js --compilerstep in CI — which run only when make (or that CI step) runs.The command sequence that gets into trouble
Nothing updates the packages copy between those two commands. Worse, the tree becomes split-brained:
scripts/test_syntax.shand the analysis tests read_build/install/default/bindirectly (new compiler), while everything routed through the cli reads the packages copy (old compiler) — in the same shell session, with no error anywhere. In practice this showed up as confusingly inconsistent results: syntax fixtures regenerated by the new compiler while mocha snapshots were regenerated by the old one, costing real debugging time before anyone suspects the binary.A programmatic illustration (probe string compiled into the binaries, then one bare
dune build):Why this resolves it
Dune promotion makes the packages binaries an output of the build itself: rules in
compiler/sync/dune(one per platform, coveringbsc,rescript-editor-analysis,rescript-tools) copy-and-strip each binary into the platform package on everydune build. There is no separate step to forget. Key properties:touchloops, andcopyExes.js's compiler mode are deleted. If a platform'senabled_ifpredicate is wrong, the binary is missing on a fresh checkout and fails loudly and proximately — the "present but stale" failure mode no longer has a producer. (copyExes.jskeeps its rewatch mode: that binary is cargo-built and stays make's responsibility.)%{system}/%{architecture}fromocamlc -config); Windows copies withoutstrip, matching the historical packaging step.(<> %{profile} browser), so amake playgroundbuild (which compiles a playground-flavouredbsc) can never overwrite the native binaries.Verification layers
make compilernow verifies instead of assumes: each packages binary iscmp-checked against the dune build output (_build/default/compiler/sync/), with one forced re-promotion retry that also self-heals a damaged or deleted copy, and a lost executable bit is restored. If promotion genuinely didn't produce the binary (e.g. an uncovered platform), it fails with a message pointing atcompiler/sync/duneinstead of anENOENTdeep inside some downstream harness.scripts/checkCompilerExes.js, which asserts on every platform in the matrix that the promoted binaries byte-match the build (and are executable on Unix) — so the per-platformenabled_ifpredicates are actually tested rather than masked by a re-copy.Verifications done (macOS/arm64)
dune build.dune buildre-creates all three; fresh worktree (fresh-clone equivalent) produces them from nothing.enabled_ifmakes bothmake compilerandcheckCompilerExes.jsfail with the intended messages; a corrupted orchmod 644binary is detected and self-healed.dune rules --profile browserconfirms the promotion targets don't exist there.make compiler,make lib,make test, Biome, and formatting all green.Linux and Windows validation is exactly what this PR's CI run provides: with no fallback producer left, a wrong platform predicate means a missing binary and a loud, attributable failure in that job.
🤖 Generated with Claude Code