diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 423e780..f2bcf46 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -894,7 +894,11 @@ class AppState: ObservableObject { } .map { availableXcode -> Xcode in let installedXcode = installedXcodes.first(where: { installedXcode in - availableXcode.version.isEquivalent(to: installedXcode.version) + if availableXcode.architectures == nil { + return availableXcode.version.isEquivalent(to: installedXcode.version) + } else { + return availableXcode.xcodeID == installedXcode.xcodeID + } }) let identicalBuilds: [XcodeID] @@ -913,7 +917,7 @@ class AppState: ObservableObject { } // If the existing install state is "installing", keep it - let existingXcodeInstallState = allXcodes.first { $0.version == availableXcode.version && $0.installState.installing }?.installState + let existingXcodeInstallState = allXcodes.first { $0.id == availableXcode.xcodeID && $0.installState.installing }?.installState // Otherwise, determine it from whether there's an installed Xcode let defaultXcodeInstallState: XcodeInstallState = installedXcode.map { .installed($0.path) } ?? .notInstalled diff --git a/Xcodes/Backend/InstalledXcode.swift b/Xcodes/Backend/InstalledXcode.swift index 87ec7f1..bc2ddd3 100644 --- a/Xcodes/Backend/InstalledXcode.swift +++ b/Xcodes/Backend/InstalledXcode.swift @@ -1,6 +1,7 @@ import Foundation import Version import Path +import XcodesKit /// A version of Xcode that's already installed public struct InstalledXcode: Equatable { @@ -36,16 +37,20 @@ public struct InstalledXcode: Equatable { prereleaseIdentifiers = ["beta"] } - // need: - // lipo -archs /Applications/Xcode-26.0.0-Beta.3.app/Contents/MacOS/Xcode - + let archsString = try? XcodesKit.Current.shell.archs(path.url.appending(path: "Contents/MacOS/Xcode")).out + + let architectures = archsString? + .trimmingCharacters(in: .whitespacesAndNewlines) + .split(separator: " ") + .compactMap { Architecture(rawValue: String($0)) } + let version = Version(major: bundleVersion.major, minor: bundleVersion.minor, patch: bundleVersion.patch, prereleaseIdentifiers: prereleaseIdentifiers, buildMetadataIdentifiers: [versionPlist.productBuildVersion].compactMap { $0 }) - self.xcodeID = XcodeID(version: version, architectures: nil) + self.xcodeID = XcodeID(version: version, architectures: architectures) } } diff --git a/Xcodes/Backend/Xcode.swift b/Xcodes/Backend/Xcode.swift index 20177a0..b172149 100644 --- a/Xcodes/Backend/Xcode.swift +++ b/Xcodes/Backend/Xcode.swift @@ -17,18 +17,6 @@ public struct XcodeID: Codable, Hashable, Identifiable { self.version = version self.architectures = architectures } - - public var architectureString: String { - switch architectures { - case .some(let architectures): - if architectures.isAppleSilicon { - return "Apple Silicon" - } else { - return "Universal" - } - default: return "Universal" - } - } } struct Xcode: Identifiable, CustomStringConvertible { diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift index b83a079..daf28e9 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift @@ -6,10 +6,14 @@ public typealias ProcessOutput = (status: Int32, out: String, err: String) extension Process { static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) async throws -> ProcessOutput { - return try await run(executable.url, workingDirectory: workingDirectory, input: input, arguments) + return try run(executable.url, workingDirectory: workingDirectory, input: input, arguments) } - static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) async throws -> ProcessOutput { + static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) throws -> ProcessOutput { + return try run(executable.url, workingDirectory: workingDirectory, input: input, arguments) + } + + static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) throws -> ProcessOutput { let process = Process() process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Shell/XcodesShell.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Shell/XcodesShell.swift index 4c72795..ef091f2 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Shell/XcodesShell.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Shell/XcodesShell.swift @@ -26,4 +26,8 @@ public struct XcodesShell { public var deleteRuntime: (String) async throws -> ProcessOutput = { try await Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "delete", $0) } + + public var archs: (URL) throws -> ProcessOutput = { + try Process.run(Path.root.usr.bin.join("lipo"), "-archs", $0.path) + } }