Today, the new Swift Build open source project was announced with the goal of delivering a new set of powerful build technologies as part of the Swift toolchain. One opportunity presented by this new project is unifying SwiftPM’s build engine implementation across all the platforms Swift supports to deliver a more consistent cross-platform experience and enable powerful new features for package developers. I wanted to write up some initial thoughts on what this evolution of SwiftPM could look like to share with folks. I’m looking forward to hearing your feedback! You can feel free to reply here, and there’s also a new Swift Build forums category for other questions and discussions regarding the new project.
-- Owen
One of SwiftPM’s primary functions is to build packages and produce binaries and other products requested by the user. The component responsible for performing these builds is the SwiftPM build system. The build system plays an important role in providing a great developer experience for Swift users on all of its supported platforms. Its capabilities enable higher level features which determine how users architect and configure their projects, and its performance and reliability have a direct impact on developer productivity.
This document proposes a new technical foundation for the SwiftPM build system based on Swift Build. In the short term, this would be designed as a transparent migration for SwiftPM users that improves the cross-platform feature parity and behavioral consistency of SwiftPM. In the long run, this work can provide the infrastructure necessary to lift longstanding limitations of the current SwiftPM build system.
Goals
There are a number of technical goals I think are worth considering when evolving the technical underpinnings of the SwiftPM build engine. At a very high level, SwiftPM’s implementation should be well positioned to support desired end-user features, adopt new compiler advances, and deliver new developer productivity improvements. Specific proposed (but not exhaustive) goals include:
Providing a great build experience on (and targeting) all of Swift’s supported platforms
It’s worth explicitly stating that as it evolves, the SwiftPM build system must continue supporting all of the platforms it does today, while also laying the foundation for future expansion. SwiftPM should have the same high-level feature set on all platforms, and build behavior should be predictable and consistent.
Adopting the library-based SwiftDriver
The libSwiftDriver library allows higher level build systems to merge Swift’s module-level build graph with their own, higher level build graph. This allows a higher level build system to schedule tasks more efficiently by avoiding overcommit of system resources. Merging the granular tasks needed to build a single Swift module into the higher-level graph of an entire build request is also an important enabler of further build graph optimizations.
Providing a technical foundation for more expressive build configuration
Users are currently limited in the types of build configuration they can express in a Package.swift manifest. The SwiftPM build system’s implementation should become more flexible that it’s possible to design more advanced configuration options and a wider array of specialized product types (e.g. GUI applications, web services).
Supporting new build graph optimization techniques
libSwiftDriver adoption allows for more granular scheduling of individual swift-frontend jobs. This gives build systems the ability to partially interleave the compilation of dependent modules, allowing for greater parallelism. For example, the build system may prioritize the frontend invocation responsible for emitting a module’s swiftmodule file, allowing compilation of dependent modules to run in parallel with the frontend jobs responsible for producing object files. Other build graph optimizations may take advantage of unique characteristics of a target platform. For example, on Apple platforms TBD files can be used to shortcut linking in incremental builds where exported symbols don’t change.
Supporting global scheduling of explicitly built modules
Explicit compilation of Clang and Swift modules has a number of benefits. By eliminating the locking which occurs in implicit module builds, it allows for better build system scheduling decisions which reduce contention and allow for additional parallelism. Explicit module builds also improve the overall correctness of module builds by eliminating unsound sharing of content in the global module cache. The debugger can leverage modules generated during the build to accelerate expression evaluation. Errors encountered when building module dependencies are clearer and easier to understand. And, by precisely capturing the dependencies of a translation unit, explicit modules make it easier to soundly share and reuse build outputs across incremental builds.
Adopting Swift Build as SwiftPM’s new build engine would make it possible to work towards achieving the goals laid out above. In the short term, Swift Build’s extensibility makes it easier to implement and organize domain specific functionality like platform support and product types. It already has support for key compiler technologies like the library-based SwiftDriver and explicitly built modules, meaning those technical investments do not need to be replicated in the SwiftPM build system to unblock work on the end user features they enable. Finally, it contains a number of performance optimizations which build on these investments to go beyond what SwiftPM supports today, including the build graph optimizations described above.
Work Needed to Adopt Swift Build as SwiftPM’s Low Level Build System
The work needed to successfully adopt Swift Build as a replacement for SwiftPM’s current build system can be divided into two broad categories: feature parity, and platform support parity.
First, SwiftPM builds using Swift Build must have full feature parity with the current build system. Today, SwiftPM has basic support for building packages on Apple platforms using the build system bundled inside Xcode via the --build-system xcode
option. However, this option does not support the complete SwiftPM feature set, and is not currently supported on non-Apple platforms. To achieve feature parity, we’d like to more tightly integrate Swift Build with the SwiftPM codebase, and build out support for missing manifest features like package plugins and macros. Higher-level features relying on information about the build will need updates to work with Swift Build as well. For example, SourceKit-LSP’s background indexing can be taught to use Swift Build’s index preparation and indexing info API.
SwiftPM builds using Swift Build must also support all of SwiftPM’s currently supported platforms. Currently, Swift Build has mature support for building on and targeting all Apple platforms, and has preliminary support for building on and targeting Linux, Windows, and Android. Further development is required to achieve Linux/Windows parity with the current SwiftPM build system. Swift Build will also need to add support for using Swift SDKs for cross-compilation by bridging them to its existing internal SDK and toolchain abstractions.
A Note on Platforms Support
Swift Build contains support for building software using a number of Apple-specific tools and product types. Now that it’s been contributed to the Swift project, we’d like to take a more principled approach to how this platform-specific support is organized as part of our efforts to provide first class support for additional non-Apple platforms. We’ve moved support for a number of tools, like the Asset Catalog and Core Data compilers, into Swift Build’s SWBApplePlatform
plugin, and we intend to continue this process of separating support for Apple platform technologies from the core build engine implementation. Even though this platform support is moving into plugins for organizational purposes, it will remain a part of the open source Swift Build distribution, in order to ensure that open source clients like SwiftPM can continue leveraging it.
This same plugin-based approach has been used to successfully develop initial support for building projects targeting Linux and Windows using Swift Build. We’re excited to continue this work in open source to ensure the new project provides full feature parity and a great experience when building for any of the platforms in the Swift ecosystem.
Next Steps
The process of integrating Swift Build with SwiftPM and implementing the required features to allow adopting it as the SwiftPM build system is just getting started. We’re sharing our ideas early so that the Swift community can participate throughout the process and provide feedback. You can check out the initial pull request to start integrating Swift Build as an alternate build system for SwiftPM here. We’re looking forward to hearing your thoughts and discussion, and excited to work with you all to improve the experience of building Swift code!