Editing and debugging a SwiftPM plugin?

I'm trying to add some functionality to the DocC plugin and I'm struggling to find a way to quickly iterate, and ideally step through, the plugin code. Also, I realize this is in part the age-old question, how do I edit package dependencies.

I have an Vapor SPM project (i.e. Package.swift), and I initially added the DocC plugin to my Package.swift, referencing my local git clone like this:

.package(url: "/Users/rmann/Projects/Personal/`SwiftDocCPlugin`/swift-docc-plugin", branch: "rmann/access"),

Then on the command line I invoked

% swift package --disable-sandbox preview-documentation --target App

And I saw it fetch from my local copy (Fetching /Users/rmann/Projects/Personal/SwiftDocCPlugin/swift-docc-plugin).

Unfortunately, changes I make in my local copy aren't picked up when I invoke the plugin a second time.

I realized I might've set it up wrong in Package.swift, so I changed it to this:

.package(path: "/Users/rmann/Projects/Personal/SwiftDocCPlugin/swift-docc-plugin"),

But the behavior is the same.

All the instructions I can find online for making a dependency editable seems to only apply to Xcode, and not swift build or swift package generate-documentation.

Moving on to the harder question: how can I debug the plugin in Xcode (e.g. set breakpoints, step, etc., and of course edit and test changes)?

Q: Editing SwiftPM Plugin

A: You can use .package(path:) for local dependency

Q: Debugging SwiftPM Plugin

A SwiftPM Plugin contains 2 parts of code:

  1. Your @main struct XX: CommandPlugin logic code which runs under Swift-DocC (in your target Package's .build/plugins/Swift-DocC/cache/Swift-DocC location)
  2. and SwiftPM code running under the toolchain's swift-package

When you run swift package preview-documentation, it will actually run swift-package first and then swift-package will launch your CommandPlugin.

In the execution of your CommandPlugin, you may call some API PackagePlugin has provided to you like PackageManager.getSymbolGraph, such API will be implemented and run in swift-package and then pass the data back to and your plugin.

swift-package ->
              -> <Your CommandPlugin>
swift-package <-

Depending on which part of code interests you, you can debug swift-package or your plugin target(Swift-DocC here).

Correct me if any part is wrong. And do you know if we have any better official resource on how to debug Plugin code? @Max_Desiatov

4 Likes

How do I begin execution with a debugger attached from Xcode?

You can use Debug => Attach to Process by PID or Name... with the plugin executable name so that Xcode automatically attaches once the plugin starts executing.

One thing to note is that attaching the debugger is relatively slow, so sometimes this requires adding a 1s sleep to the plugin because otherwise it might finish executing faster than the attach happens.

Yeah that's the challenge, right?

My favourite trick for this problem in general is to add a pause() system call to the front of my code. That stops the process until it receives a signal, and the act of attaching the debugger generates that signal and thus you can just continue from there.

Note that I’ve never tried it with SwiftPM plug-in, but I’ve used it in a wide variety of other circumstances.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

6 Likes

I'm hitting another issue today.

And I decided to use pause() to simplify my workflow (No swift-package debug pause or other sleep stuff)

That stops the process until it receives a signal, and the act of attaching the debugger generates that signal and thus you can just continue from there.

As the above image is showing, I can pause it it successfully. But I can't resume it. Any suggestion here? @eskimo

FWIW, I ran with the same advice and hit the same issue. It seems in the context of a SwiftPM plugin, just attaching the debugger wasn't sufficient to get things rolling again.

In the end, I dropped to literally adding in a ludicrous sleep() in the code to give my fumble fingers enough time to get the debugger attached, which was close enough to get it working to find out what I needed to know. Only had to do that two or three times when I was poking at things to find what I needed, but it wasn't a spectacularly easy process.

+1. End up with sleep(10).

Hmmm, weird. I just re-tested this technique with a command-line tool (on macOS 14.6.1, fwiw) and it works for me. I suspect that plug-in host is changing the signal environment in a way that affects this behaviour, but I’ve no good suggestions for how to investigate.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like