I'm trying to write a simple plugin. The same code sometimes errors out and sometimes works:
@main
struct InjectablePlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext,
target: Target) async throws -> [Command]
{
let tool = try context.tool(named: "InjectableTool")
guard let enumerator = FileManager
.default
.enumerator(at: URL(fileURLWithPath: target.directory.string),
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles, .skipsPackageDescendants]),
let urls = enumerator.allObjects as? [URL]
else {
throw InjectablePluginError.failedToListPackageDirectory
}
guard urls.count > 0 else {
throw InjectablePluginError.noFilesToProcess
}
let commands: [Command] = urls.map {
let filename = $0.lastPathComponent
return .buildCommand(displayName: "Processing \(filename)",
executable: tool.path,
arguments: [],
inputFiles: [Path($0.path)])
}
return commands
}
}
This sometimes works, sometimes errors with:
Unexpected duplicate tasks: WriteAuxiliaryFile /Path/To/Script.sh
and sometimes with:
Cycle inside InjectableTests; building could produce unreliable results. This usually can be resolved by moving the shell script phase 'Processing InjectableTests.swift' so that it runs before the build phase that depends on its outputs.
Cycle details:
→ Target 'InjectableTests' has link command with output /Path/To/InjectableTests.swift
○ Target 'InjectableTests' has compile command for Swift source files
○ That command depends on command in Target 'InjectableTests': script phase “Processing InjectableTests.swift”
That error might be detailed for someone who understands how the dependency graph works in SPM, but for me, it just seems to make no sense since my plugin doesn't output anything (yet).