fix(nix): Fix for macOS build for nixpkgs#1316
Conversation
Without this flag swiftc compiles a single-file input in script mode and emits a synthetic `_main` into the object file. Packaged into libapple_intelligence.a and linked alongside Rust's `_main`, Apple's open-source ld64 (used by nixpkgs' Darwin stdenv) picks Swift's main, leaving the app with a 5-instruction no-op that returns 0 immediately. The binary looks complete — full Rust code, Metal, Swift runtime, onnxruntime rpath — but launching it exits cleanly with code 0, no output. Production CI masks the issue because Xcode's linker happens to prefer Rust's `_main`. `-parse-as-library` keeps swiftc in library mode so no `_main` is emitted. The @_cdecl exports used by the Rust FFI are unaffected.
Without this swiftc flag the app exits with 0 immediately on nixpkgs Darwin stdenv, because the open-source ld64 picks Swift's synthetic `_main` over Rust's. The fix is one line in src-tauri/build.rs. Cargo.lock, package.json and bun.lock are unchanged between the old and new src rev, so cargoHash and frontendDepsHashes remain valid.
Hunk 3 of the patch failed to apply because cjpais/Handy#1316 added `-parse-as-library` (with explanatory comment) right where the patch expected `-target` to follow `"swiftc"`. Regenerated against current src; the substantive change (drop xcrun wrapper, call swiftc directly) is unchanged.
|
@cjpais CAN confirm this fixes the packaging issues on Nix for Darwin! This would be HUGE to get in :) |
|
Will need some time to merge! I will test it soon, I'm in some deep work right now and not able to context switch. I need to test this on my Mac before I release |
|
Got it! Thanks for your time :) @cjpais -- let me know if you need supporting evidence, because it has proved to be an elusive bug, so I'm worried you won't see a difference. |
|
macOS testers wanted If you're on macOS, a second pair of eyes on this fix would help. Two ways to test: Plain build (no Nix, Xcode toolchain)gh pr checkout 1316
bun install
bun run tauri build
open src-tauri/target/release/bundle/macos/Handy.appThe flag should be a no-op for the Xcode toolchain (Xcode's Under Nix (the build path this PR fixes)The current Nix packaging in philocalyst/nixpkgs#1 pins nix-build https://github.com/xilec/nixpkgs/archive/handy-cross-platform-deps.tar.gz -A handy
./result/bin/handyAlready independently verified on
|
xcrun is unavailable in non-Xcode setups (e.g. nixpkgs uses apple-sdk_* plus a standalone swift compiler). Honor SDKROOT and SWIFTC if set; fall back to xcrun otherwise so Apple-toolchain behavior is unchanged. Also invoke swiftc directly via the resolved path rather than via `xcrun swiftc`.
The SDKROOT/SWIFTC env-var fallbacks in build.rs are now part of cjpais/Handy#1316 alongside -parse-as-library, so the local patch is no longer needed. Bump src.rev to the new HEAD of NixOS#1316.
The SDKROOT/SWIFTC env-var fallbacks in build.rs are now part of cjpais/Handy#1316 alongside -parse-as-library, so the local patch is no longer needed. Bump src.rev to the new HEAD of NixOS#1316.
|
@xilec Yeah, that matches what I tested. My validation was on I have not tested the plain Xcode/Bun path yet, and I do not have an |
|
Thanks for confirmation, hopefully I have a chance tomorrow to test on my machine and pull in, if you leave a comment I'll be sure to see it in the morning :) |
|
@cjpais Morning ping :) |
|
Tested on my Mac, no issues. Just fixing the CI error from before and will pull in after it builds |
🧪 Test Build ReadyBuild artifacts for PR #1316 are available for testing. Download artifacts from workflow run Artifacts expire after 30 days. |
|
Works for me end to end |
|
🎉 thank you @cjpais! If it's not too much trouble, would an intermediate v0.8.3 release be possible anytime soon? It would let us tie our nixpkgs package to it. |
|
Let me check, most likely yes |
|
@cjpais thanks for cutting v0.8.3 so promptly! Really appreciate the fast turnaround. 🎉 |
|
no problem, meant to do it earlier but had to get my laptop repaired |
cjpais/Handy#1316 (-parse-as-library swiftc fix + SDKROOT/SWIFTC env-var fallbacks) shipped in v0.8.3, so revert `src` from the in-flight rev pin back to `tag = "v${finalAttrs.version}"`. bun.lock unchanged between v0.8.2 and v0.8.3, so frontendDepsHashes remain valid; cargoHash bumped due to the version field in src-tauri/Cargo.toml.
Before Submitting This PR
Please confirm you have done the following:
Human Written Description
While debugging the Handy macOS build for nixpkgs it turned out that
swiftcinbuild.rs, when invoked without-parse-as-library, emits its own_mainsymbol into the Apple Intelligence bridge object file, and the open-sourceld64used by nixpkgs' Darwin stdenv picks that_mainover Rust's — so the Rust runtime is never entered and the app silently exits with code 0. Adding the flag keepsswiftcin library mode and the binary works. Production releases (Xcode's linker) mask this because Xcode'sldprefers Rust's_main.A second related fix: the Apple Intelligence bridge build (
build.rs) invokedxcrundirectly to locate the SDK andswiftc, which is unavailable in non-Xcode toolchains (nixpkgs uses standaloneapple-sdk_*+swift). The build now honorsSDKROOTandSWIFTCenv vars when set, falling back toxcrunotherwise — Apple-toolchain behavior is unchanged. Both code paths verified onmacos-26: https://github.com/xilec/Handy/actions/runs/24940175357Related Issues/Discussions
srcis pinned to this PR's HEAD, so it's the easiest end-to-end test of the fix on Darwin under Nix. macOS users wanting to validate this PR under Nix should build from there:nix-build https://github.com/xilec/nixpkgs/archive/handy-cross-platform-deps.tar.gz -A handyworks as-is onaarch64-darwin.Community Feedback
Bug reported by philocalyst (nixpkgs maintainer working on the Handy package) in NixOS/nixpkgs#507754 and in discussion #1242.
Testing
Reproduced and fixed in a Nix flake that mirrors the Darwin build used by nixpkgs. Matrix CI on
macos-latest(aarch64-darwin) across four variants — full run: https://github.com/xilec/Handy/actions/runs/24674832551_mainaddressmain)0x100031180import Foundation, no typealias)0x100031340_mainregardless of file contentapple-sdk_26(realapple_intelligence.swiftwith FoundationModels)0x100031200-parse-as-library(this PR)0x100007b10In each job the collapsed "Diagnose binary (stub check)" step contains the
::group::_main disassembly,::group::_main symbol, and::group::Try to run (…)sections with rawotool -tVoutput and wall-clock timings. The "Compare pre-bundle vs bundled binary" step confirms that the stub_mainis already present insrc-tauri/target/<arch>/release/handybeforecargo tauri bundleruns — i.e., the bug is at Rust/link time, not in bundling.Baseline
_main(5-inst no-opreturn 0):After the fix (
_mainsaves callee-saved registers and calls into rustc'slang_start_internalviablr x8— real Rust entry):Production macOS CI (
macos-26runner, Xcode's linker) is unaffected — Xcode'sldwas already ignoring Swift's_main, the flag just stops emitting that unused symbol.References on swiftc's script-mode
_mainemission:SourceFileKind)-parse-as-libraryDebug branch used for reproduction: https://github.com/xilec/Handy/tree/debug/darwin-flake
Screenshots/Videos (if applicable)
N/A — one-line build-config change, no UI impact.
AI Assistance
If AI was used: