cargo icon indicating copy to clipboard operation
cargo copied to clipboard

Allow mutitple SemVer-compatible version with `=` exact requirement

Open andrewbanchich opened this issue 2 years ago • 7 comments

Problem

Adding multiple exact (=) versions of the same crate which Cargo considers semver compatible will produce a dependency conflict.

[dependencies]
foo = "=1.2.0"
foo_old = { version = "=1.1.0", package = "foo" }
error: failed to select a version for `foo`.
    ... required by package `rusttest v0.0.1`
versions that meet the requirements `=1.1.0` are: 1.1.0

all possible versions conflict with previously selected packages.

  previously selected package `foo v1.2.0`
    ... which satisfies dependency `foo = "=1.2.0"` of package `rusttest v0.0.1`

failed to select a version for `foo` which could resolve this conflict

Is this a technical problem, or is it Cargo thinking it knows better than me because those two versions should be semver compatible?

If it's the latter, I would consider this a bug.

Steps

See above

Possible Solution(s)

No response

Notes

No response

Version

No response

andrewbanchich avatar Oct 07 '23 12:10 andrewbanchich

1.1.0 and 1.2.0 are considered SemVer-compatible. Cargo allows only one Semver-compatible version per package. Copied from the discussion:

A compromise that was made way back at the beginning of cargo. One version per package, like Python, ends up to strict. Dependency hell. As many versions as needed, like NPM, ends up with blote. And errors of the form can not use foo as foo The compromise was keep more than one copy as long as the copies are not semver compatible

Cargo strictly follows SemVer. I would recommend remove = requirement as they should be compatible, otherwise they should be released under SemVer-incompat versions. See this chapter for more info on SemVer compatibility.

weihanglo avatar Oct 07 '23 12:10 weihanglo

I have a use case for doing this, so what I'm currently forced to do is vendor the package code and rename it to something else. That would have the same issue with dependency bloat, right?

andrewbanchich avatar Oct 07 '23 13:10 andrewbanchich

Could you share more details of your use case? That's a better way to move forward. Changing the behavior requires an RFC, as the comment states.

Without knowing the details, as I see it. The author of the package may misuse SemVer. You can also consider patching the package if possible.

weihanglo avatar Oct 07 '23 13:10 weihanglo

I'm working on a way to detect breaking changes in dependencies. Patch releases can include behavior changes in APIs, but because they're bugfixes they are considered compatible. What's more, semver is just a pinky promise.

In order to write tests for migrating from one minor / patch version to another, I need to include both in a binary and compare results.

andrewbanchich avatar Oct 07 '23 13:10 andrewbanchich

The cargo team talked about this a bit today. Just to clarify, this isn't a bug and how it is intended to behave.

We recognized that there seemed to be some use cases that could benefit from something like this, but we had various concerns, such as:

  • How difficult is this to implement? Cargo deeply assumes that only a single version of semver-compatible range is used, so it could have wide-reaching impacts, possibly to many corner cases that could be difficult to find and resolve.
  • How confusing might this be? For example, if an arbitrary dependency could impact resolution in this way, it could lead to confusing or deleterious effects. One thought was to maybe restrict this in some fashion as to where these could be specified.
  • As noted, there are some workarounds such as moving things to vendored or path dependencies.

We didn't have any conclusions at this time.

Someone could check my understanding, but this looks like it could be a duplicate of https://github.com/rust-lang/cargo/issues/13594.

ehuss avatar Apr 15 '25 18:04 ehuss

To clarify, this issue is specifically allowing it for direct dependencies, not arbitrary transitive ones.

andrewbanchich avatar Apr 16 '25 01:04 andrewbanchich

I'm hitting this same problem with

# Cargo.toml
niri-ipc-25-2-0 = { package = "niri-ipc", version = "=25.2.0" }
niri-ipc-25-5-1 = { package = "niri-ipc", version = "=25.5.1" }

Which really hurts because my dependency explicitly documents that it does not follow semver for versioning, so I must use multiple semver compatible versions of it renamed (to provide application compatibility to my users).

The cargo team talked about this a bit today. Just to clarify, this isn't a bug and how it is intended to behave. [...] How difficult is this to implement? [...] How confusing might this be?

So you gonna declare this "not a bug" because you couldn't figure out an easy solution? That is just wrong.

I told cargo - with the exact versioning syntax - to give me "=25.2.0" renamed to X and give me "=25.5.1" renamed to Y. I'm using the exact syntax "=0.0.0" because I don't care about semver here, so cargo should shut the hell up about any semver things and give me the exact versions I'm asking for. So THIS IS A BUG!

As noted, there are some workarounds such as moving things to vendored or path dependencies.

This is not a solution because of crates.io publishing requirements disallowing vendored or path dependencies. You want me to republish my dependecy to crates.io - not only once but multiple times renamed to funky crate names e.g. here my-app-niri-ipc-25-2-0 and my-app-niri-ipc-25-5-1. This is just a very ugly hack (unless I'm missing something that's what this workaround would mean with regards for crates.io).

gergo-salyi avatar May 31 '25 09:05 gergo-salyi