@@ -8,17 +8,21 @@ use crate::ops;
88use crate :: sources:: source:: QueryKind ;
99use crate :: util:: cache_lock:: CacheLockMode ;
1010use crate :: util:: context:: GlobalContext ;
11- use crate :: util:: style;
11+ use crate :: util:: toml_mut:: dependency:: { RegistrySource , Source } ;
12+ use crate :: util:: toml_mut:: manifest:: LocalManifest ;
1213use crate :: util:: CargoResult ;
14+ use crate :: util:: { style, OptVersionReq } ;
15+ use semver:: { Version , VersionReq } ;
1316use std:: cmp:: Ordering ;
14- use std:: collections:: { BTreeMap , HashSet } ;
17+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
1518use tracing:: debug;
1619
1720pub struct UpdateOptions < ' a > {
1821 pub gctx : & ' a GlobalContext ,
1922 pub to_update : Vec < String > ,
2023 pub precise : Option < & ' a str > ,
2124 pub recursive : bool ,
25+ pub breaking : bool ,
2226 pub dry_run : bool ,
2327 pub workspace : bool ,
2428}
@@ -41,7 +45,11 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
4145 Ok ( ( ) )
4246}
4347
44- pub fn update_lockfile ( ws : & Workspace < ' _ > , opts : & UpdateOptions < ' _ > ) -> CargoResult < ( ) > {
48+ pub fn update_lockfile (
49+ ws : & Workspace < ' _ > ,
50+ opts : & UpdateOptions < ' _ > ,
51+ upgrades : & HashMap < String , Version > ,
52+ ) -> CargoResult < ( ) > {
4553 if opts. recursive && opts. precise . is_some ( ) {
4654 anyhow:: bail!( "cannot specify both recursive and precise simultaneously" )
4755 }
@@ -157,7 +165,12 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
157165 . filter ( |s| !s. is_registry ( ) )
158166 . collect ( ) ;
159167
160- let keep = |p : & PackageId | !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) ;
168+ let keep = |p : & PackageId | {
169+ ( !to_avoid_sources. contains ( & p. source_id ( ) ) && !to_avoid. contains ( p) )
170+ // In case of `--breaking`, we want to keep all packages unchanged that
171+ // didn't get upgraded.
172+ || ( opts. breaking && !upgrades. contains_key ( & p. name ( ) . to_string ( ) ) )
173+ } ;
161174
162175 let mut resolve = ops:: resolve_with_previous (
163176 & mut registry,
@@ -207,6 +220,162 @@ pub fn print_lockfile_changes(
207220 }
208221}
209222
223+ pub fn update_manifests (
224+ ws : & mut Workspace < ' _ > ,
225+ opts : & UpdateOptions < ' _ > ,
226+ ) -> CargoResult < HashMap < String , Version > > {
227+ let mut upgrades = HashMap :: new ( ) ;
228+
229+ if !opts. breaking {
230+ return Ok ( upgrades) ;
231+ }
232+
233+ // Updates often require a lot of modifications to the registry, so ensure
234+ // that we're synchronized against other Cargos.
235+ let _lock = ws
236+ . gctx ( )
237+ . acquire_package_cache_lock ( CacheLockMode :: DownloadExclusive ) ?;
238+
239+ let mut registry = PackageRegistry :: new ( opts. gctx ) ?;
240+ registry. lock_patches ( ) ;
241+
242+ for member in ws. members_mut ( ) {
243+ debug ! ( "updating manifest for {}" , member. name( ) ) ;
244+
245+ let new_summary = member. manifest ( ) . summary ( ) . clone ( ) . try_map_dependencies (
246+ |dependency| -> CargoResult < _ > {
247+ if let OptVersionReq :: Req ( current) = dependency. version_req ( ) {
248+ let query = crate :: core:: dependency:: Dependency :: parse (
249+ dependency. package_name ( ) ,
250+ None ,
251+ dependency. source_id ( ) . clone ( ) ,
252+ ) ?;
253+
254+ let possibilities = {
255+ loop {
256+ match registry. query_vec ( & query, QueryKind :: Exact ) {
257+ std:: task:: Poll :: Ready ( res) => {
258+ break res?;
259+ }
260+ std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
261+ }
262+ }
263+ } ;
264+
265+ let latest = if !possibilities. is_empty ( ) {
266+ possibilities
267+ . iter ( )
268+ . map ( |s| s. as_summary ( ) )
269+ . map ( |s| s. version ( ) . clone ( ) )
270+ . max ( )
271+ } else {
272+ None
273+ } ;
274+
275+ if let Some ( latest) = latest. clone ( ) {
276+ if !current. matches ( & latest) {
277+ debug ! (
278+ "upgrading {} from {} to {}" ,
279+ dependency. package_name( ) ,
280+ current,
281+ latest
282+ ) ;
283+
284+ opts. gctx . shell ( ) . status_with_color (
285+ "Upgrading" ,
286+ format ! (
287+ "{} {} -> v{}" ,
288+ dependency. package_name( ) ,
289+ current,
290+ latest. to_string( )
291+ ) ,
292+ & style:: GOOD ,
293+ ) ?;
294+
295+ upgrades. insert ( dependency. package_name ( ) . to_string ( ) , latest. clone ( ) ) ;
296+
297+ let req = OptVersionReq :: Req ( VersionReq :: parse ( & latest. to_string ( ) ) ?) ;
298+ let mut dep = dependency. clone ( ) ;
299+ dep. set_version_req ( req) ;
300+ return Ok ( dep) ;
301+ }
302+ }
303+ }
304+
305+ Ok ( dependency)
306+ } ,
307+ ) ?;
308+
309+ let summary = member. manifest_mut ( ) . summary_mut ( ) ;
310+ * summary = new_summary;
311+ }
312+
313+ Ok ( upgrades)
314+ }
315+
316+ pub fn write_manifests (
317+ ws : & Workspace < ' _ > ,
318+ opts : & UpdateOptions < ' _ > ,
319+ upgrades : & HashMap < String , Version > ,
320+ ) -> CargoResult < ( ) > {
321+ if !opts. breaking {
322+ return Ok ( ( ) ) ;
323+ }
324+
325+ for member in ws. members ( ) {
326+ debug ! ( "writing manifest for {}" , member. name( ) ) ;
327+
328+ let manifest_path = member. manifest_path ( ) ;
329+
330+ let mut local_manifest = LocalManifest :: try_new ( & manifest_path) ?;
331+ for dep_table in local_manifest. get_dependency_tables_mut ( ) {
332+ for ( dep_key, dep_item) in dep_table. iter_mut ( ) {
333+ debug ! ( "updating dependency {}" , dep_key) ;
334+
335+ let dep_key = dep_key. get ( ) ;
336+ let dependency = match crate :: util:: toml_mut:: dependency:: Dependency :: from_toml (
337+ & manifest_path,
338+ dep_key,
339+ dep_item,
340+ ) {
341+ Ok ( dependency) => dependency,
342+ Err ( err) => {
343+ opts. gctx
344+ . shell ( )
345+ . warn ( & format ! ( "ignoring {dep_key}, unsupported entry: {err}" ) ) ?;
346+ continue ;
347+ }
348+ } ;
349+
350+ if let crate :: util:: toml_mut:: dependency:: MaybeWorkspace :: Other ( _) =
351+ dependency. source_id ( opts. gctx ) ?
352+ {
353+ if let Some ( latest) = upgrades. get ( & dependency. name ) {
354+ let mut dep =
355+ crate :: util:: toml_mut:: dependency:: Dependency :: new ( & dependency. name ) ;
356+ dep. source = Some ( Source :: Registry ( RegistrySource {
357+ version : latest. to_string ( ) ,
358+ } ) ) ;
359+
360+ * dep_item = dep. to_toml ( manifest_path) ;
361+ }
362+ }
363+ }
364+ }
365+
366+ if opts. dry_run {
367+ opts. gctx
368+ . shell ( )
369+ . warn ( "not updating manifest due to dry run" ) ?;
370+ } else {
371+ debug ! ( "writing updated manifest to {}" , manifest_path. display( ) ) ;
372+ local_manifest. write ( ) ?;
373+ }
374+ }
375+
376+ Ok ( ( ) )
377+ }
378+
210379fn print_lockfile_generation (
211380 ws : & Workspace < ' _ > ,
212381 resolve : & Resolve ,
0 commit comments