Hello all,
SwiftPM follows Semantic Versioning 2.0.0 for package versioning. SemVer defines that build metadata (the +... suffix) must be ignored for version precedence, but it can still be useful to distinguish different published artifacts that share the same precedence (e.g. 1.0.0+debug vs 1.0.0+release).
Today, SwiftPM cannot reliably pin a dependency to a specific tag when multiple tags differ only by build metadata. This has shown up in user reports such as:
- Resolving tag with Semver Build Metadata is failing · Issue #6675 · swiftlang/swift-package-manager · GitHub
- Fail to resolve package dependencies when semantic versioning includes prerelease/build metadata · Issue #80711 · swiftlang/swift · GitHub
Motivation / Use cases
Some valid workflows that benefit from metadata-distinct tags:
-
Debug/hardened variants of the same release, especially for prebuilt binaries (e.g. XCFrameworks).
Example: publish1.0.0+debugwith additional checks/assertions alongside1.0.0and allow consumers to pin to the intended artifact for testing. -
Downstream repackaging / mirroring with provenance (forks, vendor patches, internal compliance builds).
Example: a downstream publisher republishes upstream1.0.0but needs to signal extra information without changing SemVer precedence—such as a vendor patch, compliance status, or internal build provenance. They can publish tags like1.0.0+customfix,1.0.0+vendor.1, or1.0.0+corp.20250324, and consumers can pin to that exact downstream variant when desired.
Proposed solution
Keep SemVer precedence unchanged, but add an opt-in literal exact match mode for version identifiers that includes build metadata. This would allow users to pin to a specific published variant (e.g. 1.0.0+debug) by matching the full version string, while keeping range requirements precedence-based.
-
Keep existing
.exact("...")behavior unchanged for compatibility (semantic exact match). -
Add a new API for literal exact matching, including build metadata, e.g.:
.package(url: "...", .exactLiteral("1.0.0+debug")) // For URL(SCM)-based dependencies
.package(id: "scope.name", .exactLiteral("1.0.0+debug")) // For registry-based dependencies
Semantics:
.exact(...)(existing): semantic exact match (current behavior)..exactLiteral(...)(new): candidate must match the tag/version string literally, including build metadata.- Range requirements (
from,upToNextMajor, etc.) remain precedence-based and unchanged.
This approach is:
- Non-breaking (existing manifests behave exactly as today).
- Opt-in for users who need metadata-aware pinning.
- SemVer-consistent (no change to precedence/ordering rules).
I’d appreciate feedback on:
- whether the API shape (
.exactLiteral) is appropriate or if there are better alternatives. - any edge cases or scenarios I may have overlooked.
Thanks!
Anticipated questions
-
Q: Isn't current git hash based exact mechanism enough?
A: It is enough for strict reproducibility, but not for human-readable variant intent and policy workflows. Metadata-aware exact matching gives a stable, explicit selector at manifest level. Also, this proposal applies to help with registry-based workflows, not just git. -
Q: Why not just publish a new patch version instead of
1.0.0+vendor.1?
A: Patch version implies upstream API/behavior progression. In downstream mirroring/repackaging, teams often want to preserve upstream SemVer compatibility while attaching provenance. -
Q: Does this violate SemVer since build metadata is ignored for precedence?
A: No. Precedence and range behavior remain unchanged. The proposal only adds opt-in literal exact matching semantics. -
Q: Will this break existing manifests or resolver behavior?
A: No, if introduced as a new API (e.g..exactLiteral(...)) while keeping current.exact(...)behavior unchanged.