@@ -9,6 +9,7 @@ use crate::ops;
9
9
use crate :: sources:: source:: QueryKind ;
10
10
use crate :: util:: cache_lock:: CacheLockMode ;
11
11
use crate :: util:: context:: GlobalContext ;
12
+ use crate :: util:: interning:: InternedString ;
12
13
use crate :: util:: toml_mut:: dependency:: { MaybeWorkspace , Source } ;
13
14
use crate :: util:: toml_mut:: manifest:: LocalManifest ;
14
15
use crate :: util:: toml_mut:: upgrade:: upgrade_requirement;
@@ -20,12 +21,25 @@ use std::cmp::Ordering;
20
21
use std:: collections:: { BTreeMap , HashMap , HashSet } ;
21
22
use tracing:: { debug, trace} ;
22
23
23
- pub type UpgradeMap = HashMap < ( String , SourceId ) , Version > ;
24
+ /// A map of all breaking upgrades which is filled in by
25
+ /// upgrade_manifests/upgrade_dependency when going through workspace member
26
+ /// manifests, and later used by write_manifest_upgrades in order to know which
27
+ /// upgrades to write to manifest files on disk. Also used by update_lockfile to
28
+ /// know which dependencies to keep unchanged if any have been upgraded (we will
29
+ /// do either breaking or non-breaking updates, but not both).
30
+ pub type UpgradeMap = HashMap <
31
+ // The key is a package identifier consisting of the name and the source id.
32
+ ( InternedString , SourceId ) ,
33
+ // The value is the original version requirement before upgrade, and the
34
+ // upgraded version.
35
+ ( VersionReq , Version ) ,
36
+ > ;
24
37
25
38
pub struct UpdateOptions < ' a > {
26
39
pub gctx : & ' a GlobalContext ,
27
40
pub to_update : Vec < String > ,
28
41
pub precise : Option < & ' a str > ,
42
+ pub breaking : bool ,
29
43
pub recursive : bool ,
30
44
pub dry_run : bool ,
31
45
pub workspace : bool ,
@@ -49,7 +63,11 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
49
63
Ok ( ( ) )
50
64
}
51
65
52
- pub fn update_lockfile ( ws : & Workspace < ' _ > , opts : & UpdateOptions < ' _ > ) -> CargoResult < ( ) > {
66
+ pub fn update_lockfile (
67
+ ws : & Workspace < ' _ > ,
68
+ opts : & UpdateOptions < ' _ > ,
69
+ upgrades : & UpgradeMap ,
70
+ ) -> CargoResult < ( ) > {
53
71
if opts. recursive && opts. precise . is_some ( ) {
54
72
anyhow:: bail!( "cannot specify both recursive and precise simultaneously" )
55
73
}
@@ -91,8 +109,44 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
91
109
let mut registry = ws. package_registry ( ) ?;
92
110
let mut to_avoid = HashSet :: new ( ) ;
93
111
94
- if opts. to_update . is_empty ( ) {
112
+ if opts. breaking {
113
+ // We don't necessarily want to update all specified packages. If we are
114
+ // doing a breaking update (or precise upgrades, coming in #14140), we
115
+ // don't want to touch any packages that have no breaking updates. So we
116
+ // want to only avoid all packages that got upgraded.
117
+ debug ! ( "Will avoid all upgraded packages" ) ;
118
+
119
+ for name in opts. to_update . iter ( ) {
120
+ // We still want to query any specified package, for the sake of
121
+ // outputting errors if they don't exist.
122
+ previous_resolve. query ( name) ?;
123
+ }
124
+
125
+ for ( name, source_id) in upgrades. keys ( ) {
126
+ let ( version_req, _) = upgrades. get ( & ( * name, * source_id) ) . unwrap ( ) ;
127
+
128
+ if let Some ( matching_dep) = previous_resolve. iter ( ) . find ( |dep| {
129
+ dep. name ( ) == * name
130
+ && dep. source_id ( ) == * source_id
131
+ && version_req. matches ( dep. version ( ) )
132
+ } ) {
133
+ let spec = PackageIdSpec :: new ( name. to_string ( ) )
134
+ . with_url ( source_id. url ( ) . clone ( ) )
135
+ . with_version ( matching_dep. version ( ) . clone ( ) . into ( ) ) ;
136
+ let spec = format ! ( "{spec}" ) ;
137
+ debug ! ( "Will avoid {spec}" ) ;
138
+ let pid = previous_resolve. query ( & spec) ?;
139
+ to_avoid. insert ( pid) ;
140
+ } else {
141
+ // Should never happen
142
+ anyhow:: bail!(
143
+ "no package named `{name}` with source `{source_id}` and version matching `{version_req}` in the previous lockfile" ,
144
+ )
145
+ }
146
+ }
147
+ } else if opts. to_update . is_empty ( ) {
95
148
if !opts. workspace {
149
+ // TODO: Test `cargo update --breaking` on non-ws
96
150
to_avoid. extend ( previous_resolve. iter ( ) ) ;
97
151
to_avoid. extend ( previous_resolve. unused_patches ( ) ) ;
98
152
}
@@ -103,6 +157,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
103
157
if opts. recursive {
104
158
fill_with_deps ( & previous_resolve, pid, & mut to_avoid, & mut HashSet :: new ( ) ) ;
105
159
} else {
160
+ debug ! ( "Will avoid to_update {pid}" ) ;
106
161
to_avoid. insert ( pid) ;
107
162
sources. push ( match opts. precise {
108
163
Some ( precise) => {
@@ -125,6 +180,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
125
180
if let Ok ( unused_id) =
126
181
PackageIdSpec :: query_str ( name, previous_resolve. unused_patches ( ) . iter ( ) . cloned ( ) )
127
182
{
183
+ debug ! ( "Will avoid unused {unused_id}" ) ;
128
184
to_avoid. insert ( unused_id) ;
129
185
}
130
186
}
@@ -165,7 +221,10 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
165
221
. filter ( |s| !s. is_registry ( ) )
166
222
. collect ( ) ;
167
223
168
- let keep = |p : & PackageId | !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) ;
224
+ debug ! ( "avoiding packages: {:?}" , to_avoid) ;
225
+
226
+ let keep =
227
+ |p : & PackageId | ( !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) ) ;
169
228
170
229
let mut resolve = ops:: resolve_with_previous (
171
230
& mut registry,
@@ -185,11 +244,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
185
244
opts. precise . is_some ( ) ,
186
245
& mut registry,
187
246
) ?;
188
- if opts. dry_run {
189
- opts. gctx
190
- . shell ( )
191
- . warn ( "not updating lockfile due to dry run" ) ?;
192
- } else {
247
+ if !opts. dry_run {
193
248
ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
194
249
}
195
250
Ok ( ( ) )
@@ -361,7 +416,10 @@ fn upgrade_dependency(
361
416
. status_with_color ( "Upgrading" , & upgrade_message, & style:: GOOD ) ?;
362
417
}
363
418
364
- upgrades. insert ( ( name. to_string ( ) , dependency. source_id ( ) ) , latest. clone ( ) ) ;
419
+ upgrades. insert (
420
+ ( name, dependency. source_id ( ) ) ,
421
+ ( current. clone ( ) , latest. clone ( ) ) ,
422
+ ) ;
365
423
366
424
let req = OptVersionReq :: Req ( VersionReq :: parse ( & latest. to_string ( ) ) ?) ;
367
425
let mut dep = dependency. clone ( ) ;
@@ -433,7 +491,7 @@ pub fn write_manifest_upgrades(
433
491
continue ;
434
492
} ;
435
493
436
- let Some ( latest) = upgrades. get ( & ( name. to_owned ( ) , source_id) ) else {
494
+ let Some ( ( _ , latest) ) = upgrades. get ( & ( name. into ( ) , source_id) ) else {
437
495
trace ! ( "skipping dependency without an upgrade: {name}" ) ;
438
496
continue ;
439
497
} ;
0 commit comments