@@ -25,6 +25,9 @@ use crate::util::{FileLock, Filesystem, Freshness};
25
25
///
26
26
/// This maintains a filesystem lock, preventing other instances of Cargo from
27
27
/// 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.
28
31
pub struct InstallTracker {
29
32
v1 : CrateListingV1 ,
30
33
v2 : CrateListingV2 ,
@@ -33,6 +36,10 @@ pub struct InstallTracker {
33
36
unstable_upgrade : bool ,
34
37
}
35
38
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.
36
43
#[ derive( Default , Deserialize , Serialize ) ]
37
44
struct CrateListingV2 {
38
45
installs : BTreeMap < PackageId , InstallInfo > ,
@@ -41,6 +48,14 @@ struct CrateListingV2 {
41
48
other : BTreeMap < String , serde_json:: Value > ,
42
49
}
43
50
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.
44
59
#[ derive( Debug , Deserialize , Serialize ) ]
45
60
struct InstallInfo {
46
61
/// Version requested via `--version`.
@@ -68,6 +83,7 @@ struct InstallInfo {
68
83
other : BTreeMap < String , serde_json:: Value > ,
69
84
}
70
85
86
+ /// Tracking information for the set of installed packages.
71
87
#[ derive( Default , Deserialize , Serialize ) ]
72
88
pub struct CrateListingV1 {
73
89
v1 : BTreeMap < PackageId , BTreeSet < String > > ,
@@ -102,23 +118,20 @@ impl InstallTracker {
102
118
} ) ?;
103
119
104
120
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 ( ) ) ,
122
135
}
123
136
} ) ( )
124
137
. chain_err ( || {
@@ -142,8 +155,14 @@ impl InstallTracker {
142
155
///
143
156
/// Returns a tuple `(freshness, map)`. `freshness` indicates if the
144
157
/// 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).
147
166
///
148
167
/// Returns an error if there is a duplicate and `--force` is not used.
149
168
pub fn check_upgrade (
@@ -156,12 +175,17 @@ impl InstallTracker {
156
175
_rustc : & str ,
157
176
) -> CargoResult < ( Freshness , BTreeMap < String , Option < PackageId > > ) > {
158
177
let exes = exe_names ( pkg, & opts. filter ) ;
178
+ // Check if any tracked exe's are already installed.
159
179
let duplicates = self . find_duplicates ( dst, & exes) ;
160
180
if force || duplicates. is_empty ( ) {
161
181
return Ok ( ( Freshness :: Dirty , duplicates) ) ;
162
182
}
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.
165
189
let matching_duplicates: Vec < PackageId > = duplicates
166
190
. values ( )
167
191
. filter_map ( |v| match v {
@@ -170,9 +194,13 @@ impl InstallTracker {
170
194
} )
171
195
. collect ( ) ;
172
196
197
+ // If both sets are the same length, that means all duplicates come
198
+ // from packages with the same name.
173
199
if self . unstable_upgrade && matching_duplicates. len ( ) == duplicates. len ( ) {
200
+ // Determine if it is dirty or fresh.
174
201
let source_id = pkg. package_id ( ) . source_id ( ) ;
175
202
if source_id. is_path ( ) {
203
+ // `cargo install --path ...` is always rebuilt.
176
204
return Ok ( ( Freshness :: Dirty , duplicates) ) ;
177
205
}
178
206
if matching_duplicates. iter ( ) . all ( |dupe_pkg_id| {
@@ -182,6 +210,8 @@ impl InstallTracker {
182
210
. get ( dupe_pkg_id)
183
211
. expect ( "dupes must be in sync" ) ;
184
212
let precise_equal = if source_id. is_git ( ) {
213
+ // Git sources must have the exact same hash to be
214
+ // considered "fresh".
185
215
dupe_pkg_id. source_id ( ) . precise ( ) == source_id. precise ( )
186
216
} else {
187
217
true
@@ -190,13 +220,7 @@ impl InstallTracker {
190
220
dupe_pkg_id. version ( ) == pkg. version ( )
191
221
&& dupe_pkg_id. source_id ( ) == source_id
192
222
&& 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)
200
224
} ) {
201
225
Ok ( ( Freshness :: Fresh , duplicates) )
202
226
} else {
@@ -218,6 +242,11 @@ impl InstallTracker {
218
242
}
219
243
}
220
244
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.
221
250
fn find_duplicates (
222
251
& self ,
223
252
dst : & Path ,
@@ -312,7 +341,7 @@ impl CrateListingV1 {
312
341
other_bins. remove ( bin) ;
313
342
}
314
343
}
315
- // Remove empty metadata lines. If only BTreeMap had `retain` .
344
+ // Remove entries where `bins` is empty .
316
345
let to_remove = self
317
346
. v1
318
347
. iter ( )
@@ -322,11 +351,10 @@ impl CrateListingV1 {
322
351
self . v1 . remove ( p) ;
323
352
}
324
353
// Add these bins.
325
- let mut bins = bins. clone ( ) ;
326
354
self . v1
327
355
. 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 ( ) ) ;
330
358
}
331
359
332
360
fn remove ( & mut self , pkg_id : PackageId , bins : & BTreeSet < String > ) {
@@ -354,6 +382,11 @@ impl CrateListingV1 {
354
382
}
355
383
356
384
impl 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.
357
390
fn sync_v1 ( & mut self , v1 : & CrateListingV1 ) -> CargoResult < ( ) > {
358
391
// Make the `bins` entries the same.
359
392
for ( pkg_id, bins) in & v1. v1 {
@@ -397,7 +430,7 @@ impl CrateListingV2 {
397
430
info. bins . remove ( bin) ;
398
431
}
399
432
}
400
- // Remove empty metadata lines. If only BTreeMap had `retain` .
433
+ // Remove entries where `bins` is empty .
401
434
let to_remove = self
402
435
. installs
403
436
. iter ( )
@@ -408,9 +441,7 @@ impl CrateListingV2 {
408
441
}
409
442
// Add these bins.
410
443
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 ( ) ) ;
414
445
info. version_req = version_req;
415
446
info. features = feature_set ( & opts. features ) ;
416
447
info. all_features = opts. all_features ;
@@ -454,7 +485,8 @@ impl CrateListingV2 {
454
485
let mut file = lock. file ( ) ;
455
486
file. seek ( SeekFrom :: Start ( 0 ) ) ?;
456
487
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 ( ) ) ?;
458
490
Ok ( ( ) )
459
491
}
460
492
}
@@ -473,6 +505,23 @@ impl InstallInfo {
473
505
other : BTreeMap :: new ( ) ,
474
506
}
475
507
}
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
+ }
476
525
}
477
526
478
527
/// Determines the root directory where installation is done.
0 commit comments