What is a package plugin?
Xcode runs plugins, plugins can communicate back
Can be used for custom build tasks
Also custom commands to SwiftPM CLI can be added
Custom commands
First, create a new folder in package called
PluginsCreate another nested folder for plugin target, e.g.
GenerateContributorsCreate a Swift file inside, e.g.
plugin.swiftMake sure to bump the package tools version to 5.6 (or higher)
Insert
.pluginin thetargetsin your package manifest file:
.plugin(
name: "GenerateContributors",
capability: .command(
intent: .custom(
verb: "regenerate-contributors-list",
description: "Generates the CONTRIBUTORS.txt file based on Git logs"
),
permissions: [
.writeToPackageDirectory(reason: "This command write the new CONTRIBUTORS.txt to the source root.")
]
)
),intentcan define a verb for the SwiftPM command line forcommandcapability pluginspermissionsis used if you need to write into a directory (Sandbox)Implement by
import PackagePluginand conforming toCommandPluginprotocolTo shell out to other tools,
import Foundationand then callProcess().executableURLand.argumentsDefine a
Pipeto read output so you can use it in your logic
import PackagePlugin
import Foundation
@main
struct GenerateContributors: CommandPlugin {
func performCommand(
context: PluginContext,
arguments: [String]
) async throws {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/git")
process.arguments = ["log", "--pretty=format:- %an <%ae>%n"]
let outputPipe = Pipe()
process.standardOutput = outputPipe
try process.run()
process.waitUntilExit()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
let contributors = Set(output.components(separatedBy: CharacterSet.newlines)).sorted().filter { !$0.isEmpty }
try contributors.joined(separator: "\n").write(toFile: "CONTRIBUTORS.txt", atomically: true, encoding: .utf8)
}
}New command available in right-click context-menu of package
Xcode will ask for permission showing the text in
permissionsfrom manifest file
Plugins in detail
Package plugins run in a Sandbox, access to work directory only
To wrap another tool, make sure it only writes in work directory
Build Tool Plugins structure (similar to run script phases in Xcode)
Executables
Inputs
Outputs
Types of Build Tool Plugins
In-build command (if you have a clear set of outputs)
Pre-build command (if no clear outputs, but make sure to be performant!)
In-build commands
Plugins get run at the start of the build process
Use
ProcessInfo().argumentsto read arguments in your own toolTo create an executable, create new folder in
SourcesThen add a
main.swiftfileDeclare it in the
Package.manifest
Put the tool target into the
dependenciesof your plugin to use itExtend
BuildToolPlugininstead ofCommandPlugin
Pre-build commands
To make a plugin available to clients of a package, add it to
productsinPackage.swiftmanifest file
