-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lint againt non semver-open dependencies #5340
Comments
@rust-lang/cargo: this is the issue for the idea we've discussed at all hands and on the last meeting. |
Thanks for opening an issue for this @matklad! I think the general idea here is a good one to basically raise awareness about the dangers of overly restrictive version requirements, but I do think we need to tread carefully as well. These sorts of version requirements are often useful in a pinch in some situations and can act as a form of relief rather than constantly feeling like Cargo is slapping you on the wrist. I think this came up as well when publishing to crates.io awhile back as well. We considered rejecting any publishes with a version requirement that has an overly restrictive upper bound (aka in the middle of a semver range) and ended up only disallowing I'd also want to make sure that the warning story here is something more sophisticated than "warn every time we see these version requirements", although I'm not sure how we'd best do that... |
I think the most important time to enforce this is on publish, right? You don't want your libraries with overly restrictive requirements causing you problems, but you might want to use We could, as sort of the most moderate thing, not upload something with non-open requirements unless you pass an additional flag (similar to |
Agree with @withoutboats that that would a nice conservative first step, which actually will get us 90% of the most aggressive solution anyway. We've also talked several times before about various publish-time checks (like filling fields in Cargo.toml, having basic docs, etc), so perhaps it's time to introduce some general CLI for such checks? Like
If that sounds great, I'll be happy to write some mentoring instructions! |
At least starting out with warnings on publish sounds great to me! |
it would probably be useful to be able to run these checks independently of publishing, such as in CI. |
@dwijnand If I'm not mistaken this exists as |
oh yeah.. easy :) 👍 |
Mentoring instructions: We already implement similar functionality for warning about missing Cargo.toml fields, this happens here: To add checks for versions, the steps are:
|
I will take this. |
@csmoe how is it going? Do you need any help? Another special case came up in #5275: Cargo treats |
I'm not a fan of warning against In my gmp-mpfr-sys crate, I recommend using FFI also makes it difficult to use the semver trick, as different versions can have different incompatible C libraries. I don't think it's reasonable to expect a solution that merges two incompatible versions of the same C library. In such a case, it would be very useful to be able to make a dependent crate compatible with two different major versions of the -sys crate. I also have a use case for an |
Thanks a lot for the input @tspiteri ! I definitely agree we shouldn't emit any warnings for dev dependencies, as they don't affect the resolution for reverse-dependeices. I think that we should make exception for the -sys crates as well. The underlying reason why we want to discourage ~ dependencies is that they can lead to genuinely unsatisfiable dependencies. If one crate depends on However, the -sys crates already make it possible to create unsatisfiable dependencies, because of the The question is, how do we exclude -sys crates from linting? If you have a dependency specification like Could we use a heuristic "if dependency name ends in |
About the hacky heuristic to detect FFI crates: if the warnings are issued by |
Late input and FWIW: I've been using version bounds like this frequently: failure = ">=0.1.0, <2"
fern = ">=0.5.5, <2"
futures = ">=0.1.20, <0.2" I check in Cargo.lock files so I have some additional control. Advantages of this from my perspective:
So I'm personally hoping I'm not going to run afoul of the proposed warnings for doing something that has at least a certain rationale, IMO. Also this warning proposal seems to not consider the unfortunate reality that semver has some gray areas (see ongoing rust semver debates in various places) and otherwise well maintained crates break others all the time on MINOR releases. Crate authors need, from time to time, to indicate a maximum dependency bound, like futures < 0.2. Would they benefit from or appreciate a warning when doing that? |
@dekellum a dependency constraint of |
Its not clear from the proposal that "0.1" wouldn't garner a warning. In any case I personally prefer the expanded notation for use is the Cargo.toml. Also there is the potential if unfortunate scenario of needing something like ">= 0.2.1, <= 0.2.7". I wouldn't expect crate authors to write that without good reason (e.g. 0.2.8 breaks something). |
"0.1" will not garner a warning; it is the form we are guiding people towards with that warning. |
@matklad, said:
I think I have the above mentioned use cases. To give another concrete example: Lets say crate foo has ongoing "3.3.x" releases targeting rust 2018 edition, but releases a "4.0.0" to use new features in rust 2019. Meanwhile the dependent crate bar has CI on all of these versions and its 1.3.24 release has CI showing its compatible with all of the above, so it specifies its foo dependency as: foo = ">= 3.3.17, <5" Does the author of bar want to see a constant warning about this? Editorial Note: My original understanding of the aspirational purpose of Rust Editions feature has improved. If you replace "2018 edition" and "2019 edition" with "rust 1.43" and "rust 1.44" (coincidentally the first version to support the "2019 edition"), then the above example is more plausible and consistent. |
Another use case for “exotic” version dependencies: crates that must be updated in lockstep. For instance, a So I think there are legitimate use cases for using non-standard version requirements. That said, warning against them by default completely does make sense (for the above-mentioned problem of having dependencies that must be updated in lockstep, for instance -- though that'd likely be best solved by #5920). |
As a practical example of your x-derive case, We recently saw some breakage with the failure 0.1.2 release (rust-lang-deprecated/failure#234) where failure has a dependency on failure_derive written as "0.1.2" (meaning ">=0.1.2, < 0.2"). Both crates 0.1.2 version were released simultaneously but failure_derive 0.1.2 will not compile with failure 0.1.1. Cargo should IMO, be able to handle failure_derive specifying a more restrictive back-depdendency, e.g. ">= 0.1.2, < 0.2" for failure but currently cargo errors out because it views it as a dependency cycle. 2¢: I think this is a misfeature, as cargo should be (heuristically?) solving the graph with preference to single versions, effectively merging broad to narrow dependency ranges, and not being bothered by cycles. |
@dekellum EDIT: Fixed syntax typo. |
The human error rate for parsing and interpreting |
Actually the tilde syntax is extra confusing as it differs from the rubygems I'm used to, and has another weird caveat that no mortal could possibly remember:
|
@joshtriplett apparently didn't mean "~0.1.2" (human typo) and of course this lint doesn't want to keep the tilde operator either, so in that limited sense we agree. :-) |
What is the status of this issue? @matklad I need some clarifications on the issue...to create the API in |
I need to wait on dtolnay/semver#191 before I can proceed. |
@hbina I've been outside of Cargo dev for quite a while, so I am not sure what's the status here, but:
|
I have fixed all known (to me) real world slow cases in the resolver. There are still synthetic ones (#6258) but there pretty pathological. I don't remember a specific fix for |
Once #12115 is stabilized, we can add cargo lints with user control over them which would be a big help for when there are valid reasons for doing this (generally should be crate's depending on their proc macro and bins) |
FYI there is some nuance to this. See #12323 for more details |
EDIT: the issue now has mentoring instructions. Note that to implement this, we'll need to add additional API to semver crate as well!
Cargo more-or-less enforces contract on crate authors that version
1.x+n.u
can always be used instead of1.x.v
. That means that any semver requirement, except for^x.y.z
, does not make sense. Moreover, exotic version requirements may version resolution significantly more complicated.So we should lint against any sevmer requirement other than
x
,x.y
orx.y.z
.Specifically,
^x.y.z
is not necessary, because^
is default~
violates versioning contract. As a consequence Cargo will generally refuse to compile a project with two tilde dependencies with same major version.>
,>=
do not make sense, because major version upgrade, by contract, can break anything.=
is in the wrong place, lockfiles should be used to pin dependencies.<
,<=
again, version pining belongs to the lockfile.*
we already forbid. It actually makes sense for suerp-quick local development though!The interesting not clear-cut use-case is version striding (compatibility with several major versions). For example,
serde = ">= 2, < 4"
means that you are source-compatible with bothserde 2
andserde 3
. However, looks like the proper solution here is for the upstream crate to do dtolnay's trick? That is,serde 2
should depend onserde 3
, and reexport it's APIs.There are several design decisions on how to actually implement it:
We can warn on
cargo publish
enforcing this for crate.io crates, or we can warn on parsing Cargo.toml, enforcing this for anyone.We can add an opt-out mechanism. For example, we might make you to write a
reason
if you use non-standard requirement:foo = { version = "~9.2", reason = "this crate has weird rustc compatibility policy" }
.Finally, the most radical option, what if we flatly deprecate all forms of version specification, except for
x
,x.y
,x.y.z
? That way, we won't need to document and explain various forms of version requirements at all.I personally am leaning towards the last option, because it makes the overall system radically simpler for the user, but I am curious if there are some real use-cases for exotic version requirements?
The text was updated successfully, but these errors were encountered: