Skip to content

Commit 0b6ae5d

Browse files
authored
Merge pull request #333 from RobotsAndPencils/matt/OpenInRosetta
Adds open in Rosetta option for Apple Silicon machines
2 parents f8ec0a3 + b4a4f8e commit 0b6ae5d

6 files changed

Lines changed: 85 additions & 11 deletions

File tree

Xcodes.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; };
105105
E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; };
106106
E872EE4E2808D4F100D3DD8B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E872EE502808D4F100D3DD8B /* Localizable.strings */; };
107+
E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87AB3C42939B65E00D72F43 /* Hardware.swift */; };
107108
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
108109
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; };
109110
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
@@ -297,6 +298,7 @@
297298
E2AFDCCA28F024D000864ADD /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
298299
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
299300
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
301+
E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = "<group>"; };
300302
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
301303
E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
302304
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
@@ -489,6 +491,7 @@
489491
E87DD6EA25D053FA00D86808 /* Progress+.swift */,
490492
E81D7E9F2805250100A205FC /* Collection+.swift */,
491493
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */,
494+
E87AB3C42939B65E00D72F43 /* Hardware.swift */,
492495
);
493496
path = Backend;
494497
sourceTree = "<group>";
@@ -877,6 +880,7 @@
877880
CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */,
878881
CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */,
879882
CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */,
883+
E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */,
880884
CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */,
881885
CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */,
882886
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */,

Xcodes/Backend/AppState.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ class AppState: ObservableObject {
9393
}
9494
}
9595
}
96-
96+
97+
@Published var showOpenInRosettaOption = false {
98+
didSet {
99+
Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption")
100+
}
101+
}
97102
// MARK: - Publisher Cancellables
98103

99104
var cancellables = Set<AnyCancellable>()
@@ -142,6 +147,7 @@ class AppState: ObservableObject {
142147
createSymLinkOnSelect = Current.defaults.bool(forKey: "createSymLinkOnSelect") ?? false
143148
onSelectActionType = SelectedActionType(rawValue: Current.defaults.string(forKey: "onSelectActionType") ?? "none") ?? .none
144149
installPath = Current.defaults.string(forKey: "installPath") ?? Path.defaultInstallDirectory.string
150+
showOpenInRosettaOption = Current.defaults.bool(forKey: "showOpenInRosettaOption") ?? false
145151
}
146152

147153
// MARK: Timer
@@ -585,13 +591,18 @@ class AppState: ObservableObject {
585591
)
586592
}
587593

588-
func open(xcode: Xcode) {
594+
func open(xcode: Xcode, openInRosetta: Bool? = false) {
589595
switch xcode.installState {
590-
case let .installed(path):
591-
NSWorkspace.shared.openApplication(at: path.url, configuration: .init())
592-
default:
593-
Logger.appState.error("\(xcode.id) is not installed")
594-
return
596+
case let .installed(path):
597+
let config = NSWorkspace.OpenConfiguration.init()
598+
if (openInRosetta ?? false) {
599+
config.architecture = CPU_TYPE_X86_64
600+
}
601+
config.allowsRunningApplicationSubstitution = false
602+
NSWorkspace.shared.openApplication(at: path.url, configuration: config)
603+
default:
604+
Logger.appState.error("\(xcode.id) is not installed")
605+
return
595606
}
596607
}
597608

Xcodes/Backend/Hardware.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
3+
4+
struct Hardware {
5+
6+
///
7+
/// Determines the architecture of the Mac on which we're running. Returns `arm64` for Apple Silicon
8+
/// and `x86_64` for Intel-based Macs or `nil` if the system call fails.
9+
static func getMachineHardwareName() -> String?
10+
{
11+
var sysInfo = utsname()
12+
let retVal = uname(&sysInfo)
13+
var finalString: String? = nil
14+
15+
if retVal == EXIT_SUCCESS
16+
{
17+
let bytes = Data(bytes: &sysInfo.machine, count: Int(_SYS_NAMELEN))
18+
finalString = String(data: bytes, encoding: .utf8)
19+
}
20+
21+
// _SYS_NAMELEN will include a billion null-terminators. Clear those out so string comparisons work as you expect.
22+
return finalString?.trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
23+
}
24+
25+
static func isAppleSilicon() -> Bool {
26+
return Hardware.getMachineHardwareName() == "arm64"
27+
}
28+
}

Xcodes/Backend/XcodeCommands.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,34 @@ struct OpenButton: View {
9292
@EnvironmentObject var appState: AppState
9393
let xcode: Xcode?
9494

95+
var openInRosetta: Bool {
96+
appState.showOpenInRosettaOption && Hardware.isAppleSilicon()
97+
}
98+
9599
var body: some View {
96-
Button(action: open) {
97-
Text("Open")
100+
if openInRosetta {
101+
Menu("Open") {
102+
Button(action: open) {
103+
Text("Open")
104+
}
105+
.help("Open")
106+
Button(action: open) {
107+
Text("Open In Rosetta")
108+
}
109+
.help("Open In Rosetta")
110+
}
111+
} else {
112+
Button(action: open) {
113+
Text("Open")
114+
}
115+
.help("Open")
98116
}
99-
.help("Open")
117+
100118
}
101119

102120
private func open() {
103121
guard let xcode = xcode else { return }
104-
appState.open(xcode: xcode)
122+
appState.open(xcode: xcode, openInRosetta: openInRosetta)
105123
}
106124
}
107125

Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ struct AdvancedPreferencePane: View {
106106
}
107107
.groupBoxStyle(PreferencesGroupBoxStyle())
108108

109+
if Hardware.isAppleSilicon() {
110+
GroupBox(label: Text("Apple Silicon")) {
111+
Toggle("ShowOpenInRosetta", isOn: $appState.showOpenInRosettaOption)
112+
.disabled(appState.createSymLinkOnSelectDisabled)
113+
Text("ShowOpenInRosettaDescription")
114+
.font(.footnote)
115+
.fixedSize(horizontal: false, vertical: true)
116+
}
117+
.groupBoxStyle(PreferencesGroupBoxStyle())
118+
}
109119

110120
GroupBox(label: Text("PrivilegedHelper")) {
111121
VStack(alignment: .leading, spacing: 8) {

Xcodes/Resources/en.lproj/Localizable.strings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@
102102
"HelperNotInstalled" = "Helper is not installed";
103103
"InstallHelper" = "Install helper";
104104

105+
"ShowOpenInRosetta" = "Show Open In Rosetta option";
106+
"ShowOpenInRosettaDescription" = "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines.";
107+
105108
// Experiment Preference Pane
106109
"Experiments" = "Experiments";
107110
"FasterUnxip" = "Faster Unxip";

0 commit comments

Comments
 (0)