@@ -25,6 +25,9 @@ use crate::util::{FileLock, Filesystem, Freshness};
2525///
2626/// This maintains a filesystem lock, preventing other instances of Cargo from
2727/// modifying at the same time. Drop the value to unlock.
28+ ///
29+ /// If/when v2 is stabilized, it is intended that v1 is retained for a while
30+ /// during a longish transition period, and then v1 can be removed.
2831pub struct InstallTracker {
2932 v1 : CrateListingV1 ,
3033 v2 : CrateListingV2 ,
@@ -33,6 +36,10 @@ pub struct InstallTracker {
3336 unstable_upgrade : bool ,
3437}
3538
39+ /// Tracking information for the set of installed packages.
40+ ///
41+ /// This v2 format is unstable and requires the `-Z unstable-upgrade` option
42+ /// to enable.
3643#[ derive( Default , Deserialize , Serialize ) ]
3744struct CrateListingV2 {
3845 installs : BTreeMap < PackageId , InstallInfo > ,
@@ -41,6 +48,14 @@ struct CrateListingV2 {
4148 other : BTreeMap < String , serde_json:: Value > ,
4249}
4350
51+ /// Tracking information for the installation of a single package.
52+ ///
53+ /// This tracks the settings that were used when the package was installed.
54+ /// Future attempts to install the same package will check these settings to
55+ /// determine if it needs to be rebuilt/reinstalled. If nothing has changed,
56+ /// then Cargo will inform the user that it is "up to date".
57+ ///
58+ /// This is only used for the (unstable) v2 format.
4459#[ derive( Debug , Deserialize , Serialize ) ]
4560struct InstallInfo {
4661 /// Version requested via `--version`.
@@ -68,6 +83,7 @@ struct InstallInfo {
6883 other : BTreeMap < String , serde_json:: Value > ,
6984}
7085
86+ /// Tracking information for the set of installed packages.
7187#[ derive( Default , Deserialize , Serialize ) ]
7288pub struct CrateListingV1 {
7389 v1 : BTreeMap < PackageId , BTreeSet < String > > ,
@@ -102,23 +118,20 @@ impl InstallTracker {
102118 } ) ?;
103119
104120 let v2 = ( || -> CargoResult < _ > {
105- if unstable_upgrade {
106- let mut contents = String :: new ( ) ;
107- v2_lock
108- . as_ref ( )
109- . unwrap ( )
110- . file ( )
111- . read_to_string ( & mut contents) ?;
112- let mut v2 = if contents. is_empty ( ) {
113- CrateListingV2 :: default ( )
114- } else {
115- serde_json:: from_str ( & contents)
116- . chain_err ( || format_err ! ( "invalid JSON found for metadata" ) ) ?
117- } ;
118- v2. sync_v1 ( & v1) ?;
119- Ok ( v2)
120- } else {
121- Ok ( CrateListingV2 :: default ( ) )
121+ match & v2_lock {
122+ Some ( lock) => {
123+ let mut contents = String :: new ( ) ;
124+ lock. file ( ) . read_to_string ( & mut contents) ?;
125+ let mut v2 = if contents. is_empty ( ) {
126+ CrateListingV2 :: default ( )
127+ } else {
128+ serde_json:: from_str ( & contents)
129+ . chain_err ( || format_err ! ( "invalid JSON found for metadata" ) ) ?
130+ } ;
131+ v2. sync_v1 ( & v1) ?;
132+ Ok ( v2)
133+ }
134+ None => Ok ( CrateListingV2 :: default ( ) ) ,
122135 }
123136 } ) ( )
124137 . chain_err ( || {
@@ -142,8 +155,14 @@ impl InstallTracker {
142155 ///
143156 /// Returns a tuple `(freshness, map)`. `freshness` indicates if the
144157 /// package should be built (`Dirty`) or if it is already up-to-date
145- /// (`Fresh`). The map maps binary names to the PackageId that installed
146- /// it (which is None if not known).
158+ /// (`Fresh`) and should be skipped. The map maps binary names to the
159+ /// PackageId that installed it (which is None if not known).
160+ ///
161+ /// If there are no duplicates, then it will be considered `Dirty` (i.e.,
162+ /// it is OK to build/install).
163+ ///
164+ /// `force=true` will always be considered `Dirty` (i.e., it will always
165+ /// be rebuilt/reinstalled).
147166 ///
148167 /// Returns an error if there is a duplicate and `--force` is not used.
149168 pub fn check_upgrade (
@@ -156,12 +175,17 @@ impl InstallTracker {
156175 _rustc : & str ,
157176 ) -> CargoResult < ( Freshness , BTreeMap < String , Option < PackageId > > ) > {
158177 let exes = exe_names ( pkg, & opts. filter ) ;
178+ // Check if any tracked exe's are already installed.
159179 let duplicates = self . find_duplicates ( dst, & exes) ;
160180 if force || duplicates. is_empty ( ) {
161181 return Ok ( ( Freshness :: Dirty , duplicates) ) ;
162182 }
163- // If any duplicates are not tracked, then --force is required.
164- // If any duplicates are from a package with a different name, --force is required.
183+ // Check if all duplicates come from packages of the same name. If
184+ // there are duplicates from other packages, then --force will be
185+ // required.
186+ //
187+ // There may be multiple matching duplicates if different versions of
188+ // the same package installed different binaries.
165189 let matching_duplicates: Vec < PackageId > = duplicates
166190 . values ( )
167191 . filter_map ( |v| match v {
@@ -170,9 +194,13 @@ impl InstallTracker {
170194 } )
171195 . collect ( ) ;
172196
197+ // If both sets are the same length, that means all duplicates come
198+ // from packages with the same name.
173199 if self . unstable_upgrade && matching_duplicates. len ( ) == duplicates. len ( ) {
200+ // Determine if it is dirty or fresh.
174201 let source_id = pkg. package_id ( ) . source_id ( ) ;
175202 if source_id. is_path ( ) {
203+ // `cargo install --path ...` is always rebuilt.
176204 return Ok ( ( Freshness :: Dirty , duplicates) ) ;
177205 }
178206 if matching_duplicates. iter ( ) . all ( |dupe_pkg_id| {
@@ -182,6 +210,8 @@ impl InstallTracker {
182210 . get ( dupe_pkg_id)
183211 . expect ( "dupes must be in sync" ) ;
184212 let precise_equal = if source_id. is_git ( ) {
213+ // Git sources must have the exact same hash to be
214+ // considered "fresh".
185215 dupe_pkg_id. source_id ( ) . precise ( ) == source_id. precise ( )
186216 } else {
187217 true
@@ -190,13 +220,7 @@ impl InstallTracker {
190220 dupe_pkg_id. version ( ) == pkg. version ( )
191221 && dupe_pkg_id. source_id ( ) == source_id
192222 && precise_equal
193- && info. features == feature_set ( & opts. features )
194- && info. all_features == opts. all_features
195- && info. no_default_features == opts. no_default_features
196- && info. profile == profile_name ( opts. build_config . release )
197- && ( info. target . is_none ( )
198- || info. target . as_ref ( ) . map ( |t| t. as_ref ( ) ) == Some ( target) )
199- && info. bins == exes
223+ && info. is_up_to_date ( opts, target, & exes)
200224 } ) {
201225 Ok ( ( Freshness :: Fresh , duplicates) )
202226 } else {
@@ -218,6 +242,11 @@ impl InstallTracker {
218242 }
219243 }
220244
245+ /// Check if any executables are already installed.
246+ ///
247+ /// Returns a map of duplicates, the key is the executable name and the
248+ /// value is the PackageId that is already installed. The PackageId is
249+ /// None if it is an untracked executable.
221250 fn find_duplicates (
222251 & self ,
223252 dst : & Path ,
@@ -312,7 +341,7 @@ impl CrateListingV1 {
312341 other_bins. remove ( bin) ;
313342 }
314343 }
315- // Remove empty metadata lines. If only BTreeMap had `retain` .
344+ // Remove entries where `bins` is empty .
316345 let to_remove = self
317346 . v1
318347 . iter ( )
@@ -322,11 +351,10 @@ impl CrateListingV1 {
322351 self . v1 . remove ( p) ;
323352 }
324353 // Add these bins.
325- let mut bins = bins. clone ( ) ;
326354 self . v1
327355 . entry ( pkg. package_id ( ) )
328- . and_modify ( |set| set . append ( & mut bins ) )
329- . or_insert ( bins) ;
356+ . or_insert_with ( BTreeSet :: new )
357+ . append ( & mut bins. clone ( ) ) ;
330358 }
331359
332360 fn remove ( & mut self , pkg_id : PackageId , bins : & BTreeSet < String > ) {
@@ -354,6 +382,11 @@ impl CrateListingV1 {
354382}
355383
356384impl CrateListingV2 {
385+ /// Incorporate any changes from v1 into self.
386+ /// This handles the initial upgrade to v2, *and* handles the case
387+ /// where v2 is in use, and a v1 update is made, then v2 is used again.
388+ /// i.e., `cargo +new install foo ; cargo +old install bar ; cargo +new install bar`
389+ /// For now, v1 is the source of truth, so its values are trusted over v2.
357390 fn sync_v1 ( & mut self , v1 : & CrateListingV1 ) -> CargoResult < ( ) > {
358391 // Make the `bins` entries the same.
359392 for ( pkg_id, bins) in & v1. v1 {
@@ -397,7 +430,7 @@ impl CrateListingV2 {
397430 info. bins . remove ( bin) ;
398431 }
399432 }
400- // Remove empty metadata lines. If only BTreeMap had `retain` .
433+ // Remove entries where `bins` is empty .
401434 let to_remove = self
402435 . installs
403436 . iter ( )
@@ -408,9 +441,7 @@ impl CrateListingV2 {
408441 }
409442 // Add these bins.
410443 if let Some ( info) = self . installs . get_mut ( & pkg. package_id ( ) ) {
411- for bin in bins {
412- info. bins . remove ( bin) ;
413- }
444+ info. bins . append ( & mut bins. clone ( ) ) ;
414445 info. version_req = version_req;
415446 info. features = feature_set ( & opts. features ) ;
416447 info. all_features = opts. all_features ;
@@ -454,7 +485,8 @@ impl CrateListingV2 {
454485 let mut file = lock. file ( ) ;
455486 file. seek ( SeekFrom :: Start ( 0 ) ) ?;
456487 file. set_len ( 0 ) ?;
457- serde_json:: to_writer ( file, self ) ?;
488+ let data = serde_json:: to_string ( self ) ?;
489+ file. write_all ( data. as_bytes ( ) ) ?;
458490 Ok ( ( ) )
459491 }
460492}
@@ -473,6 +505,23 @@ impl InstallInfo {
473505 other : BTreeMap :: new ( ) ,
474506 }
475507 }
508+
509+ /// Determine if this installation is "up to date", or if it needs to be reinstalled.
510+ ///
511+ /// This does not do Package/Source/Version checking.
512+ fn is_up_to_date (
513+ & self ,
514+ opts : & CompileOptions < ' _ > ,
515+ target : & str ,
516+ exes : & BTreeSet < String > ,
517+ ) -> bool {
518+ self . features == feature_set ( & opts. features )
519+ && self . all_features == opts. all_features
520+ && self . no_default_features == opts. no_default_features
521+ && self . profile == profile_name ( opts. build_config . release )
522+ && ( self . target . is_none ( ) || self . target . as_ref ( ) . map ( |t| t. as_ref ( ) ) == Some ( target) )
523+ && & self . bins == exes
524+ }
476525}
477526
478527/// Determines the root directory where installation is done.
0 commit comments