I have packages that I would like to use some features included in Swift 5.6, but while retaining backwards compatibility.
To do this I declare swift-tools-version:5.2 in the main Package.swift file and I've then added a Package@swift-5.6.swift file.
When performing package actions (such as update or resolve) with Swift 5.6 it replaces the Package.resolved file with a "version 2" file that is not compatible with Swift versions prior to 5.6.
Is it possible to have Swift 5.6 not update the version of the Package.resolved file? I have to ensure not to commit the changes that are automatically made or remember to use an older version of Swift whenever dealing with this package.
That's a bad idea, at least if you work with anyone else. Resolved files keep groups in sync, so it's important not to ignore it.
@josephduffy Can you explain why you need to be careful here? Consumers of the package won't care about your resolved file, so do you have to support many Swift versions for local development of the package?
@Jon_Shier Thomas has explained it perfectly. I run tests via GitHub actions against old Xcode versions and older Swift versions on Linux. This would require deleting the Package.resolved file before testing or ensuring the version 2 migration is never committed.
I mean, you can just make sure not to commit the v2 resolved file from Swift 5.6. 5.6 can use the v1 manifest just fine, just don't use it to update dependencies. It is unfortunate that SPM shipped a breaking manifest change as part of a minor release. @NeoNacho Is there any backward compatibility affordance here? Can we version the resolved files? Can we tell 5.6 to use the older manifest format?
As I recall, it's also used to choose the major Swift version when consuming packages. While we aren't yet at the Swift 6 stage, the Swift 4 to 5 transition had bugs when packages with v4 package files were used in a v5 package. Also the annoyance of Xcode telling you to undergo the Swift 5 update when one of your packages was still on v4. So I'm pretty sure there are other impacts to not using updated versions of the package.
Forcing the v1 format when an older package file is found sounds ideal. Another use case for this is adding or updating a dependency, which ideally would be done using the current version of Swift (soon to be 5.6). As it stands we'll need to switch to an older version when making these changes.
If an existing .resolved file is still logically valid, SwiftPM will not bother to overwrite it. So no matter what, it will not “upgrade” until you actually change some dependency constraints.
When it does have to write out a new file, it will try to use the version corresponding to the tools version and any versioned manifests present. That way it still writes the pins in a format that any older toolchains supported by the package can still read. (Without this behaviour, the older toolchain would then fail to read the file and resolve anew instead, resulting in it ping‐ponging back and forth as you switched between toolchains.)
Once your package actually updates the tools version in the manifest, then SwifPM will start writing the pins file in the new format. At this point you may be using features the old format cannot express. And since old toolchains cannot load the manifest anymore anyway, there are no compatibility concerns left.
It will happen automatically whenever it makes sense. There is no benefit to be had from trying to force it to update.
This change breaks CI testing that relies on older Xcode/iOS simulators. Is there any plan to address this with a flag to opt-in for v1 manifest format instead of v2? All I ever asked for is to have a git endpoint and commit hash. How hard can that be without breaking all CI/dev environments around the globe with no added benefit? Sigh...
Here is my little script for CI that converts manifest file from v2 to v1 while preserving revisions which is enough to pull the specific commit from remote:
That sounds very fragile. If you want to ensure specific versions of dependencies, you should do that in the package definition. Also consider cloning the dependencies into your own git server and only resolving from there. That will protect you against people doing stupid things like moving tags or deleting the repo altogether à la npm left pad fiasco.
In practice it's not fragile at all. The goal isn't to lock to a single version but to keep everyone in sync with the version used by the project. This allows the dependencies to be updated regularly while keeping team members in sync with the latest building code. And in 10 years I think I've seen a grand total of one iOS dependency lost because it was deleted from git. There are a variety of strategies to deal with that but it's a non issue in the Swift ecosystem right now.