6
6
//! directory unless the `--all` flag is present.
7
7
8
8
use std:: fs;
9
+ use std:: io:: { self , ErrorKind } ;
9
10
use std:: path:: Path ;
10
11
11
12
use crate :: core:: builder:: { crate_description, Builder , RunConfig , ShouldRun , Step } ;
@@ -100,11 +101,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
100
101
return ;
101
102
}
102
103
103
- remove_dir_recursive ( "tmp" ) ;
104
+ rm_rf ( "tmp" . as_ref ( ) ) ;
104
105
105
106
// Clean the entire build directory
106
107
if all {
107
- remove_dir_recursive ( & build. out ) ;
108
+ rm_rf ( & build. out ) ;
108
109
return ;
109
110
}
110
111
@@ -135,17 +136,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
135
136
}
136
137
137
138
let path = t ! ( entry. path( ) . canonicalize( ) ) ;
138
- remove_dir_recursive ( & path) ;
139
+ rm_rf ( & path) ;
139
140
}
140
141
}
141
142
}
142
143
143
144
fn clean_default ( build : & Build ) {
144
- remove_dir_recursive ( build. out . join ( "tmp" ) ) ;
145
- remove_dir_recursive ( build. out . join ( "dist" ) ) ;
146
- remove_dir_recursive ( build. out . join ( "bootstrap" ) . join ( ".last-warned-change-id" ) ) ;
147
- remove_dir_recursive ( build. out . join ( "bootstrap-shims-dump" ) ) ;
148
- remove_dir_recursive ( build. out . join ( "rustfmt.stamp" ) ) ;
145
+ rm_rf ( & build. out . join ( "tmp" ) ) ;
146
+ rm_rf ( & build. out . join ( "dist" ) ) ;
147
+ rm_rf ( & build. out . join ( "bootstrap" ) . join ( ".last-warned-change-id" ) ) ;
148
+ rm_rf ( & build. out . join ( "bootstrap-shims-dump" ) ) ;
149
+ rm_rf ( & build. out . join ( "rustfmt.stamp" ) ) ;
149
150
150
151
let mut hosts: Vec < _ > = build. hosts . iter ( ) . map ( |t| build. out . join ( t) ) . collect ( ) ;
151
152
// After cross-compilation, artifacts of the host architecture (which may differ from build.host)
@@ -165,16 +166,78 @@ fn clean_default(build: &Build) {
165
166
continue ;
166
167
}
167
168
let path = t ! ( entry. path( ) . canonicalize( ) ) ;
168
- remove_dir_recursive ( & path) ;
169
+ rm_rf ( & path) ;
169
170
}
170
171
}
171
172
}
172
173
173
- /// Wrapper for [`std::fs::remove_dir_all`] that panics on failure and prints the `path` we failed
174
- /// on.
175
- fn remove_dir_recursive < P : AsRef < Path > > ( path : P ) {
176
- let path = path. as_ref ( ) ;
177
- if let Err ( e) = fs:: remove_dir_all ( path) {
178
- panic ! ( "failed to `remove_dir_all` at `{}`: {e}" , path. display( ) ) ;
174
+ fn rm_rf ( path : & Path ) {
175
+ match path. symlink_metadata ( ) {
176
+ Err ( e) => {
177
+ if e. kind ( ) == ErrorKind :: NotFound {
178
+ return ;
179
+ }
180
+ panic ! ( "failed to get metadata for file {}: {}" , path. display( ) , e) ;
181
+ }
182
+ Ok ( metadata) => {
183
+ if metadata. file_type ( ) . is_file ( ) || metadata. file_type ( ) . is_symlink ( ) {
184
+ do_op ( path, "remove file" , |p| match fs:: remove_file ( p) {
185
+ #[ cfg( windows) ]
186
+ Err ( e)
187
+ if e. kind ( ) == std:: io:: ErrorKind :: PermissionDenied
188
+ && p. file_name ( ) . and_then ( std:: ffi:: OsStr :: to_str)
189
+ == Some ( "bootstrap.exe" ) =>
190
+ {
191
+ eprintln ! ( "WARNING: failed to delete '{}'." , p. display( ) ) ;
192
+ Ok ( ( ) )
193
+ }
194
+ r => r,
195
+ } ) ;
196
+
197
+ return ;
198
+ }
199
+
200
+ for file in t ! ( fs:: read_dir( path) ) {
201
+ rm_rf ( & t ! ( file) . path ( ) ) ;
202
+ }
203
+
204
+ do_op ( path, "remove dir" , |p| match fs:: remove_dir ( p) {
205
+ // Check for dir not empty on Windows
206
+ // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
207
+ // match on `e.kind()` instead.
208
+ #[ cfg( windows) ]
209
+ Err ( e) if e. raw_os_error ( ) == Some ( 145 ) => Ok ( ( ) ) ,
210
+ r => r,
211
+ } ) ;
212
+ }
213
+ } ;
214
+ }
215
+
216
+ fn do_op < F > ( path : & Path , desc : & str , mut f : F )
217
+ where
218
+ F : FnMut ( & Path ) -> io:: Result < ( ) > ,
219
+ {
220
+ match f ( path) {
221
+ Ok ( ( ) ) => { }
222
+ // On windows we can't remove a readonly file, and git will often clone files as readonly.
223
+ // As a result, we have some special logic to remove readonly files on windows.
224
+ // This is also the reason that we can't use things like fs::remove_dir_all().
225
+ #[ cfg( windows) ]
226
+ Err ( ref e) if e. kind ( ) == ErrorKind :: PermissionDenied => {
227
+ let m = t ! ( path. symlink_metadata( ) ) ;
228
+ let mut p = m. permissions ( ) ;
229
+ p. set_readonly ( false ) ;
230
+ t ! ( fs:: set_permissions( path, p) ) ;
231
+ f ( path) . unwrap_or_else ( |e| {
232
+ // Delete symlinked directories on Windows
233
+ if m. file_type ( ) . is_symlink ( ) && path. is_dir ( ) && fs:: remove_dir ( path) . is_ok ( ) {
234
+ return ;
235
+ }
236
+ panic ! ( "failed to {} {}: {}" , desc, path. display( ) , e) ;
237
+ } ) ;
238
+ }
239
+ Err ( e) => {
240
+ panic ! ( "failed to {} {}: {}" , desc, path. display( ) , e) ;
241
+ }
179
242
}
180
243
}
0 commit comments