Collecting SwiftPM plugin ideas for the server (but not only) ecosystem

Hi everyone,
now that the package plugin ecosystem is slowly kicking into gear, and it's becoming more common and easy to build useful plugins, the SSWG is looking around for ideas of what kinds of plugins people have been developing as well as what kinds of plugins might still be needed.

Ideas

We're mostly looking for ideas that'd help your day to day server side development, but many of them are universally useful, and not necessarily server specific:

For example here's some ideas we had recently:

We're fishing for ideas for now, and would love to hear about your use-cases and ideas.

Looking for plugins!

We'd also love to hear about plugins you've already developed, and what limitations you've struck or what you'd like to see improved. If you'd need some help, advice, or perhaps would like to increase it's visibility by posting here or getting it properly SSWG endorsed, this is your time to let us know! :+1:

Looking forward to hear from you, thanks in advance!

19 Likes

Thanks for starting this thread Konrad!
Not an idea, but another existing plugin to add under IDL source generators: we just landed one to generate gRPC client/server code from proto files - https://github.com/grpc/grpc-swift/pull/1474

4 Likes

I think Lighter.swift is quite useful on the server (for shipping r/o endpoints and doing small scale servers).
Here is a small server example: NorthwindWebAPI.

@finestructure was asking for PostgreSQL port of Lighter, maybe I'll actually do this (or sth similar).

2 Likes

Sourcery is a popular source code generation tool. we use it for a few things in the MongoDB driver, including keeping a file containing the version string up to date, and generating a list of types to re-export from a module. from a quick search didn't find any SwiftPM plugins for it

3 Likes

We do that internally, good match!

1 Like

shameless plug for swift-package-factory, which i am now using in 3 packages of mine:

  1. swift-json
  2. swift-grammar
  3. swift-dom

it really is the reason why the first two have completely green build matrices on the swift package index, without SPF i would have had to bump the toolchain requirement.

4 Likes

Automated Swift Package versioning plugin PackageBuildInfo can be useful. It generate .swift file with current build info from git. Plugin will never change your local project.

3 Likes

Big +1 for source generation tooling - having a seamless plugin for something like Sourcery could unlock a lot of metaprogramming-esque capabilities, either as simple as the mentioned version string or something more complex like custom Codable conformances.

As a concrete example, there are a lot of runtime decoding hoops I have to jump through in DeepCodable that I would love to be able to offload to compile time with source generation, but a lack of plugins here is a showstopper.

2 Likes

Thanks for starting this @ktoso. The various nio repos contain performance and allocation benchmarking tools. I am not 100% sure if they can be modeled as SPM plugins but it would be nice to give it a try!

4 Likes

I hope to share something there soon, it’s possible.

2 Likes

I would love to see a possibility to pass an argument in the Plugin.
Potential use cases:

  • passing a path to a config file for swiftlint
  • passing a path to a config file for sourcery

Currently, providing a path to a config file has to be done in the Plugin’s implementation e.g. by a config file search using ‘FileManager’ API.

Because of that, every team/company/project would require to create their own Plugin instead of using one, e.g. provided by Swiftlint developers for a Swiftlint usage.

1 Like

I don’t get it, you do get arguments to the plug-in?

func performCommand(context: PluginContext, arguments: [String]) throws {

“Arguments”

Build tool plugins do not have arguments, only command plugins. I was wondering about that before as well, I think it might be useful.
But you can keep a config file within the projects sandbox, that's what I do w/ Lighter (the plugins can be configured by placing a Lighter.json file into the projects root which is then read by the tooling).

2 Likes

Yeah the only way is to put config files in the root dir nowadays.
There was interest to allow further "in Package.swift configuration" as well, that'd then could be type-safe but that wasn't explored more so far.

Interesting note on the cli options being passed through, might be independently useful... thanks for the reminder.

1 Like

Yes, I was referring to build plugins in my mind, thank you for clarifying :slight_smile:

I know that I can use config file in target, and it works fine for monolithic projects (one config for one target).

However, in case of modularised app, when you have each local package as a representation of specific domain and having them more than 30, it starts to be very problematic to maintain configuration for each target, with a high risk of discrepancy between them over time.

Having arguments supported by build plugins would help avoid that by just pointing to the same config file :slight_smile:

1 Like

Will it need some SwiftPM plugin to include cargo/rust project as module in swift project?

SwiftProject
|--Package.swift
|--Sources
   |--SwiftModule
   |--RustModule
      |--module.modulemap
      |--headers.h
      |--Cargo.toml
      |--src
         |--lib.rs

Hi all! Glad to see that you are collecting feedback on SPM plugins. Very recently I added the SPM build tool plugin for the Swift Confidential tool, which I created and made open-source. The plugin itself is located in its own repository. One may ask, why extracting the plugin to the separate repository? Long story short, this is mainly due to the current limitations of SwiftPM and the way it resolves dependencies declared in the package manifest. Now, let's get into the details.

The common limitation of the build tool plugins is that they practically cannot depend on executableTarget with CLI tool, even if you create buildCommand (as opposed to prebuildCommand). Well, it is theoretically possible as long as you limit the use of your plugin to macOS and Linux, or you abandon the use of any dependencies that have platform restrictions. However, in the majority of cases, the plugin creators would like to target as many platforms as possible. In such a configuration, if you attempt to build the client package, having a dependency on build tool plugin, targeting e.g. ios-arm64-simulator architecture you will most likely get an error, because the CLI tool itself (executableTarget) is unlikely to support such architecture. In fact, if your CLI tool uses Swift Argument Parser, it will currently only build for macOS and Linux, which makes perfect sense. What's less sensible is that SwiftPM is not capable of creating a separate dependency graph for the build tool and somehow figure out that even if the package targets iOS, the build tool itself should rather target the host platform (i.e. macos-x86_64 or macos-arm64). I know that the SPM plugin creators are aware of the problem, since it was mentioned at the very end of SE-0303 proposal, but I just want to emphasize the importance of this limitation.

Why is it so problematic you ask? First of all, with the above limitation the plugin author is pretty much forced to generate the so called artifact bundle, which is basically a ZIP file containing a directory with JSON manifest and CLI executable. To my best knowledge, there is currently no official Swift tooling for generating the artifact bundles, thus I wrote my own Bash script to automate this process. It would be great if the swift package was extended with let's say generate-artifactbundle subcommand to free the developers from the burden of writing their own scripts to accomplish this task.
Moving on, once the artifact bundle is generated, the plugin author needs to make a release of the CLI tool (main repo) attaching the generated bundle as a release artifact, and then reference that bundle from the plugin repository as a binaryTarget specifying the matching checksum. Speaking of a checksum, luckily there is an official command (swift package compute-checksum) for computing this checksum, but the way it works is rather questionable. To begin with, based on my research it appears that the only thing this command does is computing the SHA-256 of the specified file (it doesn't even need to be a ZIP file), yet it must be executed from the root of the Swift package directory, because it will first look into Package.swift file to resolve dependencies, even though this step appears to be completely redundant. You can try it out yourself by comparing the checksums computed by the swift package compute-checksum file.zip and shasum -a 256 file.zip | awk '{print $1}'. It seems that there is a potential for performance and usability optimizations in this regard.

To sum up, the current distribution process of the build tool plugins is rather cumbersome. It requires the creation of an additional repository, which needs to be kept in sync with the main repository and its releases.

Based on my recent investigation, it appears that the striked-through part is no longer an issue with Xcode 14 Beta 6.

This post already got long enough, so I will share the rest of my thoughts on plugins along with improvement ideas in a separate posts.

2 Likes

That seems to work fine for me w/ Lighter though, at least within Xcode. Presumably because Xcode (or SPM) knows that the package needs to be run on the host platform?

Like I said in my post, as long as your package targets macOS or Linux, it will work. But have you tried building it for iOS, tvOS or watchOS? Those platforms seem to be irrelevant to your tool, so I suppose that you haven't stumbled upon this problem. Unless there is something that I'm missing of course.

Yes, e.g. the Solar Bodies and the NorthwindSwiftUI sample apps work fine on iOS. Just tried to be sure :see_no_evil:

Xcode has one bug that when the generation happens in a dependency (e.g. like in the Northwind.swift package), not the main app, you need to add a local package to get it to work.
But otherwise it seems to be fine.