1
0
Fork 0

Fix CommandPlugin: Config.toml, auto-extract, pass-through args, pipe output

This commit is contained in:
Brendan Szymanski 2026-07-01 13:54:32 -04:00
parent f2b2026c87
commit af511f8075

View file

@ -1,70 +1,65 @@
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")
let configURL = targetDir.appending(path: "Config.toml")
guard FileManager.default.fileExists(atPath: configURL.path()) else {
continue
}
let girFiles = findGIRFiles(in: targetDir)
guard !girFiles.isEmpty else { continue }
guard let primaryGIR = girFiles.first 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",
var procArgs: [String] = [
"--gir-file", primaryGIR.path(),
"--config-toml", configURL.path(),
"--output", outputDir.path(),
]
if !arguments.isEmpty {
procArgs.append(contentsOf: arguments)
}
process.arguments = procArgs
let stdoutPipe = Pipe()
let stderrPipe = Pipe()
process.standardOutput = stdoutPipe
process.standardError = stderrPipe
try process.run()
process.waitUntilExit()
let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
if let stdoutStr = String(data: stdoutData, encoding: .utf8), !stdoutStr.isEmpty {
print(stdoutStr, terminator: "")
}
if let stderrStr = String(data: stderrData, encoding: .utf8), !stderrStr.isEmpty {
FileHandle.standardError.write(Data(stderrStr.utf8))
}
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 []
}
) else { return [] }
var girFiles: [URL] = []
for case let fileURL as URL in enumerator {
if fileURL.pathExtension == "gir" {
@ -75,14 +70,11 @@ struct SwiftGtkGenCommandPlugin: CommandPlugin {
}
}
/// 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):