-
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
Try installing exact versions before updating #8022
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Cargo.toml
Outdated
@@ -53,7 +53,7 @@ percent-encoding = "2.0" | |||
remove_dir_all = "0.5.2" | |||
rustfix = "0.5.0" | |||
same-file = "1" | |||
semver = { version = "0.9.0", features = ["serde"] } | |||
semver = { git = "https://github.com/illicitonion/semver.git", rev = "f1f912703f67ed63b751c525839731a90239bdcf", features = ["serde"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably want an upstream release of this (including dtolnay/semver#205) before merging this PR, but figured I'd see if there was appetite for it first.
} | ||
} | ||
} | ||
if let NeedsUpdate::TryWithoutFirst = needs_update { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This enum variant is weirdly tightly coupled to the style of source being used, but the args here already seem pretty tightly coupled to their caller styles. I tried a few different ways of factoring this out to avoid that, and none of them seemed great...
c559ef8
to
cd81fe9
Compare
cd81fe9
to
10c1240
Compare
When an exact version is being installed, if we already have that version from the index, we don't need to update the index before installing it. Don't do this if it's not an exact version, because the update may find us a newer version. This is particularly useful for scripts which unconditionally run `cargo install some-crate --version=1.2.3`. Before install-update, I wrote a crate to do this (https://crates.io/crates/cargo-ensure-installed) which I'm trying to replace with just `cargo install`, but the extra latency of updating the index for a no-op is noticeable. This introduces an interesting edge-case around yanked crates; the yanked-ness of crates will no longer change on install for exact version matches which were already downloaded. This feels niche enough to hopefully not matter...
10c1240
to
a721e62
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some tests here as well for this behavior?
The point about yanks feels pretty important though? It's basically subverting yanking behavior because you'll be able to install a yanked crate with --vers =a.b.c
so long as you haven't updated the index?
Thanks for the review! :) I restructured so that we instead just do a "is this already installed" check, and bail out early if so, by extracting some helper functions. Accordingly, the yank behaviour is fixed too (and I added a test for it). It's not clear to me whether the helper functions are exactly the right ones - I can imagine some of them could either be bigger or smaller, but I don't have a great instinct for the abstractions in cargo to know what naturally groups together. I'm very happy to change them around with a little guidance. I also added some tests :) |
vers | ||
}; | ||
let dep = Dependency::parse_no_deprecated(name, vers_spec, source.source_id())?; | ||
let vers = vers.map(|v| v.to_string()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit uncertain, why take a string, parse it to a VersionReq, only to immediately convert it back to a string where parse_no_deprecated
will parse it again? Couldn't select_pkg
keep the Option<&str>
to avoid that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous code did the same, so I was trying to preserve the exact semantics. If you take a look at the removed code just above this line, it roughly does:
if looks like a semver, parse it as one, then convert it back to string
else parse it to make sure it's a semver and then use the string after converting it to a VersionReq and manually re-implementing tostring for it (the format!("={}", v)
)
I'm worried that if we just pass the &str through a side-channel, it will skip running some important code.
I can happily read through parse_no_deprecated
and work out why we're doing the str -> VersionReq -> str conversion and maybe remove that, but it feels like that would be a separate change :)
src/cargo/ops/cargo_install.rs
Outdated
path.read_packages() | ||
})? | ||
} else { | ||
let mut source = map.load(source_id, &HashSet::new())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is already pretty long and gnarly. Can this new section of code somehow be moved out to another function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's fair - because of how many shared variables there are between this code and the whole rest of the method, and because we're early-returning based on the code, I was leaving it in-line, and trying to re-use some variables in the closure, but I've pulled it out... We should probably break this up in general, I guess :)
src/cargo/ops/cargo_install.rs
Outdated
} else if source.source_id().is_registry() { | ||
Some(VersionReq::any()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused by this check. Why is it needed if the code below immediately checks for is_exact
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vers
is then passed to select_pkg
. I've re-structured this to make it a bit more obvious :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is something really subtle here, so I think it might be worth changing this. From a naive look, select_pkg
with a version of None should use the "any" requirement implicitly as parse_no_deprecated
does this. However, due to the round-trip of string->VersionReq->string->VersionReq, the "any" requirement gets changed to "*"
which is different from VersionReq::any
, as "*"
means "do not select pre-release versions" (see dtolnay/semver#161). This is behavior Cargo relies on.
I'd like this to be a bit more explicit. I'm thinking, change it back so that select_pkg
takes Option<&str>
, and structure it more like it used to be. That way, there is an explicit step (where the old comment Avoid pre-release versions
was) that can very clearly call out this behavior. That way there isn't this distant assumption about what VersionReq::any
means. installed_exact_package
can throw away whatever work it does to determine if it is an exact version, I don't think it helps to pass in a VersionReq
to select_pkg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay!
src/cargo/ops/cargo_install.rs
Outdated
} else if source.source_id().is_registry() { | ||
Some(VersionReq::any()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is something really subtle here, so I think it might be worth changing this. From a naive look, select_pkg
with a version of None should use the "any" requirement implicitly as parse_no_deprecated
does this. However, due to the round-trip of string->VersionReq->string->VersionReq, the "any" requirement gets changed to "*"
which is different from VersionReq::any
, as "*"
means "do not select pre-release versions" (see dtolnay/semver#161). This is behavior Cargo relies on.
I'd like this to be a bit more explicit. I'm thinking, change it back so that select_pkg
takes Option<&str>
, and structure it more like it used to be. That way, there is an explicit step (where the old comment Avoid pre-release versions
was) that can very clearly call out this behavior. That way there isn't this distant assumption about what VersionReq::any
means. installed_exact_package
can throw away whatever work it does to determine if it is an exact version, I don't think it helps to pass in a VersionReq
to select_pkg.
Handling of these is coupled, so do the handling in one place, close to where we parse the command line flags, so we can just pass in a single derived object.
It only ever actually uses one, so let's reflect that in the types.
Thanks for the review! I progressively got the signature of Individual commits are probably useful to review separately. |
☔ The latest upstream changes (presumably #8137) made this pull request unmergeable. Please resolve the merge conflicts. |
Oh, nevermind, I missed the merge commit from last week. |
☔ The latest upstream changes (presumably #8167) made this pull request unmergeable. Please resolve the merge conflicts. |
Thanks! This looks good to me. Just waiting on getting semver update. I pinged Steve about it, and he said he'll try to take a look at it next week. |
We already have a hack in the code for is_exact: cargo/src/cargo/core/dependency.rs Lines 395 to 398 in ae08099
If it is added to upstream we should remove our use of that hack. |
Ooh - it reads to me like my new code should probably also call |
☔ The latest upstream changes (presumably #8236) made this pull request unmergeable. Please resolve the merge conflicts. |
@illicitonion if you want to switch it to do something similar to I don't care too much if it is inlined or a shared utility function, whatever is easiest. |
semver hasn't merged the upstream PR (yet)
Done and green - thanks @ehuss! |
Thanks! |
📌 Commit b719272 has been approved by |
☀️ Test successful - checks-azure |
Update cargo 7 commits in 500b2bd01c958f5a33b6aa3f080bea015877b83c..9fcb8c1d20c17f51054f7aa4e08ff28d381fe096 2020-05-18 17:12:54 +0000 to 2020-05-25 16:25:36 +0000 - Bump to semver 0.10 for `VersionReq::is_exact` (rust-lang/cargo#8279) - Fix panic with `cargo tree --target=all -Zfeatures=all` (rust-lang/cargo#8269) - Fix nightly tests with llvm-tools. (rust-lang/cargo#8272) - Provide better error messages for a bad `patch`. (rust-lang/cargo#8248) - Try installing exact versions before updating (rust-lang/cargo#8022) - Document unstable `strip` profile feature (rust-lang/cargo#8262) - Add option to strip binaries (rust-lang/cargo#8246)
When an exact version is being installed, if we already have that
version from the index, we don't need to update the index before
installing it. Don't do this if it's not an exact version, because the
update may find us a newer version.
This is particularly useful for scripts which unconditionally run
cargo install some-crate --version=1.2.3
. Before install-update, Iwrote a crate to do this
(https://crates.io/crates/cargo-ensure-installed) which I'm trying to
replace with just
cargo install
, but the extra latency of updating theindex for a no-op is noticeable.