Files
luma/Sources/LumaCore/EngineRegistry.swift
Ole André Vadla Ravnås 0e933206fb Boot virtual machines from the target picker
Add a machine abstraction to LumaCore, with QEMU as its first
backend: templates mirroring frida-core's test guests, a QMP client,
and a display that maps QEMU's surface over D-Bus rather than
copying frames.

Leave readiness to the user: drive the guest, mark it ready, and come
back to that snapshot later. Keep the machine in the project, so
booting it again resumes where it was marked. Hand the stub to
addBareboneDevice, so it joins the picker beside the other devices.

Show the display while booting, and afterwards in a Machines panel,
which the side panel now models as an enum to share its slot.
2026-08-20 14:52:52 +02:00

69 lines
2.2 KiB
Swift

import Foundation
@MainActor
public final class EngineRegistry {
public static let shared = EngineRegistry()
public init() {}
private var engines: [URL: Engine] = [:]
private var startTasks: [URL: Task<Void, Never>] = [:]
public func engine(
for workingProjectURL: URL,
dataDirectory: URL,
gitHubAuth: GitHubAuth? = nil
) throws -> Engine {
let key = workingProjectURL.standardizedFileURL
if let existing = engines[key] {
return existing
}
let fm = FileManager.default
let dbURL = key.appendingPathComponent("db.sqlite")
let tracesURL = key.appendingPathComponent("traces", isDirectory: true)
let eventsURL = key.appendingPathComponent("events.log")
try fm.createDirectory(at: key, withIntermediateDirectories: true)
try fm.createDirectory(at: tracesURL, withIntermediateDirectories: true)
let store = try ProjectStore(path: dbURL.path)
let blobs = try BlobStore(directory: tracesURL)
let eventStore = EventStore(fileURL: eventsURL)
let engine = Engine(
store: store,
blobs: blobs,
eventStore: eventStore,
dataDirectory: dataDirectory,
gitHubAuth: gitHubAuth
)
engines[key] = engine
return engine
}
public func startIfNeeded(for workingProjectURL: URL) async {
let key = workingProjectURL.standardizedFileURL
if let existing = startTasks[key] {
await existing.value
return
}
guard let engine = engines[key] else { return }
let task = Task { @MainActor in
await engine.start()
}
startTasks[key] = task
await task.value
}
public func shutdownAll() async {
for url in Array(engines.keys) {
await release(workingProjectURL: url)
}
}
public func release(workingProjectURL: URL) async {
let key = workingProjectURL.standardizedFileURL
let pending = startTasks.removeValue(forKey: key)
let engine = engines.removeValue(forKey: key)
await pending?.value
await engine?.shutdown()
}
}