Merge pull request #1 from cirruslabs/basic-implementation

Basic CLI implementation that wraps Virtualization.Framework
This commit is contained in:
Nikolay Edigaryev 2022-02-14 20:56:19 +05:00 committed by GitHub
commit 2c7217f5a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1003 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Xcode's user settings
xcuserdata/

View File

@ -0,0 +1,41 @@
import ArgumentParser
import Dispatch
import SwiftUI
import Foundation
struct Create: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "Create a VM")
@Argument(help: "VM name")
var name: String
@Option(help: ArgumentHelp("Path to the IPSW file (or \"latest\") to fetch the latest appropriate IPSW", valueName: "path")) var fromIPSW: String?
func validate() throws {
if fromIPSW == nil {
throw ValidationError("Please specify a --from-ipsw option!")
}
}
func run() throws {
Task {
do {
let vmDir = try VMStorage().create(name)
if fromIPSW! == "latest" {
_ = try await VM(vmDir: vmDir, ipswURL: nil)
} else {
_ = try await VM(vmDir: vmDir, ipswURL: URL(fileURLWithPath: fromIPSW!))
}
Foundation.exit(0)
} catch {
print(error)
Foundation.exit(1)
}
}
dispatchMain()
}
}

View File

@ -0,0 +1,26 @@
import ArgumentParser
import Dispatch
import SwiftUI
struct Delete: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "Delete a VM")
@Argument(help: "VM name")
var name: String
func run() throws {
Task {
do {
try VMStorage().delete(name)
Foundation.exit(0)
} catch {
print(error)
Foundation.exit(1)
}
}
dispatchMain()
}
}

View File

@ -0,0 +1,25 @@
import ArgumentParser
import Dispatch
import SwiftUI
struct List: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "List created VMs")
func run() throws {
Task {
do {
for vmURL in try VMStorage().list() {
print(vmURL)
}
Foundation.exit(0)
} catch {
print(error)
Foundation.exit(1)
}
}
dispatchMain()
}
}

View File

@ -0,0 +1,7 @@
import ArgumentParser
struct Root: ParsableCommand {
static var configuration = CommandConfiguration(
commandName: "tart",
subcommands: [Create.self, Run.self, List.self, Delete.self])
}

View File

@ -0,0 +1,65 @@
import ArgumentParser
import Dispatch
import SwiftUI
import Virtualization
var vm: VM?
struct Run: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "Run a VM")
@Argument(help: "VM name")
var name: String
@Flag var noGraphics: Bool = false
func run() throws {
let vmDir = try VMStorage().read(name)
vm = try VM(vmDir: vmDir)
Task {
do {
try await vm!.run()
Foundation.exit(0)
} catch {
print(error)
Foundation.exit(1)
}
}
if noGraphics {
dispatchMain()
} else {
// UI mumbo-jumbo
let nsApp = NSApplication.shared
nsApp.setActivationPolicy(.regular)
nsApp.activate(ignoringOtherApps: true)
struct MainApp : App {
var body: some Scene {
WindowGroup {
VMView(vm: vm!)
}
}
}
MainApp.main()
}
}
}
struct VMView: NSViewRepresentable {
typealias NSViewType = VZVirtualMachineView
@ObservedObject var vm: VM
func makeNSView(context: Context) -> NSViewType {
VZVirtualMachineView()
}
func updateNSView(_ nsView: NSViewType, context: Context) {
nsView.virtualMachine = vm.virtualMachine
}
}

185
Sources/tart/VM.swift Normal file
View File

@ -0,0 +1,185 @@
import Foundation
import Virtualization
struct UnsupportedRestoreImageError: Error {}
struct NoMainScreenFoundError: Error {}
class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
// Virtualization.Framework's virtual machine
@Published var virtualMachine: VZVirtualMachine
// Semaphore used to communicate with the VZVirtualMachineDelegate
var sema = DispatchSemaphore(value: 0)
// VM's config
var vmConfig: VMConfig
init(vmDir: VMDirectory) throws {
let auxStorage = VZMacAuxiliaryStorage(contentsOf: vmDir.nvramURL)
self.vmConfig = try VMConfig.init(fromURL: vmDir.configURL)
let configuration = try VM.craftConfiguration(
diskURL: vmDir.diskURL,
ecid: vmConfig.ecid,
auxStorage: auxStorage,
hardwareModel: vmConfig.hardwareModel,
cpuCount: vmConfig.cpuCount,
memorySize: vmConfig.memorySize
)
self.virtualMachine = VZVirtualMachine(configuration: configuration)
super.init()
self.virtualMachine.delegate = self
}
static func retrieveLatestIPSW() async throws -> URL {
let image = try await withCheckedThrowingContinuation { continuation in
VZMacOSRestoreImage.fetchLatestSupported() { result in continuation.resume(with: result) }
}
let (downloadedImageURL, _) = try await URLSession.shared.download(from: image.url, delegate: nil)
return downloadedImageURL
}
init(vmDir: VMDirectory, ipswURL: URL?, diskSize: UInt64 = 32 * 1024 * 1024 * 1024) async throws {
let ipswURL = ipswURL != nil ? ipswURL! : try await VM.retrieveLatestIPSW();
// Load the restore image and try to get the requirements
// that match both the image and our platform
let image = try await withCheckedThrowingContinuation { continuation in
VZMacOSRestoreImage.load(from: ipswURL) { result in continuation.resume(with: result) }
}
guard let requirements = image.mostFeaturefulSupportedConfiguration else { throw UnsupportedRestoreImageError() }
// Create NVRAM
let auxStorage = try VZMacAuxiliaryStorage(creatingStorageAt: vmDir.nvramURL, hardwareModel: requirements.hardwareModel)
// Create disk
FileManager.default.createFile(atPath: vmDir.diskURL.path, contents: nil, attributes: nil)
let diskFileHandle = try FileHandle.init(forWritingTo: vmDir.diskURL)
try diskFileHandle.truncate(atOffset: diskSize)
try diskFileHandle.close()
// Create config
self.vmConfig = VMConfig(
hardwareModel: requirements.hardwareModel,
cpuCount: requirements.minimumSupportedCPUCount,
memorySize: requirements.minimumSupportedMemorySize
)
try self.vmConfig.save(toURL: vmDir.configURL)
// Initialize the virtual machine and its configuration
let configuration = try VM.craftConfiguration(
diskURL: vmDir.diskURL,
ecid: self.vmConfig.ecid,
auxStorage: auxStorage,
hardwareModel: requirements.hardwareModel,
cpuCount: self.vmConfig.cpuCount,
memorySize: self.vmConfig.memorySize
)
self.virtualMachine = VZVirtualMachine(configuration: configuration)
super.init()
self.virtualMachine.delegate = self
// Run automated installation
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
DispatchQueue.main.async {
let installer = VZMacOSInstaller(virtualMachine: self.virtualMachine, restoringFromImageAt: ipswURL)
installer.install { result in continuation.resume(with: result) }
}
}
}
func run() async throws {
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.main.async {
self.virtualMachine.start(completionHandler: { result in
continuation.resume(with: result)
})
}
}
sema.wait()
}
static func craftConfiguration(
diskURL: URL,
ecid: VZMacMachineIdentifier,
auxStorage: VZMacAuxiliaryStorage,
hardwareModel: VZMacHardwareModel,
cpuCount: Int,
memorySize: UInt64
) throws -> VZVirtualMachineConfiguration {
let configuration = VZVirtualMachineConfiguration()
// Boot loader
configuration.bootLoader = VZMacOSBootLoader()
// CPU and memory
configuration.cpuCount = cpuCount
configuration.memorySize = memorySize
// Platform
let platform = VZMacPlatformConfiguration()
platform.machineIdentifier = ecid
platform.auxiliaryStorage = auxStorage
platform.hardwareModel = hardwareModel
configuration.platform = platform
// Display
let graphicsDeviceConfiguration = VZMacGraphicsDeviceConfiguration()
guard let mainScreen = NSScreen.main else {
throw NoMainScreenFoundError()
}
graphicsDeviceConfiguration.displays = [
VZMacGraphicsDisplayConfiguration(for: mainScreen, sizeInPoints: mainScreen.frame.size)
]
configuration.graphicsDevices = [graphicsDeviceConfiguration]
// Keyboard and mouse
configuration.keyboards = [VZUSBKeyboardConfiguration()]
configuration.pointingDevices = [VZUSBScreenCoordinatePointingDeviceConfiguration()]
// Networking
let vio = VZVirtioNetworkDeviceConfiguration()
vio.attachment = VZNATNetworkDeviceAttachment()
configuration.networkDevices = [vio]
// Storage
let attachment = try VZDiskImageStorageDeviceAttachment(url: diskURL, readOnly: false)
let storage = VZVirtioBlockDeviceConfiguration(attachment: attachment)
configuration.storageDevices = [storage]
// Entropy
configuration.entropyDevices = [VZVirtioEntropyDeviceConfiguration()]
try configuration.validate()
return configuration
}
func guestDidStop(_ virtualMachine: VZVirtualMachine) {
print("guest has stopped the virtual machine")
sema.signal()
}
func virtualMachine(_ virtualMachine: VZVirtualMachine, didStopWithError error: Error) {
print("guest has stopped the virtual machine due to error")
sema.signal()
}
func virtualMachine(_ virtualMachine: VZVirtualMachine, networkDevice: VZNetworkDevice, attachmentWasDisconnectedWithError error: Error) {
print("virtual machine's network attachment has been disconnected")
sema.signal()
}
}

View File

@ -0,0 +1,77 @@
import Virtualization
enum CodingKeys: String, CodingKey {
case version
case ecid
case hardwareModel
case cpuCount
case memorySize
}
struct VMConfig: Encodable, Decodable {
var version: Int = 0
var ecid: VZMacMachineIdentifier
var hardwareModel: VZMacHardwareModel
var cpuCount: Int
var memorySize: UInt64
init(ecid: VZMacMachineIdentifier = VZMacMachineIdentifier(), hardwareModel: VZMacHardwareModel, cpuCount: Int, memorySize: UInt64) {
self.ecid = ecid
self.hardwareModel = hardwareModel
self.cpuCount = cpuCount
self.memorySize = memorySize
}
init(fromURL: URL) throws {
let jsonConfigData = try FileHandle.init(forReadingFrom: fromURL).readToEnd()!
self = try JSONDecoder().decode(VMConfig.self, from: jsonConfigData)
}
func save(toURL: URL) throws {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
try encoder.encode(self).write(to: toURL)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.version = try container.decode(Int.self, forKey: .version)
let encodedECID = try container.decode(String.self, forKey: .ecid)
guard let data = Data.init(base64Encoded: encodedECID) else {
throw DecodingError.dataCorruptedError(forKey: .ecid,
in: container,
debugDescription: "failed to initialize Data using the provided value")
}
guard let ecid = VZMacMachineIdentifier.init(dataRepresentation: data) else {
throw DecodingError.dataCorruptedError(forKey: .ecid,
in: container,
debugDescription: "failed to initialize VZMacMachineIdentifier using the provided value")
}
self.ecid = ecid
let encodedHardwareModel = try container.decode(String.self, forKey: .hardwareModel)
guard let data = Data.init(base64Encoded: encodedHardwareModel) else {
throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "")
}
guard let hardwareModel = VZMacHardwareModel.init(dataRepresentation: data) else {
throw DecodingError.dataCorruptedError(forKey: .hardwareModel, in: container, debugDescription: "")
}
self.hardwareModel = hardwareModel
self.cpuCount = try container.decode(Int.self, forKey: .cpuCount)
self.memorySize = try container.decode(UInt64.self, forKey: .memorySize)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.version, forKey: .version)
try container.encode(self.ecid.dataRepresentation.base64EncodedString(), forKey: .ecid)
try container.encode(self.hardwareModel.dataRepresentation.base64EncodedString(), forKey: .hardwareModel)
try container.encode(self.cpuCount, forKey: .cpuCount)
try container.encode(self.memorySize, forKey: .memorySize)
}
}

View File

@ -0,0 +1,32 @@
import Foundation
struct UninitializedVMDirectoryError: Error {}
struct AlreadyInitializedVMDirectoryError: Error {}
struct VMDirectory {
var baseURL: URL
var configURL: URL { self.baseURL.appendingPathComponent("config.json") }
var diskURL: URL { self.baseURL.appendingPathComponent("disk.bin") }
var nvramURL: URL { self.baseURL.appendingPathComponent("nvram.bin") }
var initialized: Bool {
FileManager.default.fileExists(atPath: configURL.path) &&
FileManager.default.fileExists(atPath: diskURL.path) &&
FileManager.default.fileExists(atPath: nvramURL.path)
}
func initialize() throws {
if initialized {
throw AlreadyInitializedVMDirectoryError()
}
try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true, attributes: nil)
}
func validate() throws {
if !initialized {
throw UninitializedVMDirectoryError()
}
}
}

View File

@ -0,0 +1,57 @@
import Foundation
struct VMStorage {
var homeDir: URL
var tartDir: URL
var vmsDir: URL
init() {
homeDir = FileManager.default.homeDirectoryForCurrentUser
tartDir = homeDir.appendingPathComponent(".tart", isDirectory: true)
vmsDir = tartDir.appendingPathComponent("vms", isDirectory: true)
}
func create(_ name: String) throws -> VMDirectory {
let vmDir = VMDirectory(baseURL: vmURL(name))
try vmDir.initialize()
return vmDir
}
func read(_ name: String) throws -> VMDirectory {
let vmDir = VMDirectory(baseURL: vmURL(name))
try vmDir.validate()
return vmDir
}
func delete(_ name: String) throws {
try FileManager.default.removeItem(at: vmURL(name))
}
func list() throws -> [URL] {
do {
return try FileManager.default.contentsOfDirectory(at: vmsDir,
includingPropertiesForKeys: [.isDirectoryKey],
options: .skipsSubdirectoryDescendants)
} catch {
if error.isFileNotFound() {
return []
}
throw error
}
}
private func vmURL(_ name: String) -> URL {
return URL.init(fileURLWithPath: name, isDirectory: true, relativeTo: vmsDir)
}
}
extension Error {
func isFileNotFound() -> Bool {
return (self as NSError).code == NSFileReadNoSuchFileError
}
}

3
Sources/tart/main.swift Normal file
View File

@ -0,0 +1,3 @@
import SwiftUI
Root.main()

View File

@ -0,0 +1,452 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
440478FB27B134E10028EFB8 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 440478FA27B134E10028EFB8 /* ArgumentParser */; };
440478FD27B1352C0028EFB8 /* Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440478FC27B1352C0028EFB8 /* Create.swift */; };
440478FF27B13D590028EFB8 /* Run.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440478FE27B13D590028EFB8 /* Run.swift */; };
4473E1E527A94E28000850C3 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4473E1E427A94E28000850C3 /* main.swift */; };
44FDBB3427B4177C005A201B /* VMStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB3327B4177C005A201B /* VMStorage.swift */; };
44FDBB4227B43E6D005A201B /* VM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4127B43E6D005A201B /* VM.swift */; };
44FDBB4427B4445E005A201B /* Root.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4327B4445E005A201B /* Root.swift */; };
44FDBB4627B44B35005A201B /* VMConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4527B44B35005A201B /* VMConfig.swift */; };
44FDBB4827B45EA1005A201B /* Delete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4727B45EA1005A201B /* Delete.swift */; };
44FDBB4A27B45F6F005A201B /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4927B45F6F005A201B /* List.swift */; };
44FDBB4C27B69515005A201B /* VMDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44FDBB4B27B69515005A201B /* VMDirectory.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
4473E1DF27A94E27000850C3 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
440478FC27B1352C0028EFB8 /* Create.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Create.swift; sourceTree = "<group>"; };
440478FE27B13D590028EFB8 /* Run.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Run.swift; sourceTree = "<group>"; };
4473E1E127A94E27000850C3 /* tart */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tart; sourceTree = BUILT_PRODUCTS_DIR; };
4473E1E427A94E28000850C3 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
44FDBB3327B4177C005A201B /* VMStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMStorage.swift; sourceTree = "<group>"; };
44FDBB3927B43CCF005A201B /* tart-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "tart-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
44FDBB4127B43E6D005A201B /* VM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VM.swift; sourceTree = "<group>"; };
44FDBB4327B4445E005A201B /* Root.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Root.swift; sourceTree = "<group>"; };
44FDBB4527B44B35005A201B /* VMConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfig.swift; sourceTree = "<group>"; };
44FDBB4727B45EA1005A201B /* Delete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delete.swift; sourceTree = "<group>"; };
44FDBB4927B45F6F005A201B /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = "<group>"; };
44FDBB4B27B69515005A201B /* VMDirectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMDirectory.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
4473E1DE27A94E27000850C3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
440478FB27B134E10028EFB8 /* ArgumentParser in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
44FDBB3627B43CCF005A201B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4473E1D827A94E27000850C3 = {
isa = PBXGroup;
children = (
4473E1E327A94E27000850C3 /* Sources */,
4473E1E227A94E27000850C3 /* Products */,
);
sourceTree = "<group>";
};
4473E1E227A94E27000850C3 /* Products */ = {
isa = PBXGroup;
children = (
4473E1E127A94E27000850C3 /* tart */,
44FDBB3927B43CCF005A201B /* tart-tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
4473E1E327A94E27000850C3 /* Sources */ = {
isa = PBXGroup;
children = (
44FDBB4F27B6A4B6005A201B /* tart */,
);
path = Sources;
sourceTree = "<group>";
};
44FDBB4027B43DCB005A201B /* Commands */ = {
isa = PBXGroup;
children = (
440478FC27B1352C0028EFB8 /* Create.swift */,
440478FE27B13D590028EFB8 /* Run.swift */,
44FDBB4327B4445E005A201B /* Root.swift */,
44FDBB4727B45EA1005A201B /* Delete.swift */,
44FDBB4927B45F6F005A201B /* List.swift */,
);
path = Commands;
sourceTree = "<group>";
};
44FDBB4F27B6A4B6005A201B /* tart */ = {
isa = PBXGroup;
children = (
44FDBB4027B43DCB005A201B /* Commands */,
4473E1E427A94E28000850C3 /* main.swift */,
44FDBB3327B4177C005A201B /* VMStorage.swift */,
44FDBB4127B43E6D005A201B /* VM.swift */,
44FDBB4527B44B35005A201B /* VMConfig.swift */,
44FDBB4B27B69515005A201B /* VMDirectory.swift */,
);
path = tart;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
4473E1E027A94E27000850C3 /* tart */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4473E1E827A94E28000850C3 /* Build configuration list for PBXNativeTarget "tart" */;
buildPhases = (
4473E1DD27A94E27000850C3 /* Sources */,
4473E1DE27A94E27000850C3 /* Frameworks */,
4473E1DF27A94E27000850C3 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = tart;
packageProductDependencies = (
440478FA27B134E10028EFB8 /* ArgumentParser */,
);
productName = tart;
productReference = 4473E1E127A94E27000850C3 /* tart */;
productType = "com.apple.product-type.tool";
};
44FDBB3827B43CCF005A201B /* tart-tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 44FDBB3D27B43CCF005A201B /* Build configuration list for PBXNativeTarget "tart-tests" */;
buildPhases = (
44FDBB3527B43CCF005A201B /* Sources */,
44FDBB3627B43CCF005A201B /* Frameworks */,
44FDBB3727B43CCF005A201B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "tart-tests";
productName = "tart-tests";
productReference = 44FDBB3927B43CCF005A201B /* tart-tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
4473E1D927A94E27000850C3 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1320;
LastUpgradeCheck = 1320;
TargetAttributes = {
4473E1E027A94E27000850C3 = {
CreatedOnToolsVersion = 13.2.1;
};
44FDBB3827B43CCF005A201B = {
CreatedOnToolsVersion = 13.2.1;
};
};
};
buildConfigurationList = 4473E1DC27A94E27000850C3 /* Build configuration list for PBXProject "tart" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 4473E1D827A94E27000850C3;
packageReferences = (
440478F927B134E10028EFB8 /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
);
productRefGroup = 4473E1E227A94E27000850C3 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
4473E1E027A94E27000850C3 /* tart */,
44FDBB3827B43CCF005A201B /* tart-tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
44FDBB3727B43CCF005A201B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
4473E1DD27A94E27000850C3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
440478FF27B13D590028EFB8 /* Run.swift in Sources */,
4473E1E527A94E28000850C3 /* main.swift in Sources */,
44FDBB4827B45EA1005A201B /* Delete.swift in Sources */,
44FDBB3427B4177C005A201B /* VMStorage.swift in Sources */,
44FDBB4627B44B35005A201B /* VMConfig.swift in Sources */,
44FDBB4227B43E6D005A201B /* VM.swift in Sources */,
44FDBB4C27B69515005A201B /* VMDirectory.swift in Sources */,
440478FD27B1352C0028EFB8 /* Create.swift in Sources */,
44FDBB4427B4445E005A201B /* Root.swift in Sources */,
44FDBB4A27B45F6F005A201B /* List.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
44FDBB3527B43CCF005A201B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
4473E1E627A94E28000850C3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
4473E1E727A94E28000850C3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
4473E1E927A94E28000850C3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
4473E1EA27A94E28000850C3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
44FDBB3E27B43CCF005A201B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "org.cirruslabs.tart-tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
44FDBB3F27B43CCF005A201B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "org.cirruslabs.tart-tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
4473E1DC27A94E27000850C3 /* Build configuration list for PBXProject "tart" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4473E1E627A94E28000850C3 /* Debug */,
4473E1E727A94E28000850C3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4473E1E827A94E28000850C3 /* Build configuration list for PBXNativeTarget "tart" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4473E1E927A94E28000850C3 /* Debug */,
4473E1EA27A94E28000850C3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
44FDBB3D27B43CCF005A201B /* Build configuration list for PBXNativeTarget "tart-tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
44FDBB3E27B43CCF005A201B /* Debug */,
44FDBB3F27B43CCF005A201B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
440478F927B134E10028EFB8 /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-argument-parser.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
440478FA27B134E10028EFB8 /* ArgumentParser */ = {
isa = XCSwiftPackageProductDependency;
package = 440478F927B134E10028EFB8 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
productName = ArgumentParser;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 4473E1D927A94E27000850C3 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser.git",
"state": {
"branch": null,
"revision": "e394bf350e38cb100b6bc4172834770ede1b7232",
"version": "1.0.3"
}
}
]
},
"version": 1
}