Skip to content

Commit

Permalink
Rosetta support (#324)
Browse files Browse the repository at this point in the history
  • Loading branch information
edigaryev authored Nov 21, 2022
1 parent 14cbc72 commit 456ebc1
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 31 deletions.
74 changes: 69 additions & 5 deletions Sources/tart/Commands/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ struct Run: AsyncParsableCommand {
""", valueName: "path[:ro]"))
var disk: [String] = []

@Option(name: [.customLong("rosetta")], help: ArgumentHelp(
"Attaches a Rosetta share to the guest Linux VM with a specific tag (e.g. --rosetta=\"rosetta\")",
discussion: """
Requires host to be macOS 13.0 (Ventura) with Rosetta installed. The latter can be done
by running "softwareupdate --install-rosetta" (without quotes) in the Terminal.app.
Note that you also have to configure Rosetta in the guest Linux VM by following the
steps from "Mount the Shared Directory and Register Rosetta" section here:
https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta#3978496
""",
valueName: "tag"
))
var rosettaTag: String?

@Option(help: ArgumentHelp("""
Additional directory shares with an optional read-only specifier\n(e.g. --dir=\"build:~/src/build\" --dir=\"sources:~/src/sources:ro\")
""", discussion: """
Expand Down Expand Up @@ -107,7 +121,7 @@ struct Run: AsyncParsableCommand {
vmDir: vmDir,
network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(),
additionalDiskAttachments: additionalDiskAttachments,
directoryShares: directoryShares()
directorySharingDevices: directoryShares() + rosettaDirectoryShare()
)

let vncImpl: VNC? = try {
Expand Down Expand Up @@ -242,8 +256,22 @@ struct Run: AsyncParsableCommand {
return result
}

func directoryShares() throws -> [DirectoryShare] {
var result: [DirectoryShare] = []
func directoryShares() throws -> [VZDirectorySharingDeviceConfiguration] {
if dir.isEmpty {
return []
}

guard #available(macOS 13, *) else {
throw UnsupportedOSError("directory sharing", "is")
}

struct DirectoryShare {
let name: String
let path: URL
let readOnly: Bool
}

var directoryShares: [DirectoryShare] = []

for rawDir in dir {
let splits = rawDir.split(maxSplits: 2) { $0 == ":" }
Expand All @@ -264,10 +292,46 @@ struct Run: AsyncParsableCommand {

let (name, path) = (String(splits[0]), String(splits[1]))

result.append(DirectoryShare(name: name, path: URL(fileURLWithPath: NSString(string: path).expandingTildeInPath), readOnly: readOnly))
directoryShares.append(DirectoryShare(
name: name,
path: URL(fileURLWithPath: NSString(string: path).expandingTildeInPath),
readOnly: readOnly)
)
}

return result
var directories: [String : VZSharedDirectory] = Dictionary()
directoryShares.forEach { directories[$0.name] = VZSharedDirectory(url: $0.path, readOnly: $0.readOnly) }

let automountTag = VZVirtioFileSystemDeviceConfiguration.macOSGuestAutomountTag
let sharingDevice = VZVirtioFileSystemDeviceConfiguration(tag: automountTag)
sharingDevice.share = VZMultipleDirectoryShare(directories: directories)

return [sharingDevice]
}

private func rosettaDirectoryShare() throws -> [VZDirectorySharingDeviceConfiguration] {
guard let rosettaTag = rosettaTag else {
return []
}

guard #available(macOS 13, *) else {
throw UnsupportedOSError("Rosetta directory share", "is")
}

switch VZLinuxRosettaDirectoryShare.availability {
case .notInstalled:
throw UnsupportedOSError("Rosetta directory share", "is", "that have Rosetta installed")
case .notSupported:
throw UnsupportedOSError("Rosetta directory share", "is", "running Apple silicon")
default:
break
}

try VZVirtioFileSystemDeviceConfiguration.validateTag(rosettaTag)
let device = VZVirtioFileSystemDeviceConfiguration(tag: rosettaTag)
device.share = try VZLinuxRosettaDirectoryShare()

return [device]
}

private func runUI() {
Expand Down
38 changes: 12 additions & 26 deletions Sources/tart/VM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ struct DownloadFailed: Error {
struct UnsupportedOSError: Error, CustomStringConvertible {
let description: String

init(_ what: String, _ plural: String) {
description = "error: \(what) \(plural) only supported on hosts running macOS 13.0 (Ventura) or newer"
init(_ what: String, _ plural: String, _ requires: String = "running macOS 13.0 (Ventura) or newer") {
description = "error: \(what) \(plural) only supported on hosts \(requires)"
}
}

Expand All @@ -40,7 +40,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
init(vmDir: VMDirectory,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
directoryShares: [DirectoryShare] = []
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = []
) throws {
name = vmDir.name
config = try VMConfig.init(fromURL: vmDir.configURL)
Expand All @@ -54,7 +54,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL,
nvramURL: vmDir.nvramURL, vmConfig: config,
network: network, additionalDiskAttachments: additionalDiskAttachments,
directoryShares: directoryShares)
directorySharingDevices: directorySharingDevices
)
virtualMachine = VZVirtualMachine(configuration: configuration)

super.init()
Expand Down Expand Up @@ -130,7 +131,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
ipswURL: URL,
diskSizeGB: UInt16,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = []
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = []
) async throws {
var ipswURL = ipswURL

Expand Down Expand Up @@ -177,7 +179,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL,
vmConfig: config, network: network,
additionalDiskAttachments: additionalDiskAttachments,
directoryShares: [])
directorySharingDevices: directorySharingDevices
)
virtualMachine = VZVirtualMachine(configuration: configuration)

super.init()
Expand Down Expand Up @@ -253,7 +256,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
vmConfig: VMConfig,
network: Network = NetworkShared(),
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment],
directoryShares: [DirectoryShare]
directorySharingDevices: [VZDirectorySharingDeviceConfiguration]
) throws -> VZVirtualMachineConfiguration {
let configuration = VZVirtualMachineConfiguration()

Expand Down Expand Up @@ -297,19 +300,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
// Entropy
configuration.entropyDevices = [VZVirtioEntropyDeviceConfiguration()]

// Directory share
if #available(macOS 13, *) {
var directories: [String : VZSharedDirectory] = Dictionary()
directoryShares.forEach { directories[$0.name] = VZSharedDirectory(url: $0.path, readOnly: $0.readOnly) }

let automountTag = VZVirtioFileSystemDeviceConfiguration.macOSGuestAutomountTag
let sharingDevice = VZVirtioFileSystemDeviceConfiguration(tag: automountTag)
sharingDevice.share = VZMultipleDirectoryShare(directories: directories)

configuration.directorySharingDevices = [sharingDevice]
} else if !directoryShares.isEmpty {
throw UnsupportedOSError("directory sharing", "is")
}
// Directory sharing devices
configuration.directorySharingDevices = directorySharingDevices

try configuration.validate()

Expand All @@ -331,9 +323,3 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
sema.signal()
}
}

struct DirectoryShare {
let name: String
let path: URL
let readOnly: Bool
}

0 comments on commit 456ebc1

Please sign in to comment.