@@ -5,7 +5,7 @@ use crate::ops;
5
5
use crate :: util:: errors:: CargoResult ;
6
6
use crate :: util:: interning:: InternedString ;
7
7
use crate :: util:: lev_distance;
8
- use crate :: util:: Config ;
8
+ use crate :: util:: { Config , Progress , ProgressStyle } ;
9
9
10
10
use anyhow:: Context as _;
11
11
use cargo_util:: paths;
@@ -34,7 +34,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
34
34
// If the doc option is set, we just want to delete the doc directory.
35
35
if opts. doc {
36
36
target_dir = target_dir. join ( "doc" ) ;
37
- return rm_rf ( & target_dir. into_path_unlocked ( ) , config) ;
37
+ return clean_entire_folder ( & target_dir. into_path_unlocked ( ) , config) ;
38
38
}
39
39
40
40
let profiles = Profiles :: new ( ws, opts. requested_profile ) ?;
@@ -53,7 +53,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
53
53
// Note that we don't bother grabbing a lock here as we're just going to
54
54
// blow it all away anyway.
55
55
if opts. spec . is_empty ( ) {
56
- return rm_rf ( & target_dir. into_path_unlocked ( ) , config) ;
56
+ return clean_entire_folder ( & target_dir. into_path_unlocked ( ) , config) ;
57
57
}
58
58
59
59
// Clean specific packages.
@@ -133,21 +133,23 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
133
133
}
134
134
let packages = pkg_set. get_many ( pkg_ids) ?;
135
135
136
+ let mut progress = CleaningPackagesBar :: new ( config, packages. len ( ) ) ;
136
137
for pkg in packages {
137
138
let pkg_dir = format ! ( "{}-*" , pkg. name( ) ) ;
139
+ progress. on_cleaning_package ( & pkg. name ( ) ) ?;
138
140
139
141
// Clean fingerprints.
140
142
for ( _, layout) in & layouts_with_host {
141
143
let dir = escape_glob_path ( layout. fingerprint ( ) ) ?;
142
- rm_rf_glob ( & Path :: new ( & dir) . join ( & pkg_dir) , config) ?;
144
+ rm_rf_glob ( & Path :: new ( & dir) . join ( & pkg_dir) , config, & mut progress ) ?;
143
145
}
144
146
145
147
for target in pkg. targets ( ) {
146
148
if target. is_custom_build ( ) {
147
149
// Get both the build_script_build and the output directory.
148
150
for ( _, layout) in & layouts_with_host {
149
151
let dir = escape_glob_path ( layout. build ( ) ) ?;
150
- rm_rf_glob ( & Path :: new ( & dir) . join ( & pkg_dir) , config) ?;
152
+ rm_rf_glob ( & Path :: new ( & dir) . join ( & pkg_dir) , config, & mut progress ) ?;
151
153
}
152
154
continue ;
153
155
}
@@ -178,33 +180,33 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
178
180
let dir_glob = escape_glob_path ( dir) ?;
179
181
let dir_glob = Path :: new ( & dir_glob) ;
180
182
181
- rm_rf_glob ( & dir_glob. join ( & hashed_name) , config) ?;
182
- rm_rf ( & dir. join ( & unhashed_name) , config) ?;
183
+ rm_rf_glob ( & dir_glob. join ( & hashed_name) , config, & mut progress ) ?;
184
+ rm_rf ( & dir. join ( & unhashed_name) , config, & mut progress ) ?;
183
185
// Remove dep-info file generated by rustc. It is not tracked in
184
186
// file_types. It does not have a prefix.
185
187
let hashed_dep_info = dir_glob. join ( format ! ( "{}-*.d" , crate_name) ) ;
186
- rm_rf_glob ( & hashed_dep_info, config) ?;
188
+ rm_rf_glob ( & hashed_dep_info, config, & mut progress ) ?;
187
189
let unhashed_dep_info = dir. join ( format ! ( "{}.d" , crate_name) ) ;
188
- rm_rf ( & unhashed_dep_info, config) ?;
190
+ rm_rf ( & unhashed_dep_info, config, & mut progress ) ?;
189
191
// Remove split-debuginfo files generated by rustc.
190
192
let split_debuginfo_obj = dir_glob. join ( format ! ( "{}.*.o" , crate_name) ) ;
191
- rm_rf_glob ( & split_debuginfo_obj, config) ?;
193
+ rm_rf_glob ( & split_debuginfo_obj, config, & mut progress ) ?;
192
194
let split_debuginfo_dwo = dir_glob. join ( format ! ( "{}.*.dwo" , crate_name) ) ;
193
- rm_rf_glob ( & split_debuginfo_dwo, config) ?;
195
+ rm_rf_glob ( & split_debuginfo_dwo, config, & mut progress ) ?;
194
196
195
197
// Remove the uplifted copy.
196
198
if let Some ( uplift_dir) = uplift_dir {
197
199
let uplifted_path = uplift_dir. join ( file_type. uplift_filename ( target) ) ;
198
- rm_rf ( & uplifted_path, config) ?;
200
+ rm_rf ( & uplifted_path, config, & mut progress ) ?;
199
201
// Dep-info generated by Cargo itself.
200
202
let dep_info = uplifted_path. with_extension ( "d" ) ;
201
- rm_rf ( & dep_info, config) ?;
203
+ rm_rf ( & dep_info, config, & mut progress ) ?;
202
204
}
203
205
}
204
206
// TODO: what to do about build_script_build?
205
207
let dir = escape_glob_path ( layout. incremental ( ) ) ?;
206
208
let incremental = Path :: new ( & dir) . join ( format ! ( "{}-*" , crate_name) ) ;
207
- rm_rf_glob ( & incremental, config) ?;
209
+ rm_rf_glob ( & incremental, config, & mut progress ) ?;
208
210
}
209
211
}
210
212
}
@@ -220,29 +222,134 @@ fn escape_glob_path(pattern: &Path) -> CargoResult<String> {
220
222
Ok ( glob:: Pattern :: escape ( pattern) )
221
223
}
222
224
223
- fn rm_rf_glob ( pattern : & Path , config : & Config ) -> CargoResult < ( ) > {
225
+ fn rm_rf_glob (
226
+ pattern : & Path ,
227
+ config : & Config ,
228
+ progress : & mut dyn CleaningProgressBar ,
229
+ ) -> CargoResult < ( ) > {
224
230
// TODO: Display utf8 warning to user? Or switch to globset?
225
231
let pattern = pattern
226
232
. to_str ( )
227
233
. ok_or_else ( || anyhow:: anyhow!( "expected utf-8 path" ) ) ?;
228
234
for path in glob:: glob ( pattern) ? {
229
- rm_rf ( & path?, config) ?;
235
+ rm_rf ( & path?, config, progress ) ?;
230
236
}
231
237
Ok ( ( ) )
232
238
}
233
239
234
- fn rm_rf ( path : & Path , config : & Config ) -> CargoResult < ( ) > {
235
- let m = fs:: symlink_metadata ( path) ;
236
- if m. as_ref ( ) . map ( |s| s. is_dir ( ) ) . unwrap_or ( false ) {
237
- config
238
- . shell ( )
239
- . verbose ( |shell| shell. status ( "Removing" , path. display ( ) ) ) ?;
240
- paths:: remove_dir_all ( path) . with_context ( || "could not remove build directory" ) ?;
241
- } else if m. is_ok ( ) {
242
- config
243
- . shell ( )
244
- . verbose ( |shell| shell. status ( "Removing" , path. display ( ) ) ) ?;
245
- paths:: remove_file ( path) . with_context ( || "failed to remove build artifact" ) ?;
240
+ fn rm_rf ( path : & Path , config : & Config , progress : & mut dyn CleaningProgressBar ) -> CargoResult < ( ) > {
241
+ if fs:: symlink_metadata ( path) . is_err ( ) {
242
+ return Ok ( ( ) ) ;
246
243
}
244
+
245
+ config
246
+ . shell ( )
247
+ . verbose ( |shell| shell. status ( "Removing" , path. display ( ) ) ) ?;
248
+ progress. display_now ( ) ?;
249
+
250
+ for entry in walkdir:: WalkDir :: new ( path) . contents_first ( true ) {
251
+ let entry = entry?;
252
+ progress. on_clean ( ) ?;
253
+ if entry. file_type ( ) . is_dir ( ) {
254
+ paths:: remove_dir ( entry. path ( ) ) . with_context ( || "could not remove build directory" ) ?;
255
+ } else {
256
+ paths:: remove_file ( entry. path ( ) ) . with_context ( || "failed to remove build artifact" ) ?;
257
+ }
258
+ }
259
+
247
260
Ok ( ( ) )
248
261
}
262
+
263
+ fn clean_entire_folder ( path : & Path , config : & Config ) -> CargoResult < ( ) > {
264
+ let num_paths = walkdir:: WalkDir :: new ( path) . into_iter ( ) . count ( ) ;
265
+ let mut progress = CleaningFolderBar :: new ( config, num_paths) ;
266
+ rm_rf ( path, config, & mut progress)
267
+ }
268
+
269
+ trait CleaningProgressBar {
270
+ fn display_now ( & mut self ) -> CargoResult < ( ) > ;
271
+ fn on_clean ( & mut self ) -> CargoResult < ( ) > ;
272
+ }
273
+
274
+ struct CleaningFolderBar < ' cfg > {
275
+ bar : Progress < ' cfg > ,
276
+ max : usize ,
277
+ cur : usize ,
278
+ }
279
+
280
+ impl < ' cfg > CleaningFolderBar < ' cfg > {
281
+ fn new ( cfg : & ' cfg Config , max : usize ) -> Self {
282
+ Self {
283
+ bar : Progress :: with_style ( "Cleaning" , ProgressStyle :: Percentage , cfg) ,
284
+ max,
285
+ cur : 0 ,
286
+ }
287
+ }
288
+
289
+ fn cur_progress ( & self ) -> usize {
290
+ std:: cmp:: min ( self . cur , self . max )
291
+ }
292
+ }
293
+
294
+ impl < ' cfg > CleaningProgressBar for CleaningFolderBar < ' cfg > {
295
+ fn display_now ( & mut self ) -> CargoResult < ( ) > {
296
+ self . bar . tick_now ( self . cur_progress ( ) , self . max , "" )
297
+ }
298
+
299
+ fn on_clean ( & mut self ) -> CargoResult < ( ) > {
300
+ self . cur += 1 ;
301
+ self . bar . tick ( self . cur_progress ( ) , self . max , "" )
302
+ }
303
+ }
304
+
305
+ struct CleaningPackagesBar < ' cfg > {
306
+ bar : Progress < ' cfg > ,
307
+ max : usize ,
308
+ cur : usize ,
309
+ num_files_folders_cleaned : usize ,
310
+ package_being_cleaned : String ,
311
+ }
312
+
313
+ impl < ' cfg > CleaningPackagesBar < ' cfg > {
314
+ fn new ( cfg : & ' cfg Config , max : usize ) -> Self {
315
+ Self {
316
+ bar : Progress :: with_style ( "Cleaning" , ProgressStyle :: Ratio , cfg) ,
317
+ max,
318
+ cur : 0 ,
319
+ num_files_folders_cleaned : 0 ,
320
+ package_being_cleaned : String :: new ( ) ,
321
+ }
322
+ }
323
+
324
+ fn on_cleaning_package ( & mut self , package : & str ) -> CargoResult < ( ) > {
325
+ self . cur += 1 ;
326
+ self . package_being_cleaned = String :: from ( package) ;
327
+ self . bar
328
+ . tick ( self . cur_progress ( ) , self . max , & self . format_message ( ) )
329
+ }
330
+
331
+ fn cur_progress ( & self ) -> usize {
332
+ std:: cmp:: min ( self . cur , self . max )
333
+ }
334
+
335
+ fn format_message ( & self ) -> String {
336
+ format ! (
337
+ ": {}, {} files/folders cleaned" ,
338
+ self . package_being_cleaned, self . num_files_folders_cleaned
339
+ )
340
+ }
341
+ }
342
+
343
+ impl < ' cfg > CleaningProgressBar for CleaningPackagesBar < ' cfg > {
344
+ fn display_now ( & mut self ) -> CargoResult < ( ) > {
345
+ self . bar
346
+ . tick_now ( self . cur_progress ( ) , self . max , & self . format_message ( ) )
347
+ }
348
+
349
+ fn on_clean ( & mut self ) -> CargoResult < ( ) > {
350
+ self . bar
351
+ . tick ( self . cur_progress ( ) , self . max , & self . format_message ( ) ) ?;
352
+ self . num_files_folders_cleaned += 1 ;
353
+ Ok ( ( ) )
354
+ }
355
+ }
0 commit comments