Extracted from gtk-swift monorepo. Previous history contains 38 commits across the full Phase 1 development including GIR XML parsing, IR model, analysis engine, code generation, CLI, plugins, and documentation.
92 lines
3.4 KiB
Swift
92 lines
3.4 KiB
Swift
import PackagePlugin
|
|
import Foundation
|
|
|
|
/// SwiftPM command plugin for manual GIR binding regeneration.
|
|
///
|
|
/// Invoked via `swift package generate-gir`. Iterates over all package
|
|
/// targets, checks for a `Config.swift` and `.gir` files, and runs the
|
|
/// `swift-gtk-gen` executable to produce or update generated Swift sources in
|
|
/// each target's `Generated/` directory.
|
|
@main
|
|
struct SwiftGtkGenCommandPlugin: CommandPlugin {
|
|
/// Performs GIR code generation for all eligible targets in the package.
|
|
///
|
|
/// For each target that has a `Config.swift` and at least one `.gir` file,
|
|
/// launches `swift-gtk-gen` as a subprocess with arguments derived from the
|
|
/// target's directory and metadata. Generated files are written to
|
|
/// `<target>/Generated/`. Throws if any subprocess fails.
|
|
///
|
|
/// - Parameters:
|
|
/// - context: The plugin context providing access to tools and package targets.
|
|
/// - arguments: Unused command-line arguments passed to the plugin.
|
|
func performCommand(context: PluginContext, arguments: [String]) async throws {
|
|
let executable = try context.tool(named: "swift-gtk-gen")
|
|
|
|
for target in context.package.targets {
|
|
let targetDir = target.directoryURL
|
|
let configURL = targetDir.appending(path: "Config.swift")
|
|
|
|
guard FileManager.default.fileExists(atPath: configURL.path()) else {
|
|
continue
|
|
}
|
|
|
|
let girFiles = findGIRFiles(in: targetDir)
|
|
guard !girFiles.isEmpty else { continue }
|
|
|
|
let outputDir = targetDir.appending(path: "Generated")
|
|
|
|
let process = Process()
|
|
process.executableURL = executable.url
|
|
process.arguments = [
|
|
"--config", configURL.path(),
|
|
"--library", target.name,
|
|
"--version", "4.0",
|
|
"--output", outputDir.path(),
|
|
]
|
|
try process.run()
|
|
process.waitUntilExit()
|
|
|
|
guard process.terminationStatus == 0 else {
|
|
throw SwiftGtkGenPluginError.generationFailed(target.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Recursively finds all `.gir` files in the given directory.
|
|
///
|
|
/// Uses a directory enumerator to walk the file tree and collect URLs
|
|
/// whose path extension is `"gir"`.
|
|
///
|
|
/// - Parameter directory: The root directory to search.
|
|
/// - Returns: An array of URLs pointing to discovered `.gir` files.
|
|
private func findGIRFiles(in directory: URL) -> [URL] {
|
|
guard let enumerator = FileManager.default.enumerator(
|
|
at: directory, includingPropertiesForKeys: [.isRegularFileKey]
|
|
) else {
|
|
return []
|
|
}
|
|
var girFiles: [URL] = []
|
|
for case let fileURL as URL in enumerator {
|
|
if fileURL.pathExtension == "gir" {
|
|
girFiles.append(fileURL)
|
|
}
|
|
}
|
|
return girFiles
|
|
}
|
|
}
|
|
|
|
/// Errors that can occur during command-plugin GIR generation.
|
|
enum SwiftGtkGenPluginError: Error {
|
|
/// Generation failed for the specified target name.
|
|
case generationFailed(String)
|
|
}
|
|
|
|
extension SwiftGtkGenPluginError: CustomStringConvertible {
|
|
/// A human-readable description of the error.
|
|
var description: String {
|
|
switch self {
|
|
case .generationFailed(let target):
|
|
return "GIR generation failed for target: \(target)"
|
|
}
|
|
}
|
|
}
|