NOTE: This initial proposal has been supplanted by an updated plan.
Last week in the @cxx-interop-workgroup sync-up we discussed the plan for SwiftPM support in the Swift 5.9 release. Since then I have worked on documenting and refining the plan, and filing issues for Swift and SwiftPM. I would like to present the plan to you in this post.
Overview
Swift package manager recently gained a new Swift build setting, that allows users to enable C++ interoperability for a Swift target (PR1, PR2).
This now allows us to enable C++ interoperability for a Swift target. By default, C++ interoperability is "viral", i.e. the setting propagates to dependencies of a target if such target imports C++ code or uses C++ types in its public APIs or function bodies that can be inlined.
To prevent the "virality" of this build setting, we are going to recommend users to use @_implementationOnly import
when importing C++ code into Swift, and to avoid exposing C++ types in the public APIs of a Swift target that uses C++ interoperability.
This will then allow users to enable C++ interoperability in a single target, which means that they can distribute a package that exports such target without users having to opt-in into C++ interoperability. Here's a SwiftPM target diagram illustrating this scenario:
Notice that only the UsesCxxLib
target needs to enable C++ interoperability, but not the other targets.
Exposing Swift APIs to C++ is something that we won't support in Swift 5.9 in SwiftPM, as SwiftPM right now doesn't add search paths for the generated header to C++ targets that depend on a Swift target. We think that's okay, and we can have a better build story in a future release of Swift, that will allow us to expose Swift APIs to C++ from multiple SwiftPM targets.
Proposed guidance
Here's the proposed guidance we would like to document on the Swift website:
When a package intends to vend Swift APIs from a Swift target that uses C++ interoperability, the recommended approach is to:
- Enable C++ interoperability just for that target.
- Import C++ targets using
@_implementationOnly import
.- Do not use any C++ types in the public interface of the Swift target.
- Build the C++ dependencies within one package.
These guidelines ensure that the clients that consume this package and this target will not be required to enable C++ interoperability in their Swift targets, and they can still import the Swift module directly.
It’s okay to enable C++ interoperability for multiple targets within the same package, provided the targets exported by the package still follow the recommended practices above. You don’t need to bump the package’s major version if you follow the recommended approach.
Creating a package that does vend C++ APIs (it either exposes a C++ target, or a Swift target that uses C++ types in its public API and/or doesn’t use
@_implementationOnly
to import C++ targets) will force clients to enable C++ interoperability in their Swift targets. Therefore this approach is not recommended by default. However, if you’d like to make such a package, please follow these guidelines:
- If it’s an existing package, bump up the major version. Forcing clients to enable C++ interoperability is a breaking change!
- Don’t distribute such package to a wide audience. Ideally such package would be only consumed directly by another package controlled by the same package authors that doesn’t force its clients to enable C++ interoperability.
- Explicitly inform clients that they have to enable C++ interoperability when depending on targets from such package.
Note: Using Swift APIs in C++ is not yet something that’s supported in Swift package manager. Right now you can only import C++ APIs into Swift when using SwiftPM.
Getting there
Unfortunately there's still some bug fixes that need to be done in order to make the proposed plan work in Swift 5.9. It's tracked in our C++ interoperability 5.9 tracking issue, but here's the summary:
Compiler fixes required:
SwiftPM fixes required:
- [C++ interop] A Swift executable target that depends on a Swift library target that enables C++ interop should tell Swift to link using clang++, even when C++ interop is not enabled in the executable target swift-package-manager#6564
- [C++ interop] c++ language standard is not passed to Swift's clang importer when C++ interop is enabled swift-package-manager#6565
Summary
We plan to provide support for using C++ APIs in Swift in SwiftPM projects in Swift 5.9, in a way that does not require package vendors to force clients to enable C++ interoperability. The C++ interoperability workgroup will provide an example of a SwiftPM package that uses C++ interoperability on Github as well, and will document the guidance to SwiftPM users and package authors on swift.org .
Please let us know what you think of the proposed plan.