What's the recommended way to depend on apple packages with non-semantic versioning

SwiftPM tells me that a package with semantic versioning cannot depend on a package with non-semantic versioning (this is what I understand from the error message).

I get it that some Apple packages depend on the Swift toolchain and follow the "release" branch versioning schema, for example https://github.com/apple/swift-syntax.

I don't understand (or remember) why https://github.com/apple/swift-markdown follows the branch versioning as well, since it has nothing to do with the toolchain, but let's assume there is a valid reason for it.

My question is — I want to version my own package much like any other open source package out there with semantic versioning. My package depends on https://github.com/apple/swift-markdown an so it seems to be that, with SwiftPM, I can't use semantic versioning at all?

Is my understanding correct? What is the recommended way to version packages depending on some of the apple packages — is the only working solution to avoid semantic versioning altogether?

I have an issue for this, in case you'd like to chime in directly on the repo How to resolve the semantic versioning vs apple-style release branches problem? · Issue #15 · MarkCodable/MarkCodable · GitHub

Thank you, Marin


Without having tried it, I seem to recall that this error can be worked around by pinning your dependencies to specific commits by using .package(url:revision:). It's more of a pain to update those since you'll have to do it manually, but it means the dependency won't change out from under you and break your users either.

I have tried that but SwiftPM doesn't accept a revision as a stable version.

swift-markdown is used by the DocC stack, which is part of the toolchain, so swift-markdown by necessity must evolve with the toolchain.

your understanding and conclusion is correct. a package that depends on a package that evolves with the toolchain must also evolve with the toolchain. evolving with the toolchain is contagious.

for example, swift-json does not depend on any packages that evolve with the toolchain, so it evolves independently, and can use semantic versioning. swift-package-factory depends on swift-syntax, so SPF evolves with the toolchain.

the recommended way to distribute a package that evolves with the toolchain is to use toolchain tags like you described: this is exactly what SPF does. that the SPM cannot understand and resolve toolchain tags is, in my opinion a problem with SPM, not a problem with swift-markdown.

I fail to understand this logic - DocC depends on swift-markdown and DocC depends on the toolchain, therefore swift-markdown depends on the toolchain?

Looking at the current dependencies of swift-markdown — I don't see any of them being related in any way to the toolchain: swift-markdown/Package.swift at main · apple/swift-markdown · GitHub

DocC does not depend on the toolchain, DocC is part of the toolchain. (whether or not DocC should be part of the toolchain is a valid question to ask but out of scope for this thread.)

swift-markdown doesn’t depend on the toolchain, but the toolchain depends on swift-markdown, and the toolchain developers have political control over swift-markdown, so swift-markdown releases for the toolchain, and the easiest way to release for the toolchain is to tag by nightly snapshot.

if swift-markdown were to release with semvers instead of toolchain tags, that would slow down toolchain development.

I'm one of the original authors (both swift-docc and swift-markdown), I understand the relationship between these packages.

Appreciated your input and I wonder if there'll still be someone to chime in if there is a recommend way to handle dependencies for 3rd party packages beyond avoiding sem versioning.

1 Like

Sadly, there really isn't much you can do. As soon as you start depending on a package that doesn't follow SemVer you shouldn't adopt SemVer yourself, because as @taylorswift said "it is contagious". If you would adopt SemVer nevertheless you are somewhat lying because you are forcing your adopters to lock your version since other packages might depended on a different swift-markdown version.

One way out of this is vendoring the code of the package that doesn't support SemVer yourself. You can do this by copying the code over. You might also be able to do this with git submodules and a local package dependency; however, I haven't tried this out and there might be other downsides with that approach.

1 Like

this will work for swift-markdown but it will not work for swift-syntax because it depends on lib_InternalSwiftSyntaxParser.dylib/.so. so you also have to distribute the runtime binaries.

Fork and tag 0‐prefixed versions of your own where necessary. Your package graph becomes irreconcilable with the toolchain packages, but they are already irreconcilable with the rest of the world anyway.

I have a dozen or so already set up here, (but not swift-markdown yet). Others, like SwiftSyntax, vend 0‐prefixed versions directly.

Yes, I was specifically talking about swift-markdown. SwiftSyntax is a different beast :slight_smile:

1 Like

Digging more on GitHub it seems to me that the versioning strategy between semantic and branch versioning is arbitrary; probably for each respective team to decide.

For example https://github.com/apple/swift-package-manager is semantic versioned — currently at 0.6.0 and yet it is part of the toolchain as its README it states:

The package manager is available as part the Swift toolchains available on Swift.org) including snapshots for the latest versions built from main branch. For installation instructions for downloaded snapshots, please see the Getting Started section of Swift.org.

It seems that forking and tagging swift-markdown might be the only option for my case.

the 0.6.0 release was from march 2020 :slight_smile: