Merge pull request #7880 from utmapp/apple/diskimagekit

apple: adopt DiskImageKit and add disposable mode on macOS 27
This commit is contained in:
osy
2026-09-19 22:01:06 -05:00
committed by GitHub
16 changed files with 312 additions and 54 deletions
+24 -2
View File
@@ -241,9 +241,31 @@ extension UTMAppleConfiguration {
@available(iOS, unavailable, message: "Apple Virtualization not available on iOS")
@MainActor extension UTMAppleConfiguration {
func appleVZConfiguration(forValidation: Bool = false) throws -> VZVirtualMachineConfiguration {
/// Point the firmware variable stores at copies so a disposable run cannot change the originals
@available(macOS 27, *)
private func redirectFirmwareStorage(of vzconfig: VZVirtualMachineConfiguration, to directoryURL: URL) throws {
let fileManager = FileManager.default
if let efi = vzconfig.bootLoader as? VZEFIBootLoader, let variableStore = efi.variableStore {
let copyURL = directoryURL.appendingPathComponent(variableStore.url.lastPathComponent)
try fileManager.copyItem(at: variableStore.url, to: copyURL)
efi.variableStore = VZEFIVariableStore(url: copyURL)
}
#if arch(arm64)
if let platform = vzconfig.platform as? VZMacPlatformConfiguration, let auxiliaryStorage = platform.auxiliaryStorage {
let copyURL = directoryURL.appendingPathComponent(auxiliaryStorage.url.lastPathComponent)
try fileManager.copyItem(at: auxiliaryStorage.url, to: copyURL)
platform.auxiliaryStorage = VZMacAuxiliaryStorage(url: copyURL)
}
#endif
}
/// - Parameter disposableOverlayDirectoryURL: If set, guest writes go to ephemeral layers in this directory and nothing in the package is modified (macOS 27+)
func appleVZConfiguration(forValidation: Bool = false, disposableOverlayDirectoryURL: URL? = nil) throws -> VZVirtualMachineConfiguration {
let vzconfig = VZVirtualMachineConfiguration()
try system.fillVZConfiguration(vzconfig)
if #available(macOS 27, *), let disposableOverlayDirectoryURL = disposableOverlayDirectoryURL {
try redirectFirmwareStorage(of: vzconfig, to: disposableOverlayDirectoryURL)
}
if !sharedDirectories.isEmpty {
let fsConfig = VZVirtioFileSystemDeviceConfiguration(tag: shareDirectoryTag)
fsConfig.share = UTMAppleConfigurationSharedDirectory.makeDirectoryShare(from: sharedDirectories)
@@ -251,7 +273,7 @@ extension UTMAppleConfiguration {
}
if !forValidation {
vzconfig.storageDevices = try drives.compactMap { drive in
guard let attachment = try drive.vzDiskImage(useFsWorkAround: system.boot.operatingSystem == .linux) else {
guard let attachment = try drive.vzDiskImage(useFsWorkAround: system.boot.operatingSystem == .linux, disposableOverlayDirectoryURL: disposableOverlayDirectoryURL) else {
return nil
}
if #available(macOS 13, *), drive.isExternal {
+16 -9
View File
@@ -101,17 +101,24 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
try container.encode(id, forKey: .identifier)
}
func vzDiskImage(useFsWorkAround: Bool = false) throws -> VZDiskImageStorageDeviceAttachment? {
if let imageURL = imageURL {
// Use cached caching mode for virtio drive to prevent fs corruption on linux when possible
if !isNvme, useFsWorkAround {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly, cachingMode: .cached, synchronizationMode: .full)
} else {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
}
} else {
/// Create the storage attachment for this drive
/// - Parameters:
/// - useFsWorkAround: Use cached caching mode for virtio drives to prevent fs corruption on Linux
/// - disposableOverlayDirectoryURL: If set, guest writes go to an ephemeral layer in this directory and the image is never modified (macOS 27+)
func vzDiskImage(useFsWorkAround: Bool = false, disposableOverlayDirectoryURL: URL? = nil) throws -> VZDiskImageStorageDeviceAttachment? {
guard let imageURL = imageURL else {
return nil
}
// Use cached caching mode for virtio drive to prevent fs corruption on linux when possible
let useCachedMode = !isNvme && useFsWorkAround
if #available(macOS 27, *), let overlayDirectoryURL = disposableOverlayDirectoryURL, !isReadOnly {
let overlayURL = overlayDirectoryURL.appendingPathComponent("\(id).asif")
return try UTMAppleDiskImage.attachment(for: imageURL, ephemeralOverlayAt: overlayURL, cachingMode: useCachedMode ? .cached : .automatic, synchronizationMode: .full)
} else if useCachedMode {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly, cachingMode: .cached, synchronizationMode: .full)
} else {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
}
}
func hash(into hasher: inout Hasher) {
+1 -5
View File
@@ -132,12 +132,8 @@ extension UTMConfigurationDrive {
#if os(macOS)
@available(macOS 13, *)
private func createAsifImage(at newURL: URL, size sizeMib: Int) async throws {
let numBlocks = sizeMib * Int(bytesInMib) / 512
guard let asif = UTMASIFImage.sharedInstance() else {
throw UTMConfigurationError.cannotCreateDiskImage
}
try await Task.detached {
try asif.createBlank(with: newURL, numBlocks: numBlocks)
try UTMAppleDiskImage.createASIF(at: newURL, sizeMib: sizeMib)
}.value
}
+2 -2
View File
@@ -59,7 +59,7 @@ struct UTMStartActionIntent: AppIntent, UTMIntent {
@Parameter(title: "Recovery", description: "Boot into recovery mode. Only supported on Apple Virtualization backend.", default: false)
var isRecovery: Bool
@Parameter(title: "Disposible", description: "Do not save any changes to disk. Only supported on QEMU backend.", default: false)
@Parameter(title: "Disposible", description: "Do not save any changes to disk.", default: false)
var isDisposible: Bool
@MainActor
@@ -76,7 +76,7 @@ struct UTMStartActionIntent: AppIntent, UTMIntent {
#endif
}
if isDisposible {
guard vm is UTMQemuVirtualMachine else {
guard type(of: vm).capabilities.supportsDisposibleMode else {
throw UTMIntentError.unsupportedBackend
}
options.insert(.bootDisposibleMode)
+1 -1
View File
@@ -80,7 +80,7 @@ struct VMContextMenuModifier: ViewModifier {
}
#endif
if let _ = vm.config as? UTMQemuConfiguration {
if let wrapped = vm.wrapped, type(of: wrapped).capabilities.supportsDisposibleMode {
Button {
data.run(vm: vm, options: .bootDisposibleMode)
} label: {
+1 -1
View File
@@ -350,7 +350,7 @@ struct AlertMessage: Identifiable {
if #available(macOS 14, *), useNvmeAsDiskInterface {
newDisk.isNvme = true
}
if #available(macOS 26, *), UTMASIFImage.sharedInstance() != nil {
if UTMAppleDiskImage.isASIFSupported {
newDisk.isASIF = true
}
config.drives.append(newDisk)
+4 -16
View File
@@ -942,27 +942,15 @@ enum AlertItem: Identifiable {
@available(macOS 14, *)
func appleDriveInfo(for driveUrl: URL) -> (format: String?, size: Int64?) {
var format: String? = nil
var size: Int64? = nil
guard let info = try? UTMASIFImage.sharedInstance()?.retrieveInfo(driveUrl) else {
return (format, size)
guard let info = try? UTMAppleDiskImage.info(for: driveUrl) else {
return (nil, nil)
}
if let _format = info["Image Format"] as? String {
format = _format
}
if let _sizeInfo = info["Size Info"] as? [String: Any] {
if let _totalBytes = _sizeInfo["Total Bytes"] as? Int64 {
size = _totalBytes
}
}
return (format, size)
return (info.format.localizedDescription, info.size)
}
@available(macOS 14, *)
func resizeAppleDrive(for driveUrl: URL, sizeInMib: Int) throws {
let bytesinMib = 1048576
let size = Int(sizeInMib * bytesinMib)
try UTMASIFImage.sharedInstance()!.resize(with: driveUrl, size: size)
try UTMAppleDiskImage.resize(driveUrl, toSizeMib: sizeInMib)
}
#endif
+1
View File
@@ -317,6 +317,7 @@ enum UTMQuitPolicy: Int {
func applicationDidFinishLaunching(_ notification: Notification) {
CapsLockRemapper.shared.recoverAtLaunch()
UTMAppleVirtualMachine.removeStaleDisposableOverlayDirectories()
if isDockIconHidden {
NSApp.setActivationPolicy(.accessory)
}
@@ -38,7 +38,11 @@ class VMDisplayAppleWindowController: VMDisplayWindowController {
}
var defaultSubtitle: String {
""
if appleVM.isRunningAsDisposible {
return NSLocalizedString("Disposable Mode", comment: "VMDisplayAppleWindowController")
} else {
return ""
}
}
private var isSharePathAlertShownOnce = false
@@ -109,7 +109,7 @@ class VMDisplayWindowController: NSWindowController, UTMVirtualMachineDelegate {
private func stop(isKill: Bool = false) {
showConfirmAlert(NSLocalizedString("This may corrupt the VM and any unsaved changes will be lost. To quit safely, shut down from the guest.", comment: "VMDisplayWindowController")) {
self.enterSuspended(isBusy: true) // early indicator
if self.vm.registryEntry.isSuspended {
if self.vm.registryEntry.isSuspended && !self.vm.isRunningAsDisposible {
self.vm.requestVmDeleteState()
}
self.vm.requestVmStop(force: isKill)
@@ -426,7 +426,7 @@ extension VMDisplayWindowController: NSWindowDelegate {
guard !isSecondary else {
return true
}
guard !(vm.state == .stopped || (vm.state == .paused && vm.registryEntry.isSuspended)) else {
guard !(vm.state == .stopped || (vm.state == .paused && vm.registryEntry.isSuspended && !vm.isRunningAsDisposible)) else {
return true
}
if let snapshotUnsupportedError = vm.snapshotUnsupportedError {
+1 -1
View File
@@ -111,7 +111,7 @@ extension UTMData {
return
}
Task {
if wrapped.registryEntry.isSuspended {
if wrapped.registryEntry.isSuspended && !wrapped.isRunningAsDisposible {
try? await wrapped.deleteSnapshot(name: nil)
}
if vm.state == .started || vm.state == .paused {
+1 -1
View File
@@ -66,7 +66,7 @@ private struct VMMenuItem: View {
data.stop(vm: vm)
}
Button("Suspend") {
let isSnapshot = (vm.wrapped as? UTMQemuVirtualMachine)?.isRunningAsDisposible ?? false
let isSnapshot = vm.wrapped?.isRunningAsDisposible ?? false
vm.wrapped!.requestVmPause(save: !isSnapshot)
}
Button("Reset") {
@@ -24,11 +24,7 @@ struct VMConfigAppleDriveCreateView: View {
@State private var isGiB: Bool = true
private var isASIFSupported: Bool {
if #available(macOS 26, *) {
return UTMASIFImage.sharedInstance() != nil
} else {
return false
}
UTMAppleDiskImage.isASIFSupported
}
var body: some View {
+171
View File
@@ -0,0 +1,171 @@
//
// Copyright © 2026 Turing Software, LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import DiskImageKit
import Virtualization
/// Creates, inspects and resizes disk images for Apple Virtualization drives (ASIF and RAW).
///
/// On macOS 27 and later this uses DiskImageKit. On older hosts it falls back to the private
/// DiskImages2 framework through `UTMASIFImage`. Once the minimum supported host is macOS 27,
/// delete the legacy extension at the bottom of this file along with `UTMASIFImage.h/.m` and
/// its entry in `Swift-Bridging-Header.h`.
enum UTMAppleDiskImage {
enum Format: Equatable {
case asif
case raw
case layered
case other(String)
var localizedDescription: String {
switch self {
case .asif: return NSLocalizedString("ASIF", comment: "UTMAppleDiskImage")
case .raw: return NSLocalizedString("RAW", comment: "UTMAppleDiskImage")
case .layered: return NSLocalizedString("Layered", comment: "UTMAppleDiskImage")
case .other(let name): return name
}
}
}
struct Info {
let format: Format
/// Size of the virtual disk in bytes
let size: Int64
}
private static let bytesInMib = 1048576
/// Block size used when creating images, matches the legacy DiskImages2 path
private static let blockSize = 512
/// True if ASIF images can be created on this host
static var isASIFSupported: Bool {
if #available(macOS 27, *) {
return true
} else if #available(macOS 26, *) {
return UTMASIFImage.sharedInstance() != nil
} else {
return false
}
}
/// Create a new blank ASIF image
/// - Parameters:
/// - url: Location of the new image, must not exist
/// - sizeMib: Size of the virtual disk in MiB
@available(macOS 13, *)
static func createASIF(at url: URL, sizeMib: Int) throws {
let blockCount = sizeMib * bytesInMib / blockSize
if #available(macOS 27, *) {
_ = try DiskImageKit.DiskImage(creating: .asif(url: url, blockCount: blockCount, blockSize: .bytes512))
} else {
try legacyCreateASIF(at: url, blockCount: blockCount)
}
}
/// Read the format and size of an existing image
/// - Parameter url: Location of the image
@available(macOS 14, *)
static func info(for url: URL) throws -> Info {
if #available(macOS 27, *) {
let image = try DiskImageKit.DiskImage(opening: .open(url: url, mode: .readOnly))
let format: Format
switch image.format {
case .asif: format = .asif
case .raw: format = .raw
case .stack: format = .layered
@unknown default: format = .other(String(describing: image.format))
}
return Info(format: format, size: Int64(image.size))
} else {
return try legacyInfo(for: url)
}
}
/// Change the size of the virtual disk of an existing image
/// - Parameters:
/// - url: Location of the image
/// - sizeMib: New size of the virtual disk in MiB
@available(macOS 14, *)
static func resize(_ url: URL, toSizeMib sizeMib: Int) throws {
if #available(macOS 27, *) {
let image = try DiskImageKit.DiskImage(opening: .open(url: url, mode: .readWrite))
try image.truncate(blockCount: sizeMib * bytesInMib / image.blockSize.rawValue)
} else {
try legacyResize(url, toSizeMib: sizeMib)
}
}
}
// MARK: - Layered images (macOS 27+)
@available(macOS 27, *)
extension UTMAppleDiskImage {
/// Create an attachment whose guest writes land in a new copy-on-write layer
///
/// The image at `url` is opened read-only and is never modified. Any existing file at `overlayURL` is replaced.
/// - Parameters:
/// - url: Location of the base image
/// - overlayURL: Location of the overlay layer to create
/// - cachingMode: Caching mode of the attachment
/// - synchronizationMode: Synchronization mode of the attachment
static func attachment(for url: URL, ephemeralOverlayAt overlayURL: URL, cachingMode: VZDiskImageCachingMode = .automatic, synchronizationMode: VZDiskImageSynchronizationMode = .full) throws -> VZDiskImageStorageDeviceAttachment {
try? FileManager.default.removeItem(at: overlayURL)
let base = try DiskImageKit.DiskImage(opening: .open(url: url, mode: .readOnly))
let stack = try base.appending(ASIFCreationConfiguration.layer(url: overlayURL, type: .overlay))
return try VZDiskImageStorageDeviceAttachment(diskImage: stack, cachingMode: cachingMode, synchronizationMode: synchronizationMode)
}
}
// MARK: - Legacy DiskImages2 (macOS 13 to 26)
/// Remove this extension together with `UTMASIFImage` when the minimum host is macOS 27.
private extension UTMAppleDiskImage {
@available(macOS 13, *)
static func legacyCreateASIF(at url: URL, blockCount: Int) throws {
guard let asif = UTMASIFImage.sharedInstance() else {
throw UTMAppleConfigurationError.featureNotSupported
}
try asif.createBlank(with: url, numBlocks: blockCount)
}
@available(macOS 14, *)
static func legacyInfo(for url: URL) throws -> Info {
guard let asif = UTMASIFImage.sharedInstance() else {
throw UTMAppleConfigurationError.featureNotSupported
}
let info = try asif.retrieveInfo(url)
guard let sizeInfo = info["Size Info"] as? [String: Any], let totalBytes = sizeInfo["Total Bytes"] as? Int64 else {
throw UTMAppleConfigurationError.featureNotSupported
}
let format: Format
switch info["Image Format"] as? String {
case "ASIF": format = .asif
case "RAW", "RAW*": format = .raw
case .some(let name): format = .other(name)
case .none: format = .other(NSLocalizedString("Unknown", comment: "UTMAppleDiskImage"))
}
return Info(format: format, size: totalBytes)
}
@available(macOS 14, *)
static func legacyResize(_ url: URL, toSizeMib sizeMib: Int) throws {
guard let asif = UTMASIFImage.sharedInstance() else {
throw UTMAppleConfigurationError.featureNotSupported
}
try asif.resize(with: url, size: sizeMib * bytesInMib)
}
}
+73 -8
View File
@@ -34,7 +34,11 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
}
var supportsDisposibleMode: Bool {
false
if #available(macOS 27, *) {
return true
} else {
return false
}
}
var supportsRecoveryMode: Bool {
@@ -59,7 +63,9 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
private(set) var isShortcut: Bool = false
let isRunningAsDisposible: Bool = false
var isRunningAsDisposible: Bool {
disposableOverlayDirectoryURL != nil
}
weak var delegate: (any UTMVirtualMachineDelegate)?
@@ -118,6 +124,9 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
private var removableDrives: [String: Any] = [:]
/// Holds the ephemeral copy-on-write layers while running without saving changes
private var disposableOverlayDirectoryURL: URL?
/// `UTMAppleUSBPassthroughState` on macOS 27, always nil on older versions
private var usbPassthroughState: Any?
@@ -188,7 +197,9 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
}
}
}
try? updateLastModified()
if !isRunningAsDisposible {
try? updateLastModified()
}
}
func start(options: UTMVirtualMachineStartOptions = []) async throws {
@@ -196,9 +207,16 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
return
}
state = .starting
let isDisposible = options.contains(.bootDisposibleMode)
do {
let isSuspended = await registryEntry.isSuspended
let isRestoring = isSuspended && !options.contains(.bootRecovery)
// a disposable run boots fresh and leaves any saved state untouched
let isRestoring = isSuspended && !isDisposible && !options.contains(.bootRecovery)
// discard the overlays of a run that did not stop cleanly
removeDisposableOverlayDirectory()
if isDisposible {
try createDisposableOverlayDirectory()
}
try await beginAccessingResources()
try await createAppleVM(isRestoring: isRestoring)
if isRestoring {
@@ -236,11 +254,46 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
await stopUsbPassthrough()
}
await releaseFailedAppleVM()
removeDisposableOverlayDirectory()
state = .stopped
try? await deleteSnapshot()
if !isDisposible {
try? await deleteSnapshot()
}
throw error
}
}
/// Create a directory for the ephemeral copy-on-write layers of a disposable run
private func createDisposableOverlayDirectory() throws {
guard #available(macOS 27, *) else {
throw UTMAppleVirtualMachineError.operationNotAvailable
}
let url = FileManager.default.temporaryDirectory.appendingPathComponent("disposable-\(UUID().uuidString)")
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
disposableOverlayDirectoryURL = url
}
/// Discard the ephemeral copy-on-write layers of a disposable run
private func removeDisposableOverlayDirectory() {
if let url = disposableOverlayDirectoryURL {
try? FileManager.default.removeItem(at: url)
}
disposableOverlayDirectoryURL = nil
}
/// Discard the ephemeral copy-on-write layers left behind by a previous process that did not stop cleanly
///
/// Call this before any virtual machine is started.
static func removeStaleDisposableOverlayDirectories() {
let fileManager = FileManager.default
let temporaryDirectoryURL = fileManager.temporaryDirectory
guard let contents = try? fileManager.contentsOfDirectory(at: temporaryDirectoryURL, includingPropertiesForKeys: nil) else {
return
}
for url in contents where url.lastPathComponent.hasPrefix("disposable-") {
try? fileManager.removeItem(at: url)
}
}
private func _forceStop() async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
@@ -538,7 +591,7 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
let serialPort = UTMSerialPort(portNamed: name, readFileHandle: slaveTtyHandle, writeFileHandle: slaveTtyHandle, terminalFileHandle: terminalTtyHandle)
config.serials[i].interface = serialPort
}
let vzConfig = try config.appleVZConfiguration()
let vzConfig = try config.appleVZConfiguration(disposableOverlayDirectoryURL: disposableOverlayDirectoryURL)
var usbControllerDelegate: AnyObject?
if #available(macOS 27, *), hasUsbRedirection {
if isRestoring {
@@ -564,6 +617,9 @@ final class UTMAppleVirtualMachine: UTMVirtualMachine {
}
}
#endif
if isRunningAsDisposible {
snapshotUnsupportedError = UTMAppleVirtualMachineError.cannotSaveInDisposibleMode
}
}
}
@@ -784,6 +840,8 @@ extension UTMAppleVirtualMachine: VZVirtualMachineDelegate {
}
removableDrives.removeAll()
sharedDirectoriesChanged = nil
// must happen before the screenshot is saved so a disposable run leaves the package untouched
removeDisposableOverlayDirectory()
Task { @MainActor in
stopAccesingResources()
if #available(macOS 27, *) {
@@ -932,10 +990,14 @@ extension UTMAppleVirtualMachine {
url.stopAccessingSecurityScopedResource()
}
}
let attachment = try newDrive.vzDiskImage()!
if state == .started {
// detach first so a disposable run can replace the overlay of the old medium
try await detachDrive(id: drive.id)
let attachment = try newDrive.vzDiskImage(disposableOverlayDirectoryURL: disposableOverlayDirectoryURL)!
try await attachDrive(attachment, imageURL: url, id: drive.id)
} else {
// make sure the new medium can be opened before remembering it
_ = try newDrive.vzDiskImage()!
}
let file = try UTMRegistryEntry.File(url: url)
await registryEntry.setExternalDrive(file, forId: drive.id)
@@ -972,7 +1034,7 @@ extension UTMAppleVirtualMachine {
guard drive.isExternal else {
return
}
guard let attachment = try drive.vzDiskImage() else {
guard let attachment = try drive.vzDiskImage(disposableOverlayDirectoryURL: disposableOverlayDirectoryURL) else {
return
}
let configuration = VZUSBMassStorageDeviceConfiguration(attachment: attachment)
@@ -1293,6 +1355,7 @@ enum UTMAppleVirtualMachineError: Error {
case usbDeviceNotFound(String)
case usbDeviceInUse(String)
case usbDeviceNotConnected(String)
case cannotSaveInDisposibleMode
}
extension UTMAppleVirtualMachineError: LocalizedError {
@@ -1316,6 +1379,8 @@ extension UTMAppleVirtualMachineError: LocalizedError {
return String.localizedStringWithFormat(NSLocalizedString("The USB device '%@' is already connected to another virtual machine.", comment: "UTMAppleVirtualMachine"), name)
case .usbDeviceNotConnected(let name):
return String.localizedStringWithFormat(NSLocalizedString("The USB device '%@' is not connected to this virtual machine.", comment: "UTMAppleVirtualMachine"), name)
case .cannotSaveInDisposibleMode:
return NSLocalizedString("Suspend state cannot be saved when running without saving changes.", comment: "UTMAppleVirtualMachine")
}
}
}
+8
View File
@@ -672,6 +672,8 @@
CE7A1C022EFA0A01001671E9 /* UTMAppleUSBDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7A1C012EFA0A01001671E9 /* UTMAppleUSBDevice.swift */; };
CE7A1C042EFA0A01001671E9 /* UTMAppleUSBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7A1C032EFA0A01001671E9 /* UTMAppleUSBManager.swift */; };
CE7A1C062EFA0A01001671E9 /* AccessoryAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7A1C052EFA0A01001671E9 /* AccessoryAccess.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
CE1D15AB2F6B100100D15C01 /* UTMAppleDiskImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1D15AA2F6B100100D15C01 /* UTMAppleDiskImage.swift */; };
CE1D15AD2F6B100100D15C01 /* DiskImageKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1D15AC2F6B100100D15C01 /* DiskImageKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
CE6804852E4E5D84001671E9 /* UTMScriptingInputImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6804842E4E5D84001671E9 /* UTMScriptingInputImpl.swift */; };
CE68E5442E3912E0006B3645 /* VMKeyboardShortcutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE68E5422E3912E0006B3645 /* VMKeyboardShortcutsView.swift */; };
CE68E5452E3912E0006B3645 /* VMKeyboardShortcutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE68E5422E3912E0006B3645 /* VMKeyboardShortcutsView.swift */; };
@@ -2040,6 +2042,8 @@
CE7A1C012EFA0A01001671E9 /* UTMAppleUSBDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMAppleUSBDevice.swift; sourceTree = "<group>"; };
CE7A1C032EFA0A01001671E9 /* UTMAppleUSBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMAppleUSBManager.swift; sourceTree = "<group>"; };
CE7A1C052EFA0A01001671E9 /* AccessoryAccess.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AccessoryAccess.framework; path = System/Library/Frameworks/AccessoryAccess.framework; sourceTree = SDKROOT; };
CE1D15AA2F6B100100D15C01 /* UTMAppleDiskImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMAppleDiskImage.swift; sourceTree = "<group>"; };
CE1D15AC2F6B100100D15C01 /* DiskImageKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskImageKit.framework; path = System/Library/Frameworks/DiskImageKit.framework; sourceTree = SDKROOT; };
CE6804842E4E5D84001671E9 /* UTMScriptingInputImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingInputImpl.swift; sourceTree = "<group>"; };
CE68E5422E3912E0006B3645 /* VMKeyboardShortcutsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMKeyboardShortcutsView.swift; sourceTree = "<group>"; };
CE68E5472E3C3E0A006B3645 /* VMWizardOSClassicMacView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMWizardOSClassicMacView.swift; sourceTree = "<group>"; };
@@ -2362,6 +2366,7 @@
CE0B6F0124AD677200FE012D /* libgsttypefindfunctions.a in Frameworks */,
848308D5278A1F2200E3E474 /* Virtualization.framework in Frameworks */,
CE7A1C062EFA0A01001671E9 /* AccessoryAccess.framework in Frameworks */,
CE1D15AD2F6B100100D15C01 /* DiskImageKit.framework in Frameworks */,
CE0B6EE124AD677200FE012D /* gstrtsp-1.0.0.framework in Frameworks */,
848F71E6277A2466006A0240 /* SwiftTerm in Frameworks */,
CE03D08824D90F0700F76B84 /* gio-2.0.0.framework in Frameworks */,
@@ -2676,6 +2681,7 @@
84937EFE28960789003148F4 /* zstd.1.framework */,
848308D4278A1F2200E3E474 /* Virtualization.framework */,
CE7A1C052EFA0A01001671E9 /* AccessoryAccess.framework */,
CE1D15AC2F6B100100D15C01 /* DiskImageKit.framework */,
8453DCB0278CE33E0037A0DA /* qemu-img.framework */,
CE5451A426AF5F0F008594E5 /* EGL.framework */,
CE5451A226AF5F0F008594E5 /* epoxy.0.framework */,
@@ -3000,6 +3006,7 @@
CE68047D2E493D71001671E9 /* UTMUSBManager.swift */,
CE7A1C012EFA0A01001671E9 /* UTMAppleUSBDevice.swift */,
CE7A1C032EFA0A01001671E9 /* UTMAppleUSBManager.swift */,
CE1D15AA2F6B100100D15C01 /* UTMAppleDiskImage.swift */,
CE020BB524B14F8400B44AB6 /* UTMVirtualMachine.swift */,
CE928C2926ABE6690099F293 /* UTMAppleVirtualMachine.swift */,
4F9F544E438D4BE8A5CA9360 /* UTMAppleVmnetNetworkManager.swift */,
@@ -4175,6 +4182,7 @@
CE6804802E493D71001671E9 /* UTMUSBManager.swift in Sources */,
CE7A1C022EFA0A01001671E9 /* UTMAppleUSBDevice.swift in Sources */,
CE7A1C042EFA0A01001671E9 /* UTMAppleUSBManager.swift in Sources */,
CE1D15AB2F6B100100D15C01 /* UTMAppleDiskImage.swift in Sources */,
CEF0306F26A2AFDF00667B63 /* VMWizardOSView.swift in Sources */,
83034C0926AB630F006B4BAF /* UTMPendingVMView.swift in Sources */,
84909A8F27CACD5C005605F1 /* UTMPlaceholderVMView.swift in Sources */,