Destination specific products in Swift package

Currently, swift packages can expose 3 types of products to the consumer of packages:

  1. Library
  2. Executable
  3. Plugin

There is no alternative to add custom product definitions and expose custom product types. Some example for this:

  1. Developing for android platform requires producing .apk product which can be deployed on android devices.
  2. Developing for ios platform requires producing .app product.
  3. Other platforms also provide alternative packaging/deployment options, i.e .appx or .msix in Windows, snaps and flatpaks in Linux.

Currently cross-compilation destination bundles pitch outlines support for building existing products for other platforms. Allowing destination bundles to expose additional product types will make multi-platform development in Swift much easier.

This will help removing reliance on Xcode projects and other third-party setup required for doing multi-platform development, providing a standard way for multi-platform development with Swift.

Extending destination bundles destination.json schema to allow destinations to provide additional product definitions will help decouple platform-specific definitions from Swift tools version and can be updated by updating destination bundles and adding support for newer destinations.

Adding additional property swiftPackageDefinitionsPath for each runTimeTriples in destination.json will allow importing such definitions in Package.swift.

{
  "schemaVersion": "3.1",
  "runTimeTriples": [
    "<triple1>": {
      // optional:
      "swiftPackageDefinitionsPath": ["<array of paths relative to `destination.json` containing Swift modules that can be imported in `Package.swift`>"]
    },
    "<triple2>": {
      // optional:
      "swiftPackageDefinitionsPath": ["<array of paths relative to `destination.json` containing Swift modules that can be imported in `Package.swift`>"]
    }
  ]
}

To use these custom definitions in Package.swift, users can conditionally add the products based on presence of module, i.e android product can only be exposed in presence of AndroidProductTypes, iOS product can only be exposed in presence of AppleProductTypes etc:

#if canImport(AndroidProductTypes)
package.products.append(
    .androidApp(...)
)
#endif

#if canImport(AppleProductTypes)
package.products.append(
    .iOSApp(...)
)
#endif

#if canImport(WindowsProductTypes)
package.products.append(
    .windowsApp(...)
)
#endif

#if canImport(UbuntuProductTypes)
package.products.append(
    .snapApp(...)
)
#endif

Would you need additional product types for this? Storing platform-specific configuration that's external to a package in Package.swift may be not flexible enough for all of the possible use cases. Is there anything you wouldn't be able to do here with a command plugin, say an imaginary swift package build-apk for Android, or a more general plugin that handles a specific set of platforms? Then you're not constrained with package manifest evolution anymore, your plugin can read configuration from wherever it needs.

For reference, the Future Directions section of SE-0394: Package Manager Support for Custom Macros hints that something like custom target types may be on the horizon:

The macro target type is provided by a new library CompilerPluginSupport as a starting point for making package manifests themselves more extensible. Support for product and target type plugins should eventually be generalized to allow other types of externally defined specialized target types, such as, for example, a Windows application.

1 Like

Would you need additional product types for this? Storing platform-specific configuration that's external to a package in Package.swift may be not flexible enough for all of the possible use cases. Is there anything you wouldn't be able to do here with a command plugin, say an imaginary swift package build-apk for Android, or a more general plugin that handles a specific set of platforms? Then you're not constrained with package manifest evolution anymore, your plugin can read configuration from wherever it needs.

My concern is this will only handle building of product and not the running/debugging of the product. I have added more details on how this can be implemented by extending destination bundles schema, then no dependency will be present on package manifest evolution and newer version of platforms can be targeted by updating the destination bundles.

We have a couple of kinds of tools declared right now in SwiftPM codebase as an experimental implementation for the toolsets concept proposed in SE-0387, namely .debugger and .testRunner. These aren't exposed anywhere yet, but depending on how proposal review turns out it could be used for running/debugging on certain platforms.

1 Like