Using build tool plugins with .xcproj

Hi, not sure if this is Xcode specific or not. But I’m using Xcode version 13.3 (13E113), and have successfully created a little build tool plugin in one of my Swift packages, which generates Swift code for files ending in a specific extension.

I have an iOS app that I want to use to run the code generation with, but it’s using an .xcodeproj, and so xcodebuild as the build system. Does anyone know if it can take advantage of these build tool plugins? The release notes read:

Swift Packages now support build tool plugins, as defined in SE-0303 and SE-0325. This allows packages to define plugins that can specify tools that should run during a build operation, for example to generate source code. This is supported in both swift package and in Xcode’s support for packages. (79876749)

I can see the plugin in the sidebar:

Clicking on the arrow reveals the plugin file in finder. I can’t see Xcode actually attempting to run the build tool plugin.

From what I can tell, only build tool plugins show up in that package plugins section, i.e. command plugins don’t appear.

And if you do add a build tool plugin via a Package.swift file, then Xcode will translate it to a “run custom script” build step in xcodebuild. But once again, this is only if your project uses Package.swift and not a typical .xcodeproj, which seems to be a blocker for using these build tools with apps.

Currently, package plugins are a function only of Swift Packages, and are not related to Xcode projects. It should be possible to have an Xcode project that uses a local package that uses a build tool plugin, though.

And as you noted, command plugins are currently a feature only of the SwiftPM CLI.

2 Likes

From the Xcode 14 beta release notes:

  • Xcode now provides Swift Package plugins with an XcodeProjectPlugin API that extends the Swift Package Manager’s PackagePlugin API. Using this API, the plugin can get a simplified description of the Xcode project’s structure. This lets adapted plugins running in Xcode take advantage of using this API on Xcode projects. You can still use packages that haven’t been adapted to support Xcode projects with Swift Packages in Xcode. (88196725)

This looks exactly like what I’m looking for. Looking forward to trying it out!

Turns out this only seems to support command plugins for now. Hopefully build plugins will be supported at a later date!

Yep, see known issues in the release notes:

Build tool plugins that are adapted to support Xcode projects can’t yet be used with Xcode targets to generate source code and resources. This functionality is expected to be available in a future Xcode 14 beta. (92415898)

1 Like

In Xcode 14 beta 3, I converted my plugin to an XcodeBuildToolPlugin, but I conversely now get an error in the prepare packages build step saying:

Malformed input JSON: Plugin declared with buildTool capability but doesn't conform to BuildToolPlugin protocol

Is there another capability I need to define in Package.swift for Xcode build tools?

Rewatching the WWDC session on package manager plugins, I realised that my plugin needs to conform to both XcodeBuildToolPlugin and BuildToolPlugin, not just the former.
Would it be perhaps worthwhile inheriting BuildToolPlugin from the XcodeBuildToolPlugin protocol to enforce this?

Works a charm now anyway, this is a huge improvement to my workflow

2 Likes

I try to run a very simple plugin but get the following error

error: plugin compilation failed: error: link command failed with exit code 1 (use -v to see invocation)
Undefined symbols for architecture arm64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture arm64

if somebody else has this this is because the plugin was missing @main In the WWDC22 talk of @NeoNacho this was missing.

1 Like

Did you actually get it to run? I cannot seam to get it to run no matter what I do.

Yeah, I had to add it to the list of “Run build tool plug-ins" for my Xcode target in the build phases section

Is there a more efficient way than debugging via file output or throwing errors? Like… could we attach a debugger somehow?