Skip to content

feat(build): add a macOS cross-architecture dev shell - #724

Closed
kriswill wants to merge 1 commit into
DeusData:mainfrom
kriswill:feat/macos-cross-arch
Closed

kriswill wants to merge 1 commit into
DeusData:mainfrom
kriswill:feat/macos-cross-arch

Conversation

@kriswill

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a cross flake dev shell so macOS users can build the other arch locally: nix develop .#cross then scripts/build.sh --arch x86_64 (on Apple Silicon) produces a working x86_64 binary — and vice versa on Intel.

The native clang already cross-emits object code via -arch (set up by the build fix in #723 — scripts/env.sh exports ARCHFLAGS). The only thing missing for a cross link is the target-arch dependency libraries, because the dev shell's zlib/libgit2 are host-arch only. This shell puts the target-arch zlib (a hard -lz dependency) and libgit2 (optional, via pkg-config) on the link path; they're fetched as prebuilt substitutes, so building needs no Rosetta — only running an x86_64 binary on Apple Silicon does.

Scope is deliberately minimal: flake.nix only, an added crossDevShells helper plus one merged attribute. The existing default shell is untouched, and there are no flake.lock / .envrc changes.

Validation (host aarch64-darwin, targeting x86_64)

$ nix develop .#cross
[cross] target x86_64-darwin — build with: scripts/build.sh --arch x86_64
$ scripts/build.sh --arch x86_64
=== Build complete: build/c/codebase-memory-mcp ===
$ file build/c/codebase-memory-mcp
build/c/codebase-memory-mcp: Mach-O 64-bit x86_64 executable
$ otool -L build/c/codebase-memory-mcp | grep -Ei 'git2|libz'
  /nix/store/…-libgit2-1.9.4-lib/lib/libgit2.1.9.dylib   # x86_64
  /nix/store/…-zlib-1.3.2/lib/libz.dylib                 # x86_64

The binary links the x86_64 slices of libgit2 + zlib (no stale host-arch references). Constraint, as flagged on #705: a working x86_64 build needs x86_64 zlib (mandatory) + libgit2, which this shell provides as substitutes.

Depends on #723 (ARCHFLAGS + the compiler capability probe) — the cross shell is only useful once that lands, so please merge this after #723. Refs #705.

Checklist

  • Every commit is signed off (git commit -s) — required, CI rejects
    unsigned commits (DCO, see CONTRIBUTING.md)
  • Tests pass locally (make -f Makefile.cbm test)
  • Lint passes (make -f Makefile.cbm lint-ci)
  • New behavior is covered by a test (reproduce-first for bug fixes)

On the checklist: this is a flake.nix-only change — it adds an opt-in dev shell and does not touch the C sources, scripts, or Makefile, so make test / lint-ci are unaffected (no C or lint surface). Validation is the flake evaluation (both default and cross shells evaluate on aarch64-darwin; default on Linux) plus the cross-build transcript above. There is no CI runner for macOS cross-arch today; happy to discuss adding one.

Add a `cross` flake dev shell that supplies the target-arch zlib +
libgit2 (fetched as prebuilt substitutes), so `nix develop .#cross` +
`scripts/build.sh --arch x86_64` links a working x86_64 binary on an
arm64 host (and vice versa). The native clang cross-emits object code
via -arch (see ARCHFLAGS in scripts/env.sh); this shell only provides
the target-arch dependency libraries the link needs. Running the
produced binary on Apple Silicon still requires Rosetta.

Depends on the env.sh / Makefile.cbm arch changes (the build fix for
this issue). Refs DeusData#705.

Signed-off-by: Kris Williams <115474+kriswill@users.noreply.github.com>
@kriswill
kriswill requested a review from DeusData as a code owner June 30, 2026 16:24
@DeusData DeusData added enhancement New feature or request ux/behavior Display bugs, docs, adoption UX priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens. labels Jul 1, 2026
@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Huge thanks for opening this PR and for the work you put into it.

This touches the Nix flake / build environment, so it will need the usual careful build-infra review before merge. The maintainer shop is currently full, so this may sit for a bit before it gets a proper review, but we will come back with real feedback as soon as possible.

@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Thanks. Because this changes the Nix build shell and target-arch dependency resolution, maintainers need to verify it after #723. Could you add the exact nix develop .#cross smoke command/output for the host/target arch cases you tested?

@kriswill

kriswill commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@DeusData I added this cross-architecture bit to test #723 under my MacOS+Nix config, to prove that the compiler architecture probe works. I personally am not really interested in compiling x86_64-darwin binaries, since it's not practical for any developer to be using that architecture (very old Apple machines), unless they are doing so on an old Mac-mini in a homelab or something....

The purpose of the probe change is ONLY to enable an engineer to follow the instructions in CONTRIBUTING.md about running the build toolchain when using a nix devshell on Apple Silicon, which currently does NOT work see #705

@DeusData

Copy link
Copy Markdown
Owner

Thank you for this, and apologies for the wait — the delay is entirely on us, and unfortunately main moved under you while it sat. The change itself is sound; it needs one trim before it can land.

What is genuinely well done here. flake.lock is untouched, the default shell is untouched, and the new attribute is opt-in and darwin-only via optionalAttrs (returning {} elsewhere). The prebuilt substitutes ride the nixpkgs input the repo already pins, so this adds zero new supply-chain sources — no URLs, no fetchers, no new inputs, and Nix still verifies NAR hashes against the existing pin. I checked specifically whether this could touch shipped artifacts, because we are currently under a zero-tolerance hold on antivirus false positives and deliberately re-verify staged release binaries without rebuilding: it cannot. No workflow invokes nix at all, and the diff touches only devShells, never packages.default.

It also fills a real gap rather than duplicating one. #723 landed the compile half (-arch via ARCHFLAGS), and this is the complementary link-time half — nix's zlib is single-arch, unlike the universal Apple SDK libz.tbd, so plain-Xcode users never hit this and nix users always do.

The trim: please drop the libgit2 and pkg-config parts.

On 5 July — five days after you opened this — commit 2e055d7 removed libgit2 project-wide, including the Makefile's pkg-config detection, HAVE_LIBGIT2, and the CI leg. The reason is licensing: libgit2 is GPLv2-with-linking-exception, and this tree is deliberately GPL-free (scripts/license-policy.json).

So on current main, these are dead:

  • tpkgs.libgit2 in PKG_CONFIG_PATH and the libgit2 -L in NIX_LDFLAGS
  • pkgs.pkg-config in nativeBuildInputs, and the whole PKG_CONFIG_PATH export — nothing consumes pkg-config anymore

Nothing detects or links libgit2 any longer, so merging as-is would silently reintroduce a GPL-adjacent reference into a tree we scrubbed on purpose. GitHub will still merge it cleanly, which is precisely why it is worth catching by eye.

What remains after the trim — gnumake plus the target-arch zlib include and lib exports — is the genuinely missing piece, since -lz is still a hard dependency. The shell ends up about a third smaller.

One more small thing: the otool -L transcript in the description shows the binary linking libgit2, which is no longer possible on main. A quick re-validation on current main, expecting only the x86_64 nix zlib and no libgit2, would make this immediately mergeable.

And thank you for being straight that no CI runner exercises the cross shell. Agreed, and we would rather keep it that way than grow the matrix for a developer convenience — the honest disclosure is worth more to us than the coverage would be.

@DeusData

Copy link
Copy Markdown
Owner

Thank you for this — and thank you especially for the reason you gave for writing it. You said you didn't personally need x86_64-darwin builds and were doing it to exercise the arch probe from #723. Contributions that exist to make someone else's change verifiable are the ones maintainers quietly depend on and rarely get.

It has shipped as #1761, with Co-authored-by: Kris Williams on the commit.

I'm closing this rather than merging it, and the reason is entirely on my side: the libgit2 trim I asked for turned out to be larger than "delete two lines". Since you branched, main lost not just libgit2 but pkg-config along with it — the default dev shell is now just inputsFrom the package, with zlib as the only library involved. So the trimmed shell hook is a different shape from the one you wrote, and asking you to re-derive that from a review comment, three weeks after the fact, seemed like the wrong way round. The design is yours; I just applied the deletion.

What survived unchanged, because it was right:

  • the darwin-gated lib.optionalAttrs so the attribute simply doesn't exist on Linux;
  • resolving the target arch through the same pinned nixpkgs rather than adding an input, so flake.lock stays untouched and the target-arch libs arrive as prebuilt substitutes;
  • deliberately not using inputsFrom — that one is easy to get wrong, and your comment explaining why (it would put the host-arch zlib back on the link path) is the sort of note that saves the next person an afternoon;
  • the NIX_LDFLAGS-is-searched-first reasoning, and the observation that a stray host-arch copy reaching ld gets skipped as "wrong architecture" rather than silently mislinking.

What changed: libgit2 and pkg-config are gone, leaving zlib as the only library the shell places — which made the hook noticeably smaller.

One honest limitation I've recorded on the new PR: there's no Nix toolchain on my machine, so I validated by parsing the flake with nix-instantiate --parse in a nixos/nix container. That confirms syntax and that the ''${...} escaping resolves correctly, but it does not evaluate the derivation or enter the shell. Your original transcript is the only end-to-end evidence either version has, and the untested delta is a deletion. If you ever have five minutes and a macOS box with Nix, a nix develop .#cross sanity check on main afterwards would be genuinely useful — no obligation whatsoever.

#705 stays open until this lands, then closes with it.

Thanks again — for the shell, and for testing someone else's PR on your own time.

@DeusData DeusData closed this Aug 20, 2026
shashwatMishra96 pushed a commit to shashwatMishra96/codebase-memory-mcp that referenced this pull request Aug 20, 2026
`nix develop .#cross` on macOS exposes the OTHER darwin architecture's
zlib, so `scripts/build.sh --arch <target>` can link an x86_64 binary on
Apple Silicon and vice versa. The native clang already cross-emits object
code via -arch; zlib is the product's single link-time dependency, and the
host-arch copy cannot satisfy a cross link, which is the whole gap.

Darwin-gated and opt-in: the attribute set is empty on Linux, the default
shell is untouched, flake.lock is unchanged and no new flake input is
introduced. The shell deliberately does not use inputsFrom, since that
would put the host-arch zlib back on the link path.

Distilled from DeusData#724 by Kris Williams. That version also wired libgit2 via
pkg-config, which is now dead weight: libgit2 was removed project-wide for
GPL-licensing reasons and appears nowhere in the tree, so both it and
pkg-config are dropped here — leaving zlib as the only library the cross
shell needs to place.

Validated by parsing the flake with nix-instantiate in a nixos/nix
container (no Nix toolchain on the maintainer host), which also confirms
the shellHook's ''${...} escaping resolves to literal shell expansions.
Evaluating or entering the shell needs a macOS host with Nix; no CI leg
exercises it, as noted on the original PR.

Addresses DeusData#705.

Co-authored-by: Kris Williams <115474+kriswill@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens. ux/behavior Display bugs, docs, adoption UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants