Skip to content

Commit 8346bc2

Browse files
authored
fix(nix): Fix for macOS build for nixpkgs (#1316)
* fix(macos): pass -parse-as-library to swiftc 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. * fix(macos): respect SDKROOT/SWIFTC env vars for non-Xcode toolchains 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`.
1 parent 4b7bb4e commit 8346bc2

1 file changed

Lines changed: 37 additions & 22 deletions

File tree

src-tauri/build.rs

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,20 @@ fn build_apple_intelligence_bridge() {
129129
let object_path = out_dir.join("apple_intelligence.o");
130130
let static_lib_path = out_dir.join("libapple_intelligence.a");
131131

132-
let sdk_path = String::from_utf8(
133-
Command::new("xcrun")
134-
.args(["--sdk", "macosx", "--show-sdk-path"])
135-
.output()
136-
.expect("Failed to locate macOS SDK")
137-
.stdout,
138-
)
139-
.expect("SDK path is not valid UTF-8")
140-
.trim()
141-
.to_string();
132+
// SDKROOT/SWIFTC env-var overrides let non-Xcode toolchains (e.g. nixpkgs
133+
// with apple-sdk_* + standalone swift) bypass xcrun, which is Xcode-only.
134+
let sdk_path = env::var("SDKROOT").unwrap_or_else(|_| {
135+
String::from_utf8(
136+
Command::new("xcrun")
137+
.args(["--sdk", "macosx", "--show-sdk-path"])
138+
.output()
139+
.expect("Failed to locate macOS SDK")
140+
.stdout,
141+
)
142+
.expect("SDK path is not valid UTF-8")
143+
.trim()
144+
.to_string()
145+
});
142146

143147
// Check if the SDK supports FoundationModels (required for Apple Intelligence)
144148
let framework_path =
@@ -157,16 +161,19 @@ fn build_apple_intelligence_bridge() {
157161
panic!("Source file {} is missing!", source_file);
158162
}
159163

160-
let swiftc_path = String::from_utf8(
161-
Command::new("xcrun")
162-
.args(["--find", "swiftc"])
163-
.output()
164-
.expect("Failed to locate swiftc")
165-
.stdout,
166-
)
167-
.expect("swiftc path is not valid UTF-8")
168-
.trim()
169-
.to_string();
164+
// See SDKROOT note above — same env-override pattern for non-Xcode toolchains.
165+
let swiftc_path = env::var("SWIFTC").unwrap_or_else(|_| {
166+
String::from_utf8(
167+
Command::new("xcrun")
168+
.args(["--find", "swiftc"])
169+
.output()
170+
.expect("Failed to locate swiftc")
171+
.stdout,
172+
)
173+
.expect("swiftc path is not valid UTF-8")
174+
.trim()
175+
.to_string()
176+
});
170177

171178
let toolchain_swift_lib = Path::new(&swiftc_path)
172179
.parent()
@@ -178,9 +185,17 @@ fn build_apple_intelligence_bridge() {
178185
// Use macOS 11.0 as deployment target for compatibility
179186
// The @available(macOS 26.0, *) checks in Swift handle runtime availability
180187
// Weak linking for FoundationModels is handled via cargo:rustc-link-arg below
181-
let status = Command::new("xcrun")
188+
let status = Command::new(&swiftc_path)
182189
.args([
183-
"swiftc",
190+
// Without this flag swiftc treats single-file input as script
191+
// mode and emits its own `_main` symbol into the .o, which can
192+
// win the link against Rust's main under some linkers (e.g.
193+
// open-source ld64 used in nixpkgs' Darwin stdenv), producing a
194+
// binary whose main() is a 5-instruction no-op that returns 0.
195+
// `-parse-as-library` keeps the compilation in library mode so
196+
// no `_main` is emitted. See:
197+
// https://forums.swift.org/t/main-in-a-single-swift-file/63079
198+
"-parse-as-library",
184199
"-target",
185200
"arm64-apple-macosx11.0",
186201
"-sdk",

0 commit comments

Comments
 (0)