My SwiftPM wishlist (aka proposal proposals)

:wave:, hope everyone is staying safe!

I don't get much time to contribute to SwiftPM directly anymore, I still often get questions about SwiftPM enhancements or future directions.

While chatting about one such example on Twitter, one thing that occurred to me is that it might not be very clear to the community what are concrete examples of things kinds of enhancement proposals that are good candidates.

Also, while I barely contribute to SwiftPM anymore, we make very heavy use of SwiftPM in the work our team currently does, so I have a lot of opinions from managing large & complex graphs of SwiftPM projects.

With that in mind, here is my own personal "wishlist" of SwiftPM enhancements that are also unblocked from my perspective, in the sense that if someone wanted to show up with a proposal and an implementation, the project would be eager to work towards accepting them. This is just my personal list, as with an OSS project opinions may vary. :slight_smile:

  1. "Package Features": It would be very useful have something akin to Cargo's features mechanism in SwiftPM, so that packages could expose "knobs & switches" by which their clients could enable or control how the package was built. SwiftPM currently allows environment variables to be used for this purpose in the manifest, but it is a hack we really only allow because we don't have a proper feature for it. This is also somewhat related to more flexible build configurations or Bazel's configurable build attributes, although I see this as potentially orthogonal features.

  2. "Distributable Artifacts": SwiftPM should really have some kind of feature akin to "xcodebuild install", not necessarily one that does the install, but one that does create a distributable artifact (tarball, lets say) of the built package. Right now, I often do this by just embedding a Utilities/mkdist script in the project that reaches into .build, creates the tarball, and also knows how to do various linking or RPATH tricks in order to make the artifact distributable, but really, SwiftPM should know how to do this for the platforms we support. This is particularly tricky to get right when you are trying to do things like build on one Linux distro an artifact that can be run on many different distros (including ones Swift doesn't actually yet formally support). (update, see: Swift Package Installation)

  3. "Extensible Build Tools": SwiftPM should support the ability for one package to define new "build tools" (protobuf is a common example), and packages that want to use that tool can just depend on that package and then use new "build rules" to use those tools. Ankit has already talked about this here: Package Manager Extensible Build Tools

  4. "Ad Hoc Actions": In my original thinking, I hoped that Extensible Build Tools could solve most of the need for "build system extensibility". However, as our team has worked more and more building complex SwiftPM packages, I have changed my thinking that SwiftPM should have both a "developer/API" extension level, as well as a more "ad hoc" / "quick & dirty" extension mechanism for very project-specific of rapidly evolving actions that shouldn't require building what is effectively a plugin (even if extensible build tools are very ergonomic). To that end, I now that SwiftPM should support some kind of highly-sandboxed and fine grained actions, most likely implemented using something very familiar to users of typical mechanisms in other languages, i.e. shell scripts. As our team has thought more deeply about how this should be managed at the llbuild level, we have realized there is a large opportunity to do so in a very "safe" way that doesn't preclude other SwiftPM/llbuild long term goals (like high degrees of cachability, distribution, or sandboxing).

  5. "Shared Caches": Using SwiftPM on large graphs where a single developer often has many packages checked out leads to a ton of redundancy in the clones and built artifacts that SwiftPM has, making development annoying and slow. The goal for SwiftPM has always been to do something much more Nix like where SwiftPM's user experience stayed the same as it is today, but under the covers SwiftPM would intelligently cache artifacts based on strong hashes of the inputs, so that they could be reused.

  6. "Modern Sandboxed/Cached Builds": Although more related to llbuild than SwiftPM, the longterm goal of llbuild has always been to move towards a more hermetic & cacheable build model (similar to that of Bazel) for all of the work, rather than rely on legacy style incremental builds which are highly filesystem / developer machine state dependent. This isn't necessarily something that would need a SwiftPM proposal, although the sandboxing would potentially have user facing impact and may need to be staged in. David (from the llbuild team) and myself have actually started exploring this in a very limited fashion, the llbuild repository now has a new cevobuild experiment in it where David is exploring what an "llbuild v2" API would be like, one that is much more actively focused at this type of build model (while preserving the other benefits of the llbuild model, like fully dynamic work discovery).

  7. I'm less bullish on this one as a short term need, but I'd also be very interested to see more "tool" extensibility so that it is easier to plug in ad hoc CLI actions into SwiftPM. This is very useful for large projects (Vapor is probably a good example) that want to somehow piggy back on SwiftPM but then build additional domain specific commands that help with the kinds of operations users of that package want to do. This is conceptually similar to Ruby's rake and is also something I believe NPM has, although I'm not personally familiar with that ecosystem. This is also related to Bazel custom verbs.

  8. I probably forgot at least one I constantly want. :wink:

If anyone has wanted to contribute more to SwiftPM, but isn't clear on where to start with crafting a proposal, I would be very happy to spend some time collaborating or consulting on designing a proposal on any of these topics.

39 Likes

Great list!

I’d like to have a richer PackageDescription API for build settings (e.g. compiler and linker flags). Common use cases like embedding an Info.plist section in a binary can be accomplished using .unsafeFlags, but supporting them directly would be, well, safer. :slightly_smiling_face:

2 Likes

I guess I would hope those are incremental enough you would feel unblocked to just go ahead and pitch/propose them! If not though, please let me know... I would be happy to help!

You did remind me of one more that I meant to write up, but then forgot:

  1. "Extensible Product Types": Right now, SwiftPM only supports a very limited set of product types (executables, libraries, tests). In practice, the world contains many more specialized kinds of product types that would often benefit from having explicit syntax in a Package manifest, but SwiftPM cannot reasonably expect to encode all of these. You can imagine this applies to Apple product types (like a traditional framework, or app, ro other things with an Info.plist), but the universe is much much larger (think of Photoshop plugins, or domain-specific framework helper tools that must follow a particular convention, even things like a Clang AST plugin very close to home). Some of us on the SwiftPM team have often hypothesized about a potential design where packages themselves could vend new product types (similar to the concepts around extensible build tools), so that the owners of such product types could expose a custom sort of PackageDescription extension to allow clients to define their product type using whatever natural APIs made sense).
6 Likes

To that end (and apologies if this is a bit of a tangent), I'm working on a package that produces an executable. This executable runs a dynamically-generated list of tests on a file passed in as a command-line argument, and I'd like it to report results in the same format as XCTest.

Is there a way for me to call XCTest APIs from an executable target? Or alternatively, some kind of reporter API that I can call independently?

Some additional context My first thought was to pass instances of an `XCTestCase` subclass `XCTMain`, but that seems to be only in `swift-corelibs-xctest`. Adding that to my Package, I got scared off by a bunch of warnings about missing references to `swift-corelibs-foundation`.

Then I took a step back, and tried just calling run() on a single test case instance. When I did swift run, I got this error message:

dyld: Library not loaded: @rpath/XCTest.framework/Versions/A/XCTest
  Referenced from: /Users/mattt/Code/SwiftDoc/DocTest/.build/x86_64-apple-macosx/debug/swift-doctest
  Reason: image not found
1 Like

Does SPM generate a license file from the sum of all the licenses of all the packages? Cocoapods generates a markdown file containing all the licenses of all the pods that are included in the Podfile. I display this file in my app. Does SPM do this also?

Calling XCTestSuite.default.run() seems to work in a quick test, but I still had to manually hack in -rpath flags to the linker for the Frameworks and /usr/lib directories of the SDK to get XCTest.framework and libXCTestSwiftSupport.dylib to load. (Calling run() on an instance of the test case itself didn't work; none of the tests were executed.)

I'm not sure if there are any other problems with that approach, though, since XCTest artifacts are usually built as MH_BUNDLE-type binaries and loaded through the xctest helper tool, rather than being independent executables themselves.

1 Like

SwiftPM doesn't do any aggregation of licenses and reporting of the results.

This functionality isn't build into Swift Package Manager, but you can get pretty close by running the following command from the root of a package (after running swift build):

$ find .build/checkouts -iname "license.*" -exec cat {} \;

Thanks. This feature would be on my wishlist.

I guess I could run that command from a Shell Script Build Phase in Xcode.

I expect all the license files are plain ASCII. The markdown file that Cocoapods generates is very bare bones. I think there's just a header between each license that has markdown in it. A plain text file would probably be OK.

1 Like

When I was spoofing SwiftPMs XCTest behaviour to temporarily workaround having to use CMake on Windows, I was actually working from macOS. So in order to test try it during development, I filled in the gaps with this. I haven’t actually used it since (5.1.3), so it might need minor adjustments. But it should still be pretty close.

2 Likes

Without answering any of your questions, I agree this is another great one for the list, making SwiftPM+XCTest more accessible as "infrastructure" that can be used to build other kinds of test runners. We would find this very useful in llbuild for integrating our other tests (lit, googletest) into the llbuild/Swift Package variant.

2 Likes

I agree, extra features to help manage licenses would be good.

Step 1 would be to extend the SwiftPM manifest to allow specifying information on the license, so SwiftPM could even start to build those tools. This I think is a good example of something which is just waiting for a proposal.

7 Likes

I thought I recalled reading about additional tooling for handling licenses (more in the direction of validating your dependency's license compatibility with your own) in SwiftPM's EvolutionIdeas document, but it doesn't appear to be there.

Maybe something along those lines was mentioned in @rballard's WWDC'18 talk?

Both of these mention several great future features for SwiftPM though.

1 Like

@ddunbar

Agreed. I think support for fuzz testing would also be huge.

@allevato

Yeah, that's what I'm wondering myself. Thank you for verifying the approach of @rpath hacking. I'm going to give that a try for this proof-of-concept and see if any other problems shake out as a result.

@SDGGiesbrecht

I think what I'm looking for is kinda the inverse of that — it's the reporting that I'd really like to get at, rather than the XCTest API itself. But I can imagine needing something like this at some point, too, so thank you for sharing that!

1 Like

Maybe we should just add some kind of like SwiftPM API for test reporting, kind of like “extensible test tools” that then can be slotted into?

1 Like

Fuzz testing would be awesome! I recently stumbled across GitHub - loiclec/FuzzCheck: Swift package fuzzing engine. The author has moved on to Rust, but the idea of coverage-guided generators for property-based testing is really cool.

2 Likes

Swift even already has the support: swiftc supports fuzzing. We're just missing the ability of SwiftPM to understand that fuzz targets are compiled weirdly (they build binaries, not libraries, but they don't define their own main function so they need -parse-as-library).

3 Likes

Yeah, this feels like it might be a relatively small proposal / technical RFC to figure out what is left to get this more accessible in SwiftPM.

1 Like

@Aciid and I have kicked this around before: I suggested it seemed like a new target type (fuzzTarget!) but IIRC Ankit had some other ideas.