# SE-0332: Package Manager Command Plugins

**URL:** <https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769>\
**Category:** Proposal Reviews\
**Created:** [November 29, 2021, 8:14pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769 "2021-11-29T20:14:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [November 29, 2021, 8:14pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/1 "2021-11-29T20:14:24Z")

</div>

The review of [SE-0332: Package Manager Command Plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0332-swiftpm-command-plugins.md), begins now and runs through Dec 10th, 2021.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager or direct message in the Swift forums).

**What goes into a review?**

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

- What is your evaluation of the proposal?
- Is the problem being addressed significant enough to warrant a change to Swift?
- Does this proposal fit well with the feel and direction of Swift?
- If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
- How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Thank you for helping improve the Swift programming language and ecosystem.

Tom Doron  
Review Manager

---

<div class="post-metadata">

**Author:** ![lukasa](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lukasa/32/1232_2.png) [@lukasa](https://forums.swift.org/u/lukasa)\
**Post date:** [November 30, 2021, 12:04pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/2 "2021-11-30T12:04:27Z")

</div>

> Future versions of SwiftPM will almost certainly add to this set of possible intents, using availability annotations gated on the tools version to conditionally make new enum cases available.

Does this work? When was that capability added? How is it spelled?

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [November 30, 2021, 3:46pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/3 "2021-11-30T15:46:31Z")

</div>

I haven't fully reviewed, and this may have come up in the pitch, but it seems like `PluginCommandIntent` should just be a struct, not an enum, as you'd never really want to switch over it.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [November 30, 2021, 6:50pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/4 "2021-11-30T18:50:38Z")

</div>

I'm not sure of when it was added, but it's used in some places in SwiftPM's PackageDescription already, for example [swift-package-manager/LanguageStandardSettings.swift at b4e94f9f78463882659917eb8565e78357f4ef17 · apple/swift-package-manager · GitHub](https://github.com/apple/swift-package-manager/blob/b4e94f9f78463882659917eb8565e78357f4ef17/Sources/PackageDescription/LanguageStandardSettings.swift#L33:)

```swift
    @available(_PackageDescription, introduced: 5.4)
    case c17

```

What cannot be done however is to change the associated parameters of a case; it only works for adding new cases.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [November 30, 2021, 6:55pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/5 "2021-11-30T18:55:15Z")

</div>

> [@Jon\_Shier](#):
>
> I haven't fully reviewed, and this may have come up in the pitch, but it seems like `PluginCommandIntent` should just be a struct, not an enum, as you'd never really want to switch over it.

This would be a struct with static factory functions? Even in that case I think that the return value of those functions would be an enum, it's conceptually a choice among possibilities.

Hisotrically `PackageDescription` has used a a lot of static factory functions because it wasn't yet possible to annotate enum cases with availability annotations to individual enum cases. Even with that ability, I can see the argument for doing so here, since it allows for default parameter values and the possibility of adding new parameters in future SwiftPM versions.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [November 30, 2021, 7:29pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/6 "2021-11-30T19:29:41Z")

</div>

Yes, structs are generally more flexible, you just usually lose exhaustiveness checking when switching over a value. If that's not how this return value is typically used, a struct would usually be preferred, and then have the two common cases provided as static values or functions.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 7, 2021, 11:08pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/7 "2021-12-07T23:08:28Z")

</div>

> [@Jon\_Shier](#):
>
> Yes, structs are generally more flexible, you just usually lose exhaustiveness checking when switching over a value. If that's not how this return value is typically used, a struct would usually be preferred, and then have the two common cases provided as static values or functions.

That makes sense, and is consistent with existing PackageDescription types such as `SystemPackageProvider` and `LibraryType`. Those are actually implemented as enums, since there can be only one choice among a set, but they use static functions for creation, as you suggest.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 7, 2021, 11:14pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/8 "2021-12-07T23:14:50Z")

</div>

One additional thing that has come up is that for commands that produce output, it would be helpful to allow the user to provide an output path that could be passed to the plugin and which SwiftPM would add to the sandbox to allow the plugin to write to it.

In the first example (DocC), the plugin writes the output to a directory inside its assigned work directory, which is the only place in which it's allowed to modify the file system unless it asks for write access to the package directory. The example has the plugin printing out the path of the directory so the user can access the files.

But this is inconvenient, so the suggestion is to allow an optional `--output` option the plugin invocation, e.g. `swift package generate-docs --output ./my/output/path` and to make the main entry point be `performCommand(context:, targets:, arguments:, output:)` with the last parameter being a `Path?` depending on whether the user provided a path. The output could be either a file or a directory.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [December 7, 2021, 11:19pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/9 "2021-12-07T23:19:21Z")

</div>

I wanted to try the implementation before leaving a review, but it was crashing the 5.5.1 release compiler ([[SR-15536] Compiler crash while trying to build SwiftPM · Issue #57839 · apple/swift · GitHub](https://bugs.swift.org/browse/SR-15536)). Do you know if that's been fixed?

Update: it has not.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 8, 2021, 12:05am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/10 "2021-12-08T00:05:54Z")

</div>

> [@Karl](#):
>
> I wanted to try the implementation before leaving a review, but it was crashing the 5.5.1 release compiler ([[SR-15536] Compiler crash while trying to build SwiftPM - Swift ](https://bugs.swift.org/browse/SR-15536)). Do you know if that's been fixed?

Thanks for trying it out — I wasn't aware of that crash. At this point the bulk of the implementation has been merged behind a feature flag environment variable, so in a nightly toolchain setting `SWIFTPM_ENABLE_COMMAND_PLUGINS=1` should let you try this out. This requires a package tools version of 999.0, since it's an experimental feature.

All the proposed APIs are in place, but the proposed commands to list plugins and to control permissions are not yet in place. The repository [GitHub - abertelrud/swiftpm-command-plugin-examples: Some examples of SwiftPM command plugins to go along with SE-0332](https://github.com/abertelrud/swiftpm-command-plugin-examples.git) contains the examples in this proposal.

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [December 10, 2021, 11:02pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/11 "2021-12-10T23:02:51Z")

</div>

> [@abertelrud](#):
>
> But this is inconvenient, so the suggestion is to allow an optional `--output` option the plugin invocation, e.g. `swift package generate-docs --output ./my/output/path` and to make the main entry point be `performCommand(context:, targets:, arguments:, output:)` with the last parameter being a `Path?` depending on whether the user provided a path. The output could be either a file or a directory.

Would it make sense to allow plugins to define their own arguments? `output` might make sense for a document generation, but what about specifying flags that get passed to a `sass` command for example.

Overall the proposal is a +1 from me. Specifying plugins in the package description makes sense and will make it easy to integrate in the upcoming VSCode extension.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 11, 2021, 12:15am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/12 "2021-12-11T00:15:33Z")

</div>

> [@0xTim](#):
>
> Would it make sense to allow plugins to define their own arguments? `output` might make sense for a document generation, but what about specifying flags that get passed to a `sass` command for example.

Definitely, and I'm thinking that some way similar to how SwiftArgumentParser allows a command to define arguments would make sense for plugins as well. SwiftPM or the IDE could then query the plugin up-front (in a manner conceptually similar to how SwiftArgumentParser supports emitting shell completions) and then use that information to allow parameters to be specified even in an IDE. I think that should be part of a separate PR, however, to keep this one bounded.

I realize that having a more generalized approach would make a specialized `output` argument moot (though there are some nuances, such as also having to add this path to the sandbox so that the plugin can write it it), but what I'm suggesting is to add this now and then deal with the more general problem in a follow-on PR.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 11, 2021, 12:21am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/13 "2021-12-11T00:21:24Z")

</div>

Another suggestion that has come up in separate conversation is that it would be useful for the plugin to report status. This came up during the pitch as well, but seemed like something to leave for a future proposal. This is yet another area where there is a tradeoff between doing something simple now (e.g. just having a way for the plugin to periodically report percent complete and a message) vs waiting until a much more complete future proposal (e.g. providing nested status with possible attachment files, etc). Are there any strong opinions between providing simple support now that can be replaced later vs waiting to do anything until there is a more complete future proposal?

I'm thinking about something along the lines of:

```swift
extension PackageManager {
    public func reportProgress(_ message: String, completeness: Double?)
}

```

where completeness is between 0.0 and 1.0 and optional (with indeterminate progress if not specified).

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [December 11, 2021, 12:30am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/14 "2021-12-11T00:30:18Z")

</div>

> [@abertelrud](#):
>
> Are there any strong opinions between providing simple support now that can be replaced later vs waiting to do anything until there is a more complete future proposal?
> 
> I'm thinking about something along the lines of:
> 
> ```swift
> extension PackageManager {
> public func reportProgress(_ message: String, completeness: Double?)
> }
> 
> ```
> 
> where completeness is between 0.0 and 1.0 and optional (with indeterminate progress if not specified).

This seems fine - I worked on a `Progress` type design for Swift Concurrency a while back but we didn't get to pitch it yet, I think that type would work well with what is proposed here. That call would of course need to be thread-safe but I'm assuming that's a given 🙂

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 11, 2021, 1:59am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/15 "2021-12-11T01:59:33Z")

</div>

> [@ktoso](#):
>
> This seems fine - I worked on a `Progress` type design for Swift Concurrency a while back but we didn't get to pitch it yet, I think that type would work well with what is proposed here. That call would of course need to be thread-safe but I'm assuming that's a given 🙂

Yes, it would need to be thread-safe — that's a good point. There are other nuances, such as whether or not it would allow the progress to go backwards.

I guess the question is whether it's useful enough in that simplistic form to be included in this proposal as an amendment, or whether it would be better to wait and include it in a future proposal.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [December 11, 2021, 2:00am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/16 "2021-12-11T02:00:48Z")

</div>

I’d be okey with this being the simplest thing possible — we’ll handle properly reporting only “forward” progress in the APIs that call this.

In a way, this is the “ui element” of somehow drawing a progress bar in the cli, and it’s fine to force people to use it correctly.

The Progress type we prototyped had facilities to handle this all :) (as does todays NSProgress)

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 11, 2021, 4:56am UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/17 "2021-12-11T04:56:31Z")

</div>

> [@ktoso](#):
>
> In a way, this is the “ui element” of somehow drawing a progress bar in the cli, and it’s fine to force people to use it correctly.

Right, this would map fairly naturally to both the CLI and IDEs, I think.

> [@ktoso](#):
>
> The Progress type we prototyped had facilities to handle this all :) (as does todays NSProgress)

I forgot that NSProgress is actually available in open source Foundation. That would be an alternative, except for this note:  
`In swift-corelibs-foundation, Key Value Observing is not yet available`  
which makes it tricky to know when to send progress updates to the plugin host. It seems unfortunate to do something different from what's in Foundation, but it's not clear to me how a simple API involving Progress would look in the absence of KVO to notify the plugin host of when it changes.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [December 11, 2021, 1:22pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/18 "2021-12-11T13:22:45Z")

</div>

No, no - we should not just throw in NSProgress here into SwiftPM because we can do the right thing with a progress api that will actually work with swift concurrency (NSProgress is very painful to use there, but possible). We’ll get to it soon enough and hooking it all up does not need much APIs — the API you proposed to add will be ideal to easily integrate either progress api and I think that’s good enough 🙂

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 13, 2021, 5:13pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/19 "2021-12-13T17:13:01Z")

</div>

> [@ktoso](#):
>
> No, no - we should not just throw in NSProgress here into SwiftPM because we can do the right thing with a progress api that will actually work with swift concurrency (NSProgress is very painful to use there, but possible). We’ll get to it soon enough and hooking it all up does not need much APIs — the API you proposed to add will be ideal to easily integrate either progress api and I think that’s good enough 🙂

Makes sense to me. To be clear, some of the APIs assume that the plugin will use Foundation for things like launching subprocesses, but it's a good point that using Foundation types in the API taking things to another level.

Since diagnostics currently expressed through a `Diagnostics` type that's available to all kinds of plugins, perhaps `Progress` should be the same, e.g.

```swift
struct Progress {
    public static func reportProgress(_ message: String, completeness: Double?)
}

```

We could also keep that for a later evolution proposal once your progress type is available. That might be the best since this proposal already has a lot in it.

---

<div class="post-metadata">

**Author:** ![abertelrud](https://avatars.discourse-cdn.com/v4/letter/a/85e7bf/32.png) [@abertelrud](https://forums.swift.org/u/abertelrud)\
**Post date:** [December 13, 2021, 8:32pm UTC](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769/20 "2021-12-13T20:32:21Z")

</div>

As the review period is winding down and the feedback seems generally positive, I have started a PR to adjust the proposal based on the review feedback so far: [SE-0332 proposal adjustments based on review feedback by abertelrud · Pull Request #1496 · apple/swift-evolution · GitHub](https://github.com/apple/swift-evolution/pull/1496).

[Next page](https://forums.swift.org/t/se-0332-package-manager-command-plugins/53769.md?page=2)
