mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-18 21:47:08 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02f94720c5 | ||
|
|
c0443060cf | ||
|
|
9c879b3f55 | ||
|
|
f7b38769a9 | ||
|
|
7c1ed4640f |
@@ -10,8 +10,8 @@
|
|||||||
<string>org.cirruslabs.tart</string>
|
<string>org.cirruslabs.tart</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>tart</string>
|
<string>tart</string>
|
||||||
<key>LSBackgroundOnly</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>1</string>
|
<string>public.app-category.developer-tools</string>
|
||||||
<key>CFBundleIconFiles</key>
|
<key>CFBundleIconFiles</key>
|
||||||
<array>
|
<array>
|
||||||
<string>AppIcon.png</string>
|
<string>AppIcon.png</string>
|
||||||
|
|||||||
@@ -47,8 +47,5 @@ struct Pull: AsyncParsableCommand {
|
|||||||
defaultLogger.appendNewLine("pulling \(remoteName)...")
|
defaultLogger.appendNewLine("pulling \(remoteName)...")
|
||||||
|
|
||||||
try await VMStorageOCI().pull(remoteName, registry: registry, concurrency: concurrency, deduplicate: deduplicate)
|
try await VMStorageOCI().pull(remoteName, registry: registry, concurrency: concurrency, deduplicate: deduplicate)
|
||||||
|
|
||||||
// to explicitly set the image as being accessed so it won't get pruned immediately
|
|
||||||
_ = try VMStorageOCI().open(remoteName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ struct Run: AsyncParsableCommand {
|
|||||||
""", valueName: "[name:]path[:options]"))
|
""", valueName: "[name:]path[:options]"))
|
||||||
var dir: [String] = []
|
var dir: [String] = []
|
||||||
|
|
||||||
|
@Flag(help: ArgumentHelp("Enable nested virtualization if possible"))
|
||||||
|
var nested: Bool = false
|
||||||
|
|
||||||
@Option(help: ArgumentHelp("""
|
@Option(help: ArgumentHelp("""
|
||||||
Use bridged networking instead of the default shared (NAT) networking \n(e.g. --net-bridged=en0 or --net-bridged=\"Wi-Fi\")
|
Use bridged networking instead of the default shared (NAT) networking \n(e.g. --net-bridged=en0 or --net-bridged=\"Wi-Fi\")
|
||||||
""", discussion: """
|
""", discussion: """
|
||||||
@@ -221,6 +224,14 @@ struct Run: AsyncParsableCommand {
|
|||||||
throw ValidationError("--captures-system-keys can only be used with the default VM view")
|
throw ValidationError("--captures-system-keys can only be used with the default VM view")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nested {
|
||||||
|
if #unavailable(macOS 15) {
|
||||||
|
throw ValidationError("Nested virtualization is supported on hosts starting with macOS 15 (Sequia), and later.")
|
||||||
|
} else if !VZGenericPlatformConfiguration.isNestedVirtualizationSupported {
|
||||||
|
throw ValidationError("Nested virtualization is available for Mac with the M3 chip, and later.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let localStorage = VMStorageLocal()
|
let localStorage = VMStorageLocal()
|
||||||
let vmDir = try localStorage.open(name)
|
let vmDir = try localStorage.open(name)
|
||||||
if try vmDir.state() == .Suspended {
|
if try vmDir.state() == .Suspended {
|
||||||
@@ -294,6 +305,7 @@ struct Run: AsyncParsableCommand {
|
|||||||
directorySharingDevices: directoryShares() + rosettaDirectoryShare(),
|
directorySharingDevices: directoryShares() + rosettaDirectoryShare(),
|
||||||
serialPorts: serialPorts,
|
serialPorts: serialPorts,
|
||||||
suspendable: suspendable,
|
suspendable: suspendable,
|
||||||
|
nested: nested,
|
||||||
audio: !noAudio,
|
audio: !noAudio,
|
||||||
clipboard: !noClipboard,
|
clipboard: !noClipboard,
|
||||||
sync: VZDiskImageSynchronizationMode(diskOptions.syncModeRaw)
|
sync: VZDiskImageSynchronizationMode(diskOptions.syncModeRaw)
|
||||||
@@ -460,8 +472,10 @@ struct Run: AsyncParsableCommand {
|
|||||||
|
|
||||||
let useVNCWithoutGraphics = (vnc || vncExperimental) && !graphics
|
let useVNCWithoutGraphics = (vnc || vncExperimental) && !graphics
|
||||||
if noGraphics || useVNCWithoutGraphics {
|
if noGraphics || useVNCWithoutGraphics {
|
||||||
// enter the main even loop, without bringing up any UI,
|
// Enter the main event loop without bringing up any UI,
|
||||||
// and just wait for the VM to exit.
|
// waiting for the VM to exit.
|
||||||
|
NSApplication.shared.setActivationPolicy(.prohibited)
|
||||||
|
|
||||||
NSApplication.shared.run()
|
NSApplication.shared.run()
|
||||||
} else {
|
} else {
|
||||||
runUI(suspendable, captureSystemKeys)
|
runUI(suspendable, captureSystemKeys)
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ struct UnsupportedHostOSError: Error, CustomStringConvertible {
|
|||||||
VZMacOSBootLoader()
|
VZMacOSBootLoader()
|
||||||
}
|
}
|
||||||
|
|
||||||
func platform(nvramURL: URL) throws -> VZPlatformConfiguration {
|
func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration {
|
||||||
|
if needsNestedVirtualization {
|
||||||
|
throw RuntimeError.VMConfigurationError("macOS virtual machines do not support nested virtualization")
|
||||||
|
}
|
||||||
|
|
||||||
let result = VZMacPlatformConfiguration()
|
let result = VZMacPlatformConfiguration()
|
||||||
|
|
||||||
result.machineIdentifier = ecid
|
result.machineIdentifier = ecid
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ struct Linux: Platform {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func platform(nvramURL: URL) throws -> VZPlatformConfiguration {
|
func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration {
|
||||||
VZGenericPlatformConfiguration()
|
let config = VZGenericPlatformConfiguration()
|
||||||
|
if #available(macOS 15, *) {
|
||||||
|
config.isNestedVirtualizationEnabled = needsNestedVirtualization
|
||||||
|
}
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration {
|
func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Virtualization
|
|||||||
protocol Platform: Codable {
|
protocol Platform: Codable {
|
||||||
func os() -> OS
|
func os() -> OS
|
||||||
func bootLoader(nvramURL: URL) throws -> VZBootLoader
|
func bootLoader(nvramURL: URL) throws -> VZBootLoader
|
||||||
func platform(nvramURL: URL) throws -> VZPlatformConfiguration
|
func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration
|
||||||
func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration
|
func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration
|
||||||
func keyboards() -> [VZKeyboardConfiguration]
|
func keyboards() -> [VZKeyboardConfiguration]
|
||||||
func pointingDevices() -> [VZPointingDeviceConfiguration]
|
func pointingDevices() -> [VZPointingDeviceConfiguration]
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [],
|
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [],
|
||||||
serialPorts: [VZSerialPortConfiguration] = [],
|
serialPorts: [VZSerialPortConfiguration] = [],
|
||||||
suspendable: Bool = false,
|
suspendable: Bool = false,
|
||||||
|
nested: Bool = false,
|
||||||
audio: Bool = true,
|
audio: Bool = true,
|
||||||
clipboard: Bool = true,
|
clipboard: Bool = true,
|
||||||
sync: VZDiskImageSynchronizationMode = .full
|
sync: VZDiskImageSynchronizationMode = .full
|
||||||
@@ -66,6 +67,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
directorySharingDevices: directorySharingDevices,
|
directorySharingDevices: directorySharingDevices,
|
||||||
serialPorts: serialPorts,
|
serialPorts: serialPorts,
|
||||||
suspendable: suspendable,
|
suspendable: suspendable,
|
||||||
|
nested: nested,
|
||||||
audio: audio,
|
audio: audio,
|
||||||
clipboard: clipboard,
|
clipboard: clipboard,
|
||||||
sync: sync
|
sync: sync
|
||||||
@@ -295,6 +297,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
directorySharingDevices: [VZDirectorySharingDeviceConfiguration],
|
directorySharingDevices: [VZDirectorySharingDeviceConfiguration],
|
||||||
serialPorts: [VZSerialPortConfiguration],
|
serialPorts: [VZSerialPortConfiguration],
|
||||||
suspendable: Bool = false,
|
suspendable: Bool = false,
|
||||||
|
nested: Bool = false,
|
||||||
audio: Bool = true,
|
audio: Bool = true,
|
||||||
clipboard: Bool = true,
|
clipboard: Bool = true,
|
||||||
sync: VZDiskImageSynchronizationMode = .full
|
sync: VZDiskImageSynchronizationMode = .full
|
||||||
@@ -309,7 +312,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||||||
configuration.memorySize = vmConfig.memorySize
|
configuration.memorySize = vmConfig.memorySize
|
||||||
|
|
||||||
// Platform
|
// Platform
|
||||||
configuration.platform = try vmConfig.platform.platform(nvramURL: nvramURL)
|
configuration.platform = try vmConfig.platform.platform(nvramURL: nvramURL, needsNestedVirtualization: nested)
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
configuration.graphicsDevices = [vmConfig.platform.graphicsDevice(vmConfig: vmConfig)]
|
configuration.graphicsDevices = [vmConfig.platform.graphicsDevice(vmConfig: vmConfig)]
|
||||||
|
|||||||
@@ -239,6 +239,9 @@ class VMStorageOCI: PrunableStorage {
|
|||||||
// are excluded from garbage collection
|
// are excluded from garbage collection
|
||||||
VMDirectory(baseURL: vmURL(name)).markExplicitlyPulled()
|
VMDirectory(baseURL: vmURL(name)).markExplicitlyPulled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to explicitly set the image as being accessed so it won't get pruned immediately
|
||||||
|
_ = try VMStorageOCI().open(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func linked(from: RemoteName, to: RemoteName) -> Bool {
|
func linked(from: RemoteName, to: RemoteName) -> Bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user