I am trying to create a build tool plugin for a package (SwiftGtk). It is a source generation plugin, so I have mostly just copied the SwiftGen example plugin.
I got the plugin working correctly at one point (for SwiftGLib, a dependency of SwiftGtk which I am working on first), but after deleting the .build
directory to verify that it works for clean builds, I got an error. The error seems to be caused by SwiftPM not building the plugin's dependencies before running commands generated by the plugin. The plugin declares the tool as a dependency in the package manifest. Here's the error (I added newlines and indentation to make it readable):
error: failed: PrebuildCommand(
configuration: SPMBuildCore.BuildToolPluginInvocationResult.CommandConfiguration(
displayName: Optional("Running gir2swift"),
executable: <AbsolutePath:"/Users/user/Desktop/Projects/Swift/SwiftGtk/SwiftGLib/.build/x86_64-apple-macosx/debug/gir2swift">,
arguments: [
"-o", "/Users/user/Desktop/Projects/Swift/SwiftGtk/SwiftGLib/.build/plugins/outputs/swiftglib/GLib/Gir2SwiftPlugin/Gir2SwiftOutputDir",
"--manifest", "/Users/user/Desktop/Projects/Swift/SwiftGtk/SwiftGLib/gir2swift-manifest.yml"
],
environment: [:],
workingDirectory: nil
),
outputFilesDirectory: <AbsolutePath:"/Users/user/Desktop/Projects/Swift/SwiftGtk/SwiftGLib/.build/plugins/outputs/swiftglib/GLib/Gir2SwiftPlugin/Gir2SwiftOutputDir">
)
Here are the relevant parts of the Package.swift
:
let package = Package(
name: "GLib",
products: [ .library(name: "GLib", targets: ["GLib"]) ],
dependencies: [
// ...
],
targets: [
.systemLibrary(
name: "CGLib",
pkgConfig: "gio-unix-2.0",
providers: [
// ...
]
),
.target(
name: "GLib",
dependencies: ["CGLib"],
plugins: ["Gir2SwiftPlugin"]
),
.executableTarget(
name: "gir2swift",
dependencies: ["libgir2swift"]
),
.target(name: "libgir2swift"),
.plugin(name: "Gir2SwiftPlugin", capability: .buildTool(), dependencies: [.target(name: "gir2swift")]),
]
)
And here's the plugin:
@main struct Gir2SwiftPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
let outputDir = context.pluginWorkDirectory.appending("Gir2SwiftOutputDir")
try FileManager.default.createDirectory(atPath: outputDir.string, withIntermediateDirectories: true)
return [.prebuildCommand(
displayName: "Running gir2swift",
executable: try context.tool(named: "gir2swift").path,
arguments: [
"-o", outputDir.string,
"--manifest", context.package.directory.appending("gir2swift-manifest.yml"),
],
outputFilesDirectory: outputDir
)]
}
}
Perhaps there is some quirk about dependencies in plugins that I don't know about? Can build tool plugins only rely on binary targets or something? From what I have seen that doesn't seem to be the case, because SwiftPM was actively building the gir2swift
tool as part of the plugin's build originally, and I don't know what I changed for it to stop working.
Also: I have had so many weird issues with plugins trying to get this working, originally I had the plugin in a separate package but it wouldn't run, and then I moved the plugin into the package (but not the gir2swift
tool) and it claimed that the gir2swift
product didn't exist. Once I get the plugin working with everything in one package I may come back and post about some of the other issues I was facing. Even if I am just missing something, the error reporting for build tool plugins seems pretty bad. Many of the issues that I am facing have very minimal error messages, and when a plugin attempts to write outside of the sandbox, it seems to just silently fail (I will have to do further testing to confirm that, but it does seem to be the case from what I have observed).
If it's helpful I have uploaded a zip of the package to we transfer, but that link will only last a week so if anyone has a better place to host the file let me know (it's a pity files can't be attached to posts).