Skip to content

Commit

Permalink
Better Rosetta 2 Installer
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaacMarovitz committed Nov 22, 2023
1 parent 2b3f910 commit da8ccc7
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 44 deletions.
20 changes: 20 additions & 0 deletions Whisky/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -12463,6 +12463,16 @@
}
}
},
"setup.retry" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Retry"
}
}
}
},
"setup.rosetta" : {
"localizations" : {
"da" : {
Expand Down Expand Up @@ -12581,6 +12591,16 @@
}
}
},
"setup.rosetta.fail" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Rosetta 2 installation failed. Please install it manually."
}
}
}
},
"setup.rosetta.subtitle" : {
"localizations" : {
"da" : {
Expand Down
45 changes: 39 additions & 6 deletions Whisky/Utils/Rosetta2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,47 @@ class Rosetta2 {
return FileManager.default.fileExists(atPath: rosetta2RuntimeBin)
}()

static func launchRosettaInstaller() {
let task = Process()
task.launchPath = "/usr/sbin/softwareupdate"
task.arguments = ["--install-rosetta"]
static func installRosetta() async -> Bool {
let process = Process()
let pipe = Pipe()
guard let log = Log(bottle: nil,
args: [],
environment: nil) else {
return false
}

process.launchPath = "/usr/sbin/softwareupdate"
process.arguments = ["--install-rosetta", "--agree-to-license"]
process.standardOutput = pipe
process.standardError = pipe

pipe.fileHandleForReading.readabilityHandler = { pipe in
let line = String(decoding: pipe.availableData, as: UTF8.self)
log.write(line: "\(line)", printLine: false)
}

do {
try task.run()
try process.run()
var isRunning = true
log.write(line: "Launched Rosetta Install (\(process.processIdentifier))\n")

while isRunning {
process.waitUntilExit()
if pipe.fileHandleForReading.availableData.count == 0 {
isRunning = false
}
}
log.write(line: "Process exited with code \(process.terminationStatus)")
_ = try pipe.fileHandleForReading.readToEnd()

if process.terminationStatus != 0 {
log.write(line: "Failed to install Rosetta 2: \(process.terminationStatus)")
return false
}

return true
} catch {
NSLog("Failed to install Rosetta 2: \(error)")
return false
}
}
}
2 changes: 1 addition & 1 deletion Whisky/Utils/Wine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct WineCrashError: Error, CustomStringConvertible {
let output: String?
let description: String

init( description: String, output: String? = nil) {
init(description: String, output: String? = nil) {
self.output = output
self.description = description
}
Expand Down
103 changes: 67 additions & 36 deletions Whisky/Views/Setup/RosettaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,58 +21,85 @@ import WhiskyKit

struct RosettaView: View {
@State var installing: Bool = true
@State var successful: Bool = true
@Binding var path: [SetupStage]
@Binding var showSetup: Bool

var body: some View {
VStack {
VStack {
Text("setup.rosetta")
.font(.title)
.fontWeight(.bold)
Text("setup.rosetta.subtitle")
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
Group {
if installing {
ProgressView()
.scaleEffect(2)
} else {
Text("setup.rosetta")
.font(.title)
.fontWeight(.bold)
Text("setup.rosetta.subtitle")
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
Group {
if installing {
ProgressView()
.scaleEffect(2)
} else {
if successful {
Image(systemName: "checkmark.circle")
.resizable()
.foregroundStyle(.green)
.frame(width: 80, height: 80)
} else {
VStack {
Image(systemName: "xmark.circle")
.resizable()
.foregroundStyle(.red)
.frame(width: 80, height: 80)
.padding(.bottom, 20)
Text("setup.rosetta.fail")
.font(.subheadline)
}
}
}
.frame(width: 80, height: 80)
Spacer()
}
Spacer()
HStack {
if !successful {
Button("setup.quit") {
exit(0)
}
.keyboardShortcut(.cancelAction)
Spacer()
Button("setup.retry") {
installing = true
successful = true

Task.detached {
await checkOrInstall()
}
}
.keyboardShortcut(.defaultAction)
}
}
}
.frame(width: 400, height: 200)
.onAppear {
Rosetta2.launchRosettaInstaller()
var runCount = 0
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
runCount += 1
Task.detached {
await checkOrInstall()
}
}
}

if Rosetta2.isRosettaInstalled {
timer.invalidate()
installing = false
Task.detached {
sleep(2)
await proceed()
}
}
if runCount >= 300 {
// Timer has run for too long
timer.invalidate()
installing = false
Task.detached {
sleep(2)
await proceed()
}
}
func checkOrInstall() async {
if Rosetta2.isRosettaInstalled {
installing = false

sleep(2)
await proceed()
} else {
let result = await Rosetta2.installRosetta()

installing = false
successful = result

if result {
sleep(2)
await proceed()
}
}
}
Expand All @@ -87,3 +114,7 @@ struct RosettaView: View {
showSetup = false
}
}

#Preview {
RosettaView(path: .constant([]), showSetup: .constant(true))
}
2 changes: 1 addition & 1 deletion Whisky/Views/Setup/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ struct WelcomeView: View {
}
}
}
.frame(width: 400, height: 250)
.frame(width: 400, height: 200)
}

func checkInstallStatus() {
Expand Down

0 comments on commit da8ccc7

Please sign in to comment.