SE-0236: Package Manager Platform Deployment Settings

I see that there would be some utility here, but I think we should actually not implement this behaviour and instead error if there are product dependencies which do not support the current platform. Skipping will lead to very confusing behaviour, because users can no longer rely on the fact that if they specify a product dependency that this dependency will actually be available to them in their source code. I think we should keep this proposal focused and solve platform-specific dependencies at a later time.

That sounds good to me.

1 Like
  • the distinction between .supported and .untested seems interesting, but there is no way for the author to actually say "supported" because platforms can be added at a later time. So really .untested is the only sensible thing to ever specify

You can drop .supported as you like, my suggestion was not completely validated.

personally, I think that having an array already implies that everything that is not in the array is unsupported

I think an array is not the correct way to represent “platforms”, we want an option set, or more ideally using a proper language feature like a switch statement that returns a version. However you guys want to limit the language features that can be used in the Manifest, so we must use an array.

Maybe I’m making fuss, arrays are fine provided you don’t need the everything-else-is-default part. I agree else is unnecessary for the nothing-else case.

So with both of these, only .else(untested) is really necessary and does seem better than .all . I prefer the more explicit .useDefaultForUnspecifiedPlatforms that @Aciid proposed, though.

I agree .useDefaultForUnspecifiedPlatforms is super precise, but it is also ugly. Considering that it will be very common for people to use it, I think some more thought should be put into the name.

.else may not be the best either, but at least it is attractive. It is also very clear when reading the list.

At the end of the day this is bike shedding, but it will strike me a little hard if in the future every time I am writing or reading a manifest there is an unwieldy .useDefaultForUnspecifiedPlatforms in there.

Sorta: I'd rather have .all.

There’s always: .elseSupported of course.

1 Like

I agree that .useDefaultForUnspecifiedPlatforms is a bit long and not very aesthetically pleasing. Personally, I don't find .else* any better either but I can't really explain why. I think .all is still my #1 preference. Considering that it'll show up often and something short is favorable, it might be better to go with .all even if it is not fully clear what it means for beginners. One can sort of guess what it means and then confirm by looking at the documentation.

I agree that .useDefaultForUnspecifiedPlatforms is ugly, but I consider that a benefit. It should be the exception rather than the rule to use it, because my assumption would be that most packages are either:

  • using only the Swift stdlib, so they presumably would work anywhere and would not declare platforms at all.
  • need specific platform API, in which case they need to declare explicit platforms and it becomes quite likely they wouldn't work on untested platforms, so they should not allow unspecified ones

Am I missing something here and should we expect that use of .useDefaultForUnspecifiedPlatforms would be common?

One reason could be because some APIs (like JSON formatting) in Foundation require newer deployment target where as they’re available unconditionally in corelibs-foundation.

As I've mentioned before, this is generally not a good idea because it makes it much harder to bring up a new platform…or even an existing platform (using an iOS package on tvOS). Max's idea of warning about it is as far as I'd want to go.

1 Like

In earlier internal discussions, we were considering an explicit escape hatch for bring up (e.g. a CLI option) and I thought it was still in the actual proposal, but I was mistaken. Taking that into account, I agree that this isn't a good idea and packages should generally allow unspecified platforms.

Yet another alternative is controlling this using modifiers on the array instead of in the array: .only([..]) and .customize([..]). So this would look something like:

platforms: .customize([.macOS(.v10_13), iOS(v12)])

platforms: .only([.linux(), .iOS(v12])
1 Like

I think I am coming around to the explicit specification via either .else or your suggestion. Considering Jordan's point, we actually do want to promote that people support an unbounded set of platforms as the default case and a bounded set should be the exception. With that in mind, the recommendation should not have a longer spelling.

Of the two alternatives, I think the modifiers suggestion has the better behaviour when authoring the manifest, because we get better autocomplete. With .else we need a lot of runtime checking: enforce that it is always specified, that there is only one and that it is the last element of the platforms array. Autocomplete would allow it anywhere and also multiple times. This seems like a strong point for the modifiers.

I do think .else reads slightly better and I am not entirely happy with .customize as a spelling.

What about removing (at least, for now) the option to restrict platforms? Currently the real need is to set a minimum target for Apple platforms so we could start simply with:

platforms: .supports([.macOS(.v10_13), .iOS(v12), .linux()])

Defined platforms are explicitly supported for at least the specified version, other platforms are implicitly "untested" but valid. In the future, in the need arises, we could add the option to restrict platforms.

Also, I don't find in the proposal the option to define an Apple platform target without a specified version. For parity with the .linux() case I think we should support setting .macOS() or .iOS() (in which case it would be set to Swift PM default).

I disagree that there's no need for defining platforms yet. If your package imports AppKit, you know it will only work on macOS and there should be a way to express that in your manifest.

Go back to 2015:

If your package imports UIKit, you know it will only work on iOS and there should be a way to express that in your manifest.

That's not longer true, there is tvOS, (next year) macOS and maybe some future platform. The package that should be vetting the dependencies is the one consuming them.

2 Likes

In order to be able to vet, the information has to exist first, though. If this isn't declared in the manifest, we have to rely on imprecise sources such as READMEs or compiler diagnostics and that is a bad user experience.

That’s exactly for what the .supported option is for, to mark the explicitly supported platforms. But users should be able to try packages in all platforms.

And I totally disagree that there is currently a need to limit the platforms a package could be build on. Who is complaining that they could not build a custom UIViewController package or a Core Data wrapper on Linux?

Stepping back, it seems like the primary goals are:

  1. Allow developers to specific a minimum platform version
  2. Promote packages to be available for all platforms
  3. Allow for new platforms to be added by default to all packages
  4. Allow developers to specify that only certain platforms are supported

To me, this implies that platforms should only provide a mechanism for changing the default value for the minimum version.

So:

    platforms: [
       .macOS(.v10_13), .iOS(.v12)
    ]

Would mean that all platforms are supported, but macOS and iOS have version v10.13 and v12 as the deployment targets set.

This solves #1, #2, and #3 above.

Allowing developers to restrict platforms is a different goal that updating the minimum supported version. As such, I think it would be best to be a separate option (and frankly, outside of this particular proposal):

    platforms: [
       .macOS(.v10_13), .iOS(.v12)
    ],
    restrictedToListPlatformsOnly: true /* default is false */

If restrictedToListPlatformsOnly is set to true, then developers can also do the AppKit scenario that Boris mentioned.

Now, if you want to support everything but iOS, let's say, then we can model it this way instead:

    platforms: [
       .macOS(.v10_13)
    ],
    unsupportedPlatforms: [ .iOS ]

Or even this:

    platforms: [
       .macOS(.v10_13), .iOS(.unsupported)
    ],

This still allows new platforms to be brought up transparently, still allows for developers to specify minimum deployment versions. This removes any of the magic .all and .else items, and it provides a simpler model than the modifier approach of .only and .customize.

1 Like

I disagree with this for two reasons:

  • it goes against many other features in both Swift and SwiftPM where we allow library authors to limit what clients can do. For example, the products API limits which targets of a package are exposed to clients, it is explicitly not possible for client to depend on any target to try it out. It could be a valid stance for a package manager to allow clients to override everything, but we clearly haven't taken that approach, so I don't see why this is would be different.
  • even if we do allow clients to use packages on any platform, this will only work sometimes since clients cannot override the deployment target of their dependencies. For example, if a hypothetical future version 12 of watchOS supports UIKit, clients will only be able to try that out once version 12 is the default, i.e. the oldest available version. This will take years, so obviously they will have to fork the package anyway, in order to specify the deployment target.

I think the only interesting case here is completely new platforms. Deployment target won't yet be relevant for them and allowing experimentation is something that really makes sense in that case.

The examples you are listing might not be as interesting because they will cause build errors, but as adoption for other use cases than server side broadens, there will be subtle problems as well. Runtime errors and crashes could be occurring on unsupported platforms.

I like this idea, it allows clearly declaring the intent and it promotes "support everything" to be the default which I think we want to be striving for.

5 Likes

I like this too. It also aligns well with API availability macros behavior, where you can declare a platform as explicitly unsupported (i.e. tvOS) if its availability would otherwise be inferred to be available by default (i.e. from the presence of an iOS availability declaration).

"Supported unless told otherwise."