r? @weihanglo
rustbot has assigned @weihanglo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.
Use r?
to explicitly pick a reviewer
Should we wait for #14049, or this can be reviewed independently?
The 4 new commits here can be reviewed. I think #14049 is close to being merged anyway.
The 4 new commits here can be reviewed. I think #14049 is close to being merged anyway.
In the future, please make a blocking thing like that explicit either by making this a draft or opening an issue on an arbitrary part of the code.
In #12425 (comment), this PR was marked as addressing
Consider an error message if the command completed without doing any upgrades.
Except this PR description does not include an explanation as to why this PR addresses that and why an error is not the way forward
(btw probably best not to mark things as done when they aren't merged as things can change especially when the "solution" is not decided yet)
1 | //! Duplicating tests for `cargo update --precise` with the | ||
2 | //! update-precise-breaking feature enabled. When the feature is stabilized, | ||
3 | //! this file can be deleted. | ||
4 | |||
5 | #![allow(deprecated)] |
Still trying to decide what the best approach is here but in the mean time, could you split this into two commits
That way it shows what behavior change happened (or not) with the feature.
Instead of duplicating tests, one approach we could take is using a test only env var to activate the unstable behavior, for example __CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2
.
(Yes I admit that this opens a door for people without -Zunstable-options
, though we already have __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS
hence 😬)
__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2
is used from tests to modify Cargo behaviour. You would still need duplicate tests if you wanted to run Cargo with both options.
I was thinking the duplicate test file is OK, as it is meant to be temporary and can be deleted later.
I have put it in a separate commit, and it didn't need any modification after implementing the feature.
This was split out into its own commit but I'd like to see it split into two commits.
Currently, the commit is
$ cp tests/testsuite/update.rs
$ edit tests/testsuite/update_duplicated_with_precise_breaking_feature.rs
$ git commit
I was asking to see
$ cp tests/testsuite/update.rs
$ git commit
$ edit tests/testsuite/update_duplicated_with_precise_breaking_feature.rs
$ git commit
Depending on how well it diffs, maybe it should be more
This makes it easier to audit in this review, and in the future, what the differences are between the two tests files.
1 | use std::collections::HashMap; |
Only did a quick glance at the tests; haven't gotten to the implementation yet.
Except this PR description does not include an explanation as to why this PR addresses that and why an error is not the way forward
Description updated.
(btw probably best not to mark things as done when they aren't merged as things can change especially when the "solution" is not decided yet)
Got it.
Description updated.
I'm still not seeing the description talk to
why an error is not the way forward
btw it would be good to focus on user impact and not implementation. The paragraph that alludes to this starts off by discussing the implementation which someone is likely to gloss over when looking for user impact.
☔ The latest upstream changes (presumably #14049) made this pull request unmergeable. Please resolve the merge conflicts.
103 | .fail_if_stable_opt("--breaking", 12425)?; | ||
104 | |||
105 | let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?; | ||
106 | ops::resolve_ws(&ws, update_opts.dry_run)?; |
With this change, it is now possible to revert the introduction of the dry_run
argument in resolve_ws
. Let me know what you think.
1823 | 1812 | .masquerade_as_nightly_cargo(&["update-precise-breaking"]) | |
1824 | 1813 | .with_status(101) | |
1825 | 1814 | .with_stderr_data(str![[r#" | |
1826 | [UPDATING] `dummy-registry` index | ||
1827 | [ERROR] failed to select a version for the requirement `pre = "^0.1"` | ||
1828 | candidate versions found which didn't match: 0.2.0-beta | ||
1829 | location searched: `dummy-registry` index (which is replacing registry `crates-io`) | ||
1830 | required by package `foo v0.0.1 ([ROOT]/foo)` | ||
1831 | if you are looking for the prerelease package it needs to be specified explicitly | ||
1832 | pre = { version = "0.2.0-beta" } | ||
1833 | perhaps a crate was updated and forgotten to be re-vendored? | ||
1815 | [ERROR] New requirement ^0.2 is invalid, because it doesn't match 0.2.0-beta |
@epage I'm curious what you think of this.
Would you mind changing the first letter to lower case? There is a convention we do that. (I know old message didnt' follow the convention…)
We have another unstable --precise <pre-release>
, which we may want to allow here?
Lower casing fixed. Awaiting feedback on the error case.
Why is this an error?
0.1
0.2.0-beta
is publishedcargo update --precise 0.2.0-beta
We gave it a valid version that it a breaking change from the current one, so it seems like we should just use that as our version requirement.
The fact that the error doesn't make sense from the users perspective ("I never said the version req was ^0.2
so why is it trying that?") I think helps highlight that.
Looks like its only an error because the user's req was 0.1
instead of 0.1.0
? In that case, I think its reasonable for us to act as if it was 0.1.0
.
122 | return self.matches(&version); | ||
120 | let mut version_cleaned = version.clone(); | ||
121 | version_cleaned.pre = semver::Prerelease::EMPTY; | ||
122 | return self.matches(&version) || self.matches(&version_cleaned); |
This needs more looking into. A test is failing. I'll also move this and related changes to a separate commit.
Two commits inserted at the beginning of the PR.
14 | 14 | use crate::util::toml_mut::upgrade::upgrade_requirement; | |
15 | 15 | use crate::util::{style, OptVersionReq}; | |
16 | 16 | use crate::util::{CargoResult, VersionExt}; | |
17 | use anyhow::Context; |
nit: Context
is a name so general that may conflict with other structs.
use anyhow::Context; | |
use anyhow::Context as _; |
Fixed.
There is discrepancy between the normal update and breaking update.
v
prefixNot a blocker for this PR, but we might want track this somewhere.
This was discussed here: #13979 (comment)
I'll add it to the task list.
What's the motivation behind 45bc28c?
In this test (update_breaking_spec_version_transitive
) I wanted to specifically target foo.dep
but not bar.dep
. I can do that if foo.dep=1.0
and bar.dep=2.0
:
p.cargo("update -Zunstable-options --breaking dep@1.0")
.with_stderr_data(str![[r#"
[UPGRADING] dep ^1.0 -> ^3.0
[UPDATING] dep v1.0.0 -> v3.0.0
But not if bar.dep=1.1
, because that makes both foo.dep
and bar.dep
resolve to 1.1
.
1511 | .file("src/lib.rs", "") | ||
1512 | .build(); | ||
1513 | |||
1514 | p.cargo("update -p incompatible --precise 2.0.0") |
-p
is not needed anymore :)
Fixed.
2851 | [ERROR] failed to select a version for the requirement `renamed-from = "^1.0"` | ||
2852 | candidate versions found which didn't match: 2.0.0 | ||
2853 | location searched: `dummy-registry` index (which is replacing registry `crates-io`) | ||
2854 | required by package `foo v0.0.1 ([..]/foo)` |
[..]
glob is not necessary.
Fixed.
1937 | candidate versions found which didn't match: 2.0.0 | ||
1938 | location searched: `dummy-registry` index (which is replacing registry `crates-io`) | ||
1939 | required by package `foo v0.0.1 ([ROOT]/foo)` | ||
1940 | perhaps a crate was updated and forgotten to be re-vendored? |
Oh no this error message is bad 😢
Keep in mind this is without the feature implemented. In the next commit this becomes:
[UPGRADING] pre ^1.0.0-alpha -> ^2.0.0
[UPDATING] `dummy-registry` index
[UPDATING] pre v1.0.0-alpha -> v2.0.0
Could we document this type alias? It's not immediately clear what should be included in this HashMap. This LockedMap is a good reference:
cargo/src/cargo/core/registry.rs
Lines 136 to 154 in 4403332
Fixed.
Description updated.
I'm still not seeing the description talk to
why an error is not the way forward
I have updated the task list. Hope that's OK.
This is ready for another look now.
117 | 117 | /// and we're not sure if this part of the functionality should be implemented in semver or cargo. | |
118 | 118 | pub fn matches_prerelease(&self, version: &Version) -> bool { | |
119 | 119 | if version.is_prerelease() { | |
120 | let mut version = version.clone(); | ||
121 | version.pre = semver::Prerelease::EMPTY; | ||
122 | return self.matches(&version); | ||
120 | // Only in the case of "ordinary" version requirements with pre-release | ||
121 | // versions do we need to help the version matching. In the case of Any, | ||
122 | // Locked, or Precise, the `matches()` function is already doing the | ||
123 | // correct handling. | ||
124 | if let OptVersionReq::Req(_) = self { | ||
125 | let mut version = version.clone(); | ||
126 | version.pre = semver::Prerelease::EMPTY; | ||
127 | return self.matches(&version); | ||
128 | } |
I don't think this is an appropriate place to be putting this fix.
Note the comment in the description
we're not sure if this part of the functionality should be implemented in semver or cargo.
At this time, this is an abstraction that we shouldn't be breaking with cargo-specific logic
btw these two commtis seem unrelated with the rest and seem like they should be their own PR
Hmm, but all I'm doing here is relaxing the cargo-specific logic. Without this fix, I get this bug:
Upgrading pre ^0.1.0 -> ^0.2.0-beta
Updating `dummy-registry` index
error: failed to select a version for the requirement `pre = "^0.2.0-beta"`
candidate versions found which didn't match: 0.2.0-beta
What do you think I should do?
If we take 0.2.0-beta
, remove -beta
, we get 0.2.0
. That seems like 0.2.0-beta
should be able to match that.
Any insight into why this isn't working?
This is a bug for this PR.
matches_prerelease
will alaways a OptVersionReq::Req
in my previous PR, but this PR will add OptVersionReq::Precise
to match , see
let req = if precise.is_some() {
OptVersionReq::Precise(new_version, new_version_req)
} else {
OptVersionReq::Req(new_version_req)
};
Except this PR description does not include an explanation as to why this PR addresses that and why an error is not the way forward
Description updated.
The PR says:
This PR is making a change that also affects cargo update --breaking. We'll reuse ops::update_lockfile() for both breaking and non-breaking updates. The benefit is more consistent output and behaviour between the two. In particular, it addresses a task about an error output if there is nothing to upgrade. See #12425 (comment).
This is focused on the implementation, glosses over the reason, and doesn't say what is being changed.
I think its best to split this out from this PR, either making ti independent or making one depend on the other. Trying to slip it in like this is making it harder to review and discuss both.
This seems to incorrectly be interacting with the precise-prerelease feature. The intention of that feature is that we should occasionally allow matching pre-releases with a regular version requirement.
1713 | 1709 | ||
1714 | 1710 | p.cargo("update -Zunstable-options incompatible --precise 0.2.0") | |
1715 | 1711 | .masquerade_as_nightly_cargo(&["update-precise-breaking"]) | |
1716 | .with_status(101) | ||
1717 | 1712 | .with_stderr_data(str![[r#" | |
1713 | [UPGRADING] incompatible ^0.1 -> ^0.2 | ||
1718 | 1714 | [UPDATING] `dummy-registry` index | |
1719 | [ERROR] failed to select a version for the requirement `incompatible = "^0.1"` | ||
1720 | candidate versions found which didn't match: 0.2.0 | ||
1721 | location searched: `dummy-registry` index (which is replacing registry `crates-io`) | ||
1722 | required by package `foo v0.0.1 ([ROOT]/foo)` | ||
1723 | perhaps a crate was updated and forgotten to be re-vendored? | ||
1715 | [UPDATING] incompatible v0.1.0 -> v0.2.0 | ||
1716 | [NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest |
update_precise_breaking_consistent_output
Consistent with what? How will this ensure we keep remaining consistent?
2255 | 2218 | // No spec | |
2256 | 2219 | p.cargo("update -Zunstable-options incompatible --precise 0.3.1") |
update_precise_breaking_spec_version
I'm confused as to what this test is trying to capture compared to other tests
Oh, its not that there is no spec but that we aren't specifying a version in the spec, only the name
Does cargo update --precise <no-meta>
work on packages with metadata? Whichever way, this should do the same if its a breaking change
165 | 182 | .filter(|s| !s.is_registry()) | |
166 | 183 | .collect(); | |
167 | 184 | ||
168 | let keep = |p: &PackageId| !to_avoid_sources.contains(&p.source_id()) && !to_avoid.contains(p); | ||
185 | let breaking_precise_upgrades = opts.precise.is_some() && !upgrades.is_empty(); | ||
186 | let breaking_mode = opts.breaking || breaking_precise_upgrades; | ||
187 | |||
188 | let keep = |p: &PackageId| { | ||
189 | (!to_avoid_sources.contains(&p.source_id()) && !to_avoid.contains(p)) | ||
190 | // In case of `--breaking` or precise upgrades, we want to keep all | ||
191 | // packages unchanged that didn't get upgraded. | ||
192 | || (breaking_mode && !upgrades.contains_key(&(p.name().to_string(), p.source_id()))) | ||
193 | }; |
@torhovland I was very intentionally trying to avoid changing this in past PRs. To go back and change it requires a good explanation. Even better if you can also split this into its own commit or PR
@Eh2406 I'm very cautious of messing with keep
and would appreciate your input on tweaking it for this case
keep
also makes my head spin. I feel like it's something about the triple negative structure, we update the ones that are not keep
, we keep
the ones that are not avoid
, we avoid
the things that asked to be updated. But I've never found it clearer naming structure, so maybe that's not it. It's also trying to do a linear prediction of resolution which feels like an approximation.
In specific, why does this need to be a separate lookup as opposed to adding the matching packages to to_avoid
?
Also, this callback is run a lot on no-op builds, so very small performance problems end up being a high percentage of runtime. I suspect the to_string
is going to end up being expensive. Can the upgrades
map be InternedStrings
or &str
?
The reason this is not put in to_avoid
is that those require PackageId
s, i.e. they require a version. When doing upgrades, we don't have any versions, only version requirements.
I'm pulling this out into a separate PR: #14259
I assume with that pulled out this PR will be updated so we can get it merged without #14259 so this doesn't get blocked on that decision?
OK, I suppose we can do that. It will help illustrate why #14259 is useful.
I don't like the idea of not building on #14259. I suggest we rather let this PR depend on #14259 and leave it as draft for now. Here's why:
Some of the duplicated existing precise tests with the unstable feature enabled are no longer passing. In other words, without #14259 we will be changing the behaviour of the non-breaking precise update.
Some invocations are now silently finishing rather than reporting an error. For example:
[ERROR] package ID specification `incompatible@2.0.0` did not match any packages
Did you mean one of these?
incompatible@0.3.1
-[UPDATING] incompatible v0.1.0 -> v0.2.0
-[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
+[LOCKING] 1 package to latest compatible version
+[UPDATING] incompatible v0.1.0 -> v0.2.0 (latest: v0.2.1)
I don't like the idea of not building on #14259. I suggest we rather let this PR depend on #14259 and leave it as draft for now.
That PR has behavior changes that have not been agreed to, internal and user-facing. We should not block progress forward on something on changes like that. If there is some intermediate change that side steps the controversial parts that unblocks --precise
, then let's go for that.
I'm not seeing this error covered in the tests
Why did the style for Upgrading
change?
27 | 29 | new_req_text.remove(0); | |
28 | 30 | } | |
29 | 31 | // Validate contract | |
30 | #[cfg(debug_assertions)] | ||
31 | { | ||
32 | assert!( |
With my other comment in mind, is there a reason this should be a bail
, rather than an assert
?
2565 | } | ||
2566 | |||
2567 | #[cargo_test] | ||
2568 | fn update_precise_breaking_direct_plus_transitive() { |
@epage Can I ask you to take a quick look at this test, please? I believe it answers the concern you raised in office hours. The test demonstrates that we do support upgrading a direct dependency while leaving the transitive dependency at its original version.
If this is reassuring to you, it means #14259 is a valid step forward and we should get it merged. Then I'll clean up this PR.
☔ The latest upstream changes (possibly 081d7ba) made this pull request unmergeable. Please resolve the merge conflicts.
Login to write a write a comment.
Implements the second half of #12425.
With the
unstable-options
feature enabled,cargo update --precise
will allow making upgrades/downgrades that require changes to be made to the manifest files, similar tocargo update --breaking
.Blocked on #14259. See https://github.com/rust-lang/cargo/pull/14140/files#r1682381732.