Skip to content

fix(dart): keep the build directory out of the vodozemac bindings - #62

Merged
nikzen merged 1 commit into
mainfrom
fix/vodozemac-darwin-build-paths
Aug 24, 2026
Merged

fix(dart): keep the build directory out of the vodozemac bindings#62
nikzen merged 1 commit into
mainfrom
fix/vodozemac-darwin-build-paths

Conversation

@nikzen

@nikzen nikzen commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

nix build --rebuild shows that two builds of famedly-vodozemac on a Mac are
never the same bytes. Nix builds in /nix/var/nix/builds/nix-<pid>-<random>,
and darwin has no sandbox to remap that to a constant, so the path reaches the
artefacts: 104 occurrences in the shared library, 212 in the static one, the
symbol suffixes LLVM hashes from a module's path, and LC_ID_DYLIB, which ld64
fills with the full output path when no install name is given.

Remapping the prefix leaves the static library reproducible down to the
byte
and takes the shared one from ~1500 differing bytes to 48: the Mach-O
UUID and the ad-hoc signature over it. -Wl,-no_uuid would remove those at the
cost of ever symbolicating a crash, which is the worse trade.

The install name is worth fixing either way. The library announced its own
location as a build directory that stopped existing when the build finished.
Nothing reads it, since flutter_rust_bridge opens the file by the path it is
handed, but anything that linked against it would have kept that reference.

Inert on Linux: the hook is darwin-only, and the sandbox already mounts the
build directory at a constant /build. web.nix is a different artefact by a
different route and would need its own measurement.

Stacked on #56, where native.nix lives — the file this used to patch does not
survive that split. The derivation does: with this applied, both branches
produce the same store path.

Found while measuring what was worth putting in the shared binary cache (#59),
which it does not block.

Test plan

  • nix flake check --all-systems, prek --all-files --stage pre-push
  • nix build --rebuild on aarch64-darwin: the .a is byte-identical,
    the .dylib down to 48 bytes, no build directory paths left
  • otool -l shows the install name under $out/lib
  • a Dart project using the bindings still loads them

Made with Cursor


The stack

main ─┬─ #63  make room on the runner
      ├─ #59  substitute from the shared binary cache
      └─ #56  sign, describe and scan what we publish
            └─ #62  keep the build directory out of vodozemac  ←
                  └─ #61  tell a web build which build it is
                        └─ #64  let a project choose its vodozemac

#63 and #59 stand outside the chain and can land in any order. #59 and #56
both touch the vodozemac module, so whichever goes second wants a rebase.

@nikzen
nikzen force-pushed the fix/vodozemac-darwin-build-paths branch from 1fcd558 to 49ca2ff Compare August 3, 2026 16:33
@nikzen
nikzen changed the base branch from main to feat/supply-chain August 3, 2026 16:33
@nikzen
nikzen force-pushed the fix/vodozemac-darwin-build-paths branch from 49ca2ff to d7355b0 Compare August 4, 2026 07:29
Base automatically changed from feat/supply-chain to main August 5, 2026 03:36
Nix builds in `/nix/var/nix/builds/nix-<pid>-<random>`, and on darwin, where no
sandbox remaps that to a constant, the name reached the artefacts: a hundred
and four times in the shared library, two hundred and twelve in the static one,
plus the hashes LLVM derives from a module's path and the install name the
linker writes. Two builds were never the same bytes, so nobody could rebuild
the library and see that it matched what was shipped — which is most of what
pinning a dependency by version is for.

Rewriting the prefix leaves the static library reproducible down to the byte.
The shared one keeps a UUID and an ad-hoc signature the linker derives from the
rest; dropping the UUID would buy the last forty-eight bytes at the cost of
symbolicating a crash, which is the worse trade.

The install name is worth fixing whether or not any of that matters. Until now
the library announced its own location as a directory that stopped existing
when the build finished. Nothing reads it, because `flutter_rust_bridge` opens
the file by the path it is handed, but anything that linked against it would
have kept a reference to somewhere that was never there.

Signed-off-by: Niklas Zender <n.zender@famedly.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@nikzen
nikzen force-pushed the fix/vodozemac-darwin-build-paths branch from d7355b0 to 889b040 Compare August 24, 2026 09:01
@nikzen
nikzen merged commit ec6a012 into main Aug 24, 2026
3 checks passed
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