Building on recent proposals to improve the expressivity of package manifests, I'd like to share a proposal to unify SwiftPM's concept of products and targets. This is a major change to SwiftPM's build model, so I'm sharing it a little earlier than I would otherwise to collect feedback on the overall design. There is the beginning of an implementation available, but right now it's quite minimal and just enough to explore some of the design ideas in the proposal. I'm looking forward to your feedback! I'm especially interested in hearing people's opinions on the proposed method for configuring target visibility, and how packages migrate to the new model.
One of the common product use-case for XCFramework vendors is to declare dependencies between binary targets using a wrapper target while exposing product with the same binary target name. One example showed here: SwiftPM - Binary target with sub-dependencies - #7 by bielikb
After products deprecation how can this be achieved?
It would remain possible for a target to declare a dependency on binaryTargets, and then that target could declare public visibility rather than exposing it via a product.
I strongly agree that targets and products are currently very confusing, and I am glad to see the SwiftPM team is already making effort toward resolving that.
But I think the confusion might stem from a more fundamental tension: SwiftPM package manifests currently serve a dual role as build recipes and artifact BOMs. While it’s beneficial to be able to derive a BOM from a build recipe, the pitch here seems to indicate that building and packaging really are separate steps, and just because I use SwiftPM for both doesn’t mean that a single primitive is going to be sufficiently expressive, forward-compatible, or understandable.
In other words, is the right call perhaps to lean more heavily into the split between products and targets? In the same context in which we are discussing declarative manifests, it seems reasonable for such a manifest to only declare its products, especially if it is a binary-only package.
I support the unification of products and targets.
I noticed the proposal include this
In general, existing CLI options for working with products will continue to be supported to allow working with packages that have older tools versions. For example, both --target and --product will continue to be supported by swift build.
This is great that we will continue to support them. However, I have a comment unrelated to the overall design.
SE-0509 added support for generating an SBOM. Currently, build time SBOM generation emits a failure when used with the --target argument.
❯ swift build --target Test --sbom-spec cyclonedx
Building for debugging...
/Users/bkhouri/Downloads/can_be_deleted/_radar/157839834/Test/Package.swift: Test-product: ld: warning: building for macOS-12.0, but linking with dylib '@rpath/AppleConnectClient.framework/Versions/A/AppleConnectClient' which was built for newer version 13.0
[68 / 70] Test-product
Build complete! (17.43 secs.)
error: --sbom-spec or SWIFTPM_BUILD_SBOM_SPEC cannot be used with --target flag
Although this proposal mentioned CLI changes, it does mention an update to swift package generate-sbom but does not explicitly mention the case of a build-time SBOM generation.
Can the proposal be updated explicitly indicating swift build --target <name> --sbom-spec <spec> will produce a SBOM if the target has a visibility set (.package or .public), preserving a similar behaviour as if the target was defined as a product?
This is a very welcome change to the package manifest. I know that in particular the plugin tool dependency can be a source of confusion because executable targets are automatically promoted as products, and depending on the circumstances the plugin might depend on one or the other, and the plugin code needs to use the artifact's name to work correctly.
One thing that the redundancy between products and targets affords is the ability to separate between the name of the artifact (e.g. libfoo, or some-exec), which is currently set on the product, and the name of the module (e.g. CFoo, SomeExecutable) set on the target. Swift has its conventional CamelCase and C* for wrappers of C code, which makes sense for the module identity. But, then target artifacts can be in namespaces expecting ke-bab, or other case systems for the file names.
Should there be a way to specify the name for the artifact, separate from the module name for a target, public or not, in cases where it matters?
I think this is something we could consider allowing, but it would need to be conditioned on the target type because in some cases the module name is expected to match the binary or wrapper. For example, on Apple platforms, there's an assumption that if a framework named Foo.framework contains a Swift module, that module's name is Foo, and the compiler relies on this to avoid expensive directory traversals when looking for modules in framework search paths. A CLI tool has no such requirement, so there's an argument to be made the module name should be freely customizable there.
I wonder specifically about C targets with a module map where the convention is to name them CXyz, meanwhile, the library should probably be [lib]xyz.[so|dylib] because that's what the library name the C world might expect for it.
Currently SwiftPM adds the prefix/extension based on the targeted platform, so a dynamic library product Foo, for example, would produce libFoo.so when targeting Linux. Right now the proposal doesn't suggest changing that, but it's something we could consider