I have some specific replies to folks in the thread, but first some high level notes.
Generally I think this is really good! It's an important forward step and this definitely provides a bunch of useful tools. Unfortunately I also think it's probably not quite far enough along to tackle anything more complex than the simplest of use-cases, and it also has some areas where things are highly underspecified.
Here are some of my notes.
First, a couple of nits. Should the ExtensionCapability statics be functions, or lets? That is, should we be writing capability: .buildTool() or capability: .buildTool? The latter seems moderately nicer to me. Secondly, the argument label using: is extremely general. Do we want to be consuming it here? Or should we provide a more specific label, e.g. usingExtensions.
More substantively, how do we intend for users to develop against the new PackageExtension API? Right now when writing a Package.swift it can be quite painful to get autocomplete on many platforms, as the library we're writing against doesn't necessarily existing in an easy-to-consume way. Xcode deals with this today: do we intend for Xcode to do the same for extension targets? What about the Swift LSP implementation?
I also have concerns about tool distribution. While it's somewhat elegant to force tools to be distributed within the SwiftPM package graph, it does make some build tools exceedingly painful to use. In particular, tools with complex dependencies will either need to be built as giant static executables or have unstated dependencies on the OS. As an example, consider a tool that used perl to generate source files. How would we distribute such a dependency today?
I think the above complaint is part of a broader deficiency in SwiftPM which is that it still does not handle the interface between SwiftPM-land and OS dependencies very well. System library targets already don't work very well, and here we will be adding another interface that is likely to make things difficult. I would like us to consider whether we need a way to specify what the dependencies are for various projects from system package managers more formally.
Finally, and this is the biggest one for me, there is this line in the proposal:
The proposal is very light on details here, and I think we need to expand on it. For example, currently binary targets are supported only on Apple platforms, and only as .zips containing XCFrameworks (yes, I am aware there is an exception for those distributed as paths instead of URLs, it's immaterial to this discussion).
Does this proposal plan to lift the Apple-platform-only limitation? If so, how? How does it plan to tackle the need to express what the other platforms it supports are? How will it interact with architecture differences on those platforms (e.g. 32-bit vs 64-bit Windows, ARM vs 86)? What about dependencies on the system: do we have any guidance for how to build tools so that they are most likely to work on multiple Linux distributions?
The proposal as currently written alludes to some of this complexity with:
This is, again, extremely light on detail. Is the plan that this support will follow in a different pitch? If so, when?
This proposal also fails to address the failure modes of this support. What happens if the binary build tool does not contain a binary for a given Swift platform? How will that manifest to the user? Should they be able to override the binary with one they know is equivalent on their platform?
If we shipped this without Linux support for binary targets, what will happen if you declare a manifest that uses package extensions on a platform that doesn't support them? Will SwiftPM fail? Or will it silently ignore the extensions?
I think we need substantially more detail on how this is going to work to understand the effectiveness of this proposal as a whole. Right now, as specified, it seems hard for Swift Protobuf to adopt it, and impossible for grpc-swift to adopt it (see below). swift-nio-ssl is a very long way from being able to adopt it due to the complexity of its build systems and the desire to avoid foisting them on all users.
This does not address @tachyonics' concern. To explain why, we need to disambiguate two different uses of "dependency" in SwiftPM.
SwiftPM uses the word "dependency" in two places. One is as a package-level modifier. Here you express a dependency on a package, e.g:
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.15.0"),
],
Another place is as a target-level modifier, where you express a dependency on a specific product vended by a package, e.g:
dependencies: [
.product(name: "NIO", package: "swift-nio"),
]
So you're right that extension targets cannot depend on libraries (either targets or products). However, there are two ways in which you're missing important details. First, they can (and indeed in many cases must) depend on executable targets or products. This means that Swift Packages that provide extensions may themselves depend on other packages.
To consider an example, consider grpc-swift. This provides a source generation tool that plugs into protoc, just as Swift Protobuf does. The package that vends that target (protoc-gen-grpc-swift) depends on SwiftNIO and other libraries for its runtime functionality.
The problem here is that SwiftPM has to produce a buildable package graph, not separate for each target. This means that package dependencies need to be sympathetic. We cannot get around this with target-based dependency resolution: if the individual packages declare incompatible dependency constraints, the entire graph is unbuildable, even if none of the specific targets we're trying to build hit the problem.
More broadly, grpc-swift demonstrates another limitation of the proposal: protoc-gen-grpc-swift has library dependencies. It does this because the interface between protoc and its plugins is itself a protobuf interface, and so it depends on protobuf for the serialisation and deserialisation. As a result, the only way to use grpc-swift as a build extension today is to ship protoc-gen-grpc-swift as a binary executable as well, in a separate target. That's not really ideal. @abertelrud how complex is lifting this constraint?