Skip to content

Commit 4bd0ec6

Browse files
scouzi1966claude
andcommitted
fix: resolve executable path via _NSGetExecutablePath, not argv[0]
Bare-name PATH invocation (e.g. `afm` from any shell) sets CommandLine.arguments[0] to "afm", which URL(fileURLWithPath:) treats as relative to the CWD. The metallib search and webui search both walked the wrong base directory, so Homebrew installs failed with "MLX metallib not found" whenever invoked without a full path. Switch both lookups to _NSGetExecutablePath, which goes through the Mach-O loader and returns the true absolute path regardless of how the binary was invoked. Bundle.main.executableURL and argv[0] remain as fallbacks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4cbbe06 commit 4bd0ec6

2 files changed

Lines changed: 44 additions & 6 deletions

File tree

Sources/MacLocalAPI/Server.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Vapor
22
import Foundation
33
import Compression
4+
import Darwin
45
#if canImport(AppKit)
56
import AppKit
67
#endif
@@ -916,13 +917,24 @@ class Server {
916917
let fileManager = FileManager.default
917918
let cwd = fileManager.currentDirectoryPath
918919

919-
// Get the executable's absolute directory
920-
let executablePath = CommandLine.arguments[0]
920+
// Get the executable's absolute directory. argv[0] is unreliable when invoked
921+
// via PATH (it is just "afm"), so go through the Mach-O loader.
922+
var size: UInt32 = 0
923+
_ = _NSGetExecutablePath(nil, &size)
921924
let executableURL: URL
922-
if executablePath.hasPrefix("/") {
923-
executableURL = URL(fileURLWithPath: executablePath)
925+
if size > 0 {
926+
var buffer = [CChar](repeating: 0, count: Int(size))
927+
if _NSGetExecutablePath(&buffer, &size) == 0 {
928+
executableURL = URL(fileURLWithPath: String(cString: buffer)).resolvingSymlinksInPath()
929+
} else if let bundleExec = Bundle.main.executableURL {
930+
executableURL = bundleExec.resolvingSymlinksInPath()
931+
} else {
932+
executableURL = URL(fileURLWithPath: cwd).appendingPathComponent(CommandLine.arguments[0])
933+
}
934+
} else if let bundleExec = Bundle.main.executableURL {
935+
executableURL = bundleExec.resolvingSymlinksInPath()
924936
} else {
925-
executableURL = URL(fileURLWithPath: cwd).appendingPathComponent(executablePath)
937+
executableURL = URL(fileURLWithPath: cwd).appendingPathComponent(CommandLine.arguments[0])
926938
}
927939
let executableDir = executableURL.deletingLastPathComponent().standardized.path
928940

Sources/MacLocalAPI/Utils/MLXMetalLibrary.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
11
import Foundation
22
import ArgumentParser
3+
import Darwin
34

45
enum MLXMetalLibrary {
56
private static let lock = NSLock()
67
private static var initialized = false
78

9+
/// Resolve the absolute path to this binary.
10+
///
11+
/// `CommandLine.arguments[0]` is unreliable: when invoked via PATH it is just the
12+
/// basename ("afm"), so `URL(fileURLWithPath:)` resolves it relative to the current
13+
/// working directory instead of the actual binary location. Use `_NSGetExecutablePath`
14+
/// (which goes through the Mach-O loader) and fall back to `Bundle.main.executableURL`
15+
/// or argv[0] only if that fails.
16+
private static func resolveExecutableURL() -> URL {
17+
var size: UInt32 = 0
18+
_ = _NSGetExecutablePath(nil, &size)
19+
if size > 0 {
20+
var buffer = [CChar](repeating: 0, count: Int(size))
21+
if _NSGetExecutablePath(&buffer, &size) == 0 {
22+
let path = String(cString: buffer)
23+
if !path.isEmpty {
24+
return URL(fileURLWithPath: path).resolvingSymlinksInPath()
25+
}
26+
}
27+
}
28+
if let bundleExec = Bundle.main.executableURL {
29+
return bundleExec.resolvingSymlinksInPath()
30+
}
31+
return URL(fileURLWithPath: CommandLine.arguments[0]).resolvingSymlinksInPath()
32+
}
33+
834
/// Find the metallib without using Bundle.module (which fatalError's when relocated).
935
///
1036
/// Search order:
@@ -14,7 +40,7 @@ enum MLXMetalLibrary {
1440
/// 4. SPM Bundle.module (only if the bundle actually exists — never fatalError)
1541
private static func resolveMetallib() -> URL? {
1642
let fileManager = FileManager.default
17-
let executableURL = URL(fileURLWithPath: CommandLine.arguments[0]).resolvingSymlinksInPath()
43+
let executableURL = resolveExecutableURL()
1844
let executableDir = executableURL.deletingLastPathComponent()
1945

2046
// 1. Explicit env var override

0 commit comments

Comments
 (0)