Provide configurable tree shaking - #891
Draft
statonjr wants to merge 9 commits into
Draft
Conversation
…aped fixture The spec.alpha hello world that motivates the key needs a git dep, and the shakelocal fixtures run in CI without one, so the fixture reproduces spec's two shapes in miniature: an app's res behind a path -main never takes and a :local/root library's dynaload behind a delay, each vouched for by its own deps.edn. The partial fixture adds one caller nothing vouches for and must still bail with a hint naming that caller alone, which is the only way to show an allowed site was honored without letting a non-allowed one through. Both callers are ^:redef because the inline pass would otherwise splice them into their callers and the bail would name the caller instead.
…ast it A reachable resolve/eval-family reference bails the tree-shake, and it must: the static graph cannot follow a runtime name. But some of those sites are dead in a built binary and the author can prove it — spec.alpha's res and spec.gen's dynaload are the motivating pair, and one s/def is enough to lose the whole shake. dce-bail-scan now takes an allow set of def fqns and skips those defs in both the bail scan and the compiler-needed scan: a site vouched never to run needs no compiler, and skipping only the bail scan would let an allowed eval caller shake without dropping the image, breaking the no-bail- drops-compiler invariant shakelocal asserts. Nothing is kept on an allowed def's behalf. The bail diagnostic lists each site once (the IR+text ref union had it printing every line twice) and ends with the paste-ready deps.edn key for every def it named, so the path from skipped to kept is one paste.
jolt.host/build-binary already carries its cross-compile target as trailing optional arguments, so the allow list rides the same way (index 2) rather than widening the positional contract every caller would have to learn. An absent or nil option is the empty list, which is today's behaviour exactly.
… path A library knows its own dynamic sites; the app that pulls it in does not. So the key is read where :jolt/native is read — each dependency's deps.edn in resolve-deps, the project's own in resolve-project — and unioned, project first, so a library ships its list once and an app can still vouch for a library that has not. Entries travel as ns/name strings because that is the shape the Scheme bail scan keys on.
The driver takes it as the third trailing option, after the cross-compile target and pack, so an app that declares nothing passes an empty vector and builds exactly as before. This turns the allow-dynamic fixtures green: verified by mutation that dropping either the app's or the library's declaration bails the shaking fixture again.
…nit level The README says when the key is safe to add, that a library can ship it, and that the name to paste is the one the bail prints. The changelog records why an allowed def skips the compiler scan too, because that is the one choice a reader could reasonably have made the other way. The deps unit gate now resolves the committed shake fixture and asserts both declarations arrive, project first: the build-level gate can show the shake ran, not which declaration got it there.
The header said bail scan only, which is the half of the decision a reader landing here first would take as the whole: an allowed eval caller also skips the compiler-needed scan, and dce-bail-scan explains why.
statonjr
marked this pull request as draft
September 8, 2026 01:53
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.
deps.edncan now vouch for a runtime var lookup sojolt build --tree-shakeproceeds past it::jolt/tree-shake {:allow-dynamic [clojure.spec.alpha/res clojure.spec.gen.alpha/dynaload]}The key is read from the app's
deps.ednand every library's, where:jolt/nativeis read, and unioned. Adefin the set is skipped by the bail scan. Nothing is kept on its behalf. With no key, the shake bails exactly as before.When the shake still bails, the message lists each site once and ends with the paste-ready key:
Real-world check, the hello world from the issue with
spec.alpha 0.5.238: bails with the two sites the issue names; with the pasted key it shakes, drops the compiler image, ands/describestill printsint?. Binary 30.7MB -> 17.4MB (13.3MB, 43%).Fixes #890