From 895adea2f6c6213d32c916db934f2069f1c35f34 Mon Sep 17 00:00:00 2001 From: Kris Williams <115474+kriswill@users.noreply.github.com> Date: Tue, 30 Jun 2026 08:34:24 -0700 Subject: [PATCH] feat(build): add a macOS cross-architecture dev shell 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 #705. Signed-off-by: Kris Williams <115474+kriswill@users.noreply.github.com> --- flake.nix | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index e3b7d76dd..92c16c90a 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,52 @@ let systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + + # Cross dev shell (macOS only): build the *other* darwin arch locally. + # The native clang already cross-EMITS object code via -arch (scripts/env.sh + # exports ARCHFLAGS); this shell only puts the target-arch zlib (a hard + # `-lz` dependency) and libgit2 (optional, found via pkg-config) on the + # link path, since the host-arch copies can't satisfy an x86_64/arm64 link. + # Building needs no Rosetta; *running* an x86_64 binary on Apple Silicon + # does. The target-arch libs are fetched as prebuilt substitutes. Returns + # {} on Linux/Windows (no cross target). + crossDevShells = pkgs: + let + inherit (nixpkgs) lib; + crossTarget = + { + "aarch64-darwin" = "x86_64-darwin"; + "x86_64-darwin" = "aarch64-darwin"; + } + .${pkgs.system} or null; + mkCrossShell = + targetSystem: + let + tpkgs = nixpkgs.legacyPackages.${targetSystem}; + targetArch = if targetSystem == "x86_64-darwin" then "x86_64" else "arm64"; + in + pkgs.mkShell { + # Host toolchain only (clang comes from the shell stdenv); do NOT + # pull host zlib/libgit2 in — that's the wrong-arch copy we avoid. + nativeBuildInputs = [ pkgs.pkg-config pkgs.gnumake ]; + shellHook = '' + # libgit2 headers + libs come through pkg-config (the Makefile + # auto-detects it). zlib is a bare `-lz`/`#include `, so + # supply its include (header is arch-independent) and lib dir by + # hand. NIX_LDFLAGS is searched before the link's own -L, so the + # target-arch slice resolves; the host-arch copy (if any reaches + # ld) is skipped as "wrong architecture". + export PKG_CONFIG_PATH="${lib.getDev tpkgs.libgit2}/lib/pkgconfig:${lib.getDev tpkgs.zlib}/lib/pkgconfig''${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" + export NIX_CFLAGS_COMPILE="-I${lib.getDev tpkgs.zlib}/include ''${NIX_CFLAGS_COMPILE:-}" + export NIX_LDFLAGS="-L${lib.getLib tpkgs.zlib}/lib -L${lib.getLib tpkgs.libgit2}/lib ''${NIX_LDFLAGS:-}" + echo "[cross] target ${targetSystem} — build with: scripts/build.sh --arch ${targetArch}" + ''; + }; + in + lib.optionalAttrs (crossTarget != null) { + # `nix develop .#cross` then `scripts/build.sh --arch `. + cross = mkCrossShell crossTarget; + }; in { packages = forAllSystems (pkgs: { @@ -49,6 +95,6 @@ # otherwise the build falls back to shelling out to `git log`. packages = [ pkgs.pkg-config pkgs.libgit2 ]; }; - }); + } // crossDevShells pkgs); }; }