Improve amfidont binary discovery

This commit is contained in:
Lakr
2026-03-12 13:22:45 +08:00
parent fc66ea6ddf
commit 1fe7d8b37e
2 changed files with 68 additions and 2 deletions
+10 -2
View File
@@ -317,10 +317,18 @@ struct StartAmfidontCLI: AsyncParsableCommand {
@Option(name: .customLong("project-root"), help: "Project root path.", transform: URL.init(fileURLWithPath:))
var projectRoot: URL = VPhoneHost.currentDirectoryURL()
@Option(name: .customLong("amfidont-bin"), help: "Path to amfidont.", transform: URL.init(fileURLWithPath:))
var amfidontBinary: URL = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Library/Python/3.9/bin/amfidont")
@Option(name: .customLong("amfidont-bin"), help: "Path to amfidont. Defaults to PATH/common install locations.", transform: URL.init(fileURLWithPath:))
var amfidontBinary: URL?
mutating func run() async throws {
let amfidontBinary = try VPhoneHost.resolveExecutableURL(
explicit: amfidontBinary,
name: "amfidont",
additionalSearchDirectories: [
URL(fileURLWithPath: "/opt/homebrew/bin", isDirectory: true),
URL(fileURLWithPath: "/usr/local/bin", isDirectory: true),
]
)
try VPhoneHost.requireFile(amfidontBinary)
let releaseBinary = projectRoot.appendingPathComponent(".build/release/vphone-cli")
try VPhoneHost.requireFile(releaseBinary)
@@ -201,6 +201,64 @@ enum VPhoneHost {
}
}
static func resolveExecutableURL(
explicit: URL? = nil,
name: String,
additionalSearchDirectories: [URL] = []
) throws -> URL {
if let explicit {
try requireFile(explicit)
guard FileManager.default.isExecutableFile(atPath: explicit.path) else {
throw VPhoneHostError.invalidArgument("Executable is not runnable: \(explicit.path)")
}
return explicit
}
var searchDirectories: [URL] = []
searchDirectories.append(contentsOf: additionalSearchDirectories)
searchDirectories.append(contentsOf: pathSearchDirectories())
searchDirectories.append(contentsOf: userPythonBinDirectories())
var seenPaths = Set<String>()
for directory in searchDirectories {
let normalized = directory.standardizedFileURL.path
guard seenPaths.insert(normalized).inserted else { continue }
let candidate = directory.appendingPathComponent(name)
if FileManager.default.isExecutableFile(atPath: candidate.path) {
return candidate
}
}
throw VPhoneHostError.missingFile("Executable '\(name)' not found. Install it or pass an explicit path.")
}
static func pathSearchDirectories() -> [URL] {
let defaultPath = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
let pathValue = ProcessInfo.processInfo.environment["PATH"] ?? defaultPath
return pathValue
.split(separator: ":")
.map { URL(fileURLWithPath: String($0), isDirectory: true) }
}
static func userPythonBinDirectories() -> [URL] {
let pythonRoot = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
.appendingPathComponent("Library/Python", isDirectory: true)
guard let entries = try? FileManager.default.contentsOfDirectory(
at: pythonRoot,
includingPropertiesForKeys: [.isDirectoryKey],
options: [.skipsHiddenFiles]
) else {
return []
}
return entries
.filter {
(try? $0.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) ?? false
}
.sorted { $0.lastPathComponent > $1.lastPathComponent }
.map { $0.appendingPathComponent("bin", isDirectory: true) }
}
static func createSparseFile(at url: URL, size: UInt64) throws {
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: url.path) {