This code allows your build tool plugins to easily and portably run executable targets, executable files by their path, commands that can be invoked from a shell, or Swift script files.
This package provides (and demonstrates) workarounds for Swift Package Manager bugs and limitations.
This is just a partial list:
-
Bugs:
- Plugin outputs are not automatically rebuilt when a plugin's executable changes (SPM issue #6936)
- Broken file system path handling on Windows (SPM issue #6994)
- If you use a plugin to generate tests or source for an executable, on Windows, SPM will try to link the plugin itself into the executable, resulting in “duplicate main” link errors (SPM issue #6859).
-
swift SomeFile.swift
doesn't work on Windows.
-
Limitations:
- No easy way to reentrantly invoke SPM from within a build tool plugin, a key to working around many of the other bugs and limitations described here.
- No easy way to find the source files on which an executable product depends.
- SPM's
Path
type doesn't interoperate well with Foundation'sURL
. - The released version of the API docs for build tool plugins is inaccurate and confusing (fixes here).
Note: Plugin outputs are not automatically rebuilt when a plugin's source changes (SPM issue #6936). We don't have a workaround for this problem.
-
SPM build tool plugins cannot have any dependencies on libraries, so you must arrange for your plugin's source to include
SPMBuildToolSupport.swift
. One way to do that if you want to stay up-to-date with improvements here, and especially if your project contains multiple plugins, is to make this repository a submodule of yours, and symlink the file into each subdirectory of yourPlugins/
directory (assuming standard SPM layout). -
Make your plugin inherit from
SPMBuildToolPlugin
and implement itsbuildCommands
method (instead of inheriting fromBuildToolPlugin
and implementingcreateBuildCommands
). This project contains several examples. Executables that can run build commands are divided into the following cases:-
.targetInThisPackage
: an executable target in the same package as the plugin. -
.file
: a specific executable file. -
.command
: an executable found in the environment's executable search path, given the name you'd use to invoke it in a shell (e.g. "find"). -
.swiftScript
: the executable produced by building a single specific.swift
file, almost as though the file was passed as a parameter to theswift
command. -
.swiftToolchainCommand
: an executable from the currently-running Swift toolchain, given the name you'd use to invoke it in a shell (e.g. "swift", "swiftc", "clang").
-
-
To turn a
PackagePlugin.Path
or aFoundation.URL
into a string that will be recognized by the host OS (say, to pass on a command line), use its.platformString
property. Do not useURL
's other properties (e.g..path
) for this purpose, as tempting as it may be. -
Avoid naïve path manipulations on a
PackagePlugin.Path
directly, which is buggy on some platforms. Consider using itsurl
property and then, if necessary, converting the result back to aPackagePlugin.Path
. -
To avoid spurious warnings from SPM about unhandled sources, do not use SPM's
.sourceFiles(withSuffix: ".in")
to find the input files to your build plugin. Instead, exclude them from the target inPackage.swift
and in your plugin, locate them relative to other directories in your project.LocalTargetCommandDemoPlugin.swift
shows an example. -
On Windows:
- In
Package.swift
, omit executable targets in your package from the list of your build tool's dependencies. - To speed up builds when using
.targetInThisPackage(name:)
:- Make sure all the targets omitted above have a corresponding
.product
of the same name in your package. -
set
SPM_BUILD_TOOL_SUPPORT_NO_REENTRANT_BUILD=1
in your environment - Build those products in a separate build step before building anything that depends on the build tools that use them.
- Make sure all the targets omitted above have a corresponding
- In