66//! directory unless the `--all` flag is present.
77
88use std:: fs;
9+ use std:: io:: { self , ErrorKind } ;
910use std:: path:: Path ;
1011
1112use crate :: core:: builder:: { crate_description, Builder , RunConfig , ShouldRun , Step } ;
@@ -100,11 +101,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
100101 return ;
101102 }
102103
103- remove_dir_recursive ( "tmp" ) ;
104+ rm_rf ( "tmp" . as_ref ( ) ) ;
104105
105106 // Clean the entire build directory
106107 if all {
107- remove_dir_recursive ( & build. out ) ;
108+ rm_rf ( & build. out ) ;
108109 return ;
109110 }
110111
@@ -135,17 +136,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
135136 }
136137
137138 let path = t ! ( entry. path( ) . canonicalize( ) ) ;
138- remove_dir_recursive ( & path) ;
139+ rm_rf ( & path) ;
139140 }
140141 }
141142}
142143
143144fn 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" ) ) ;
149150
150151 let mut hosts: Vec < _ > = build. hosts . iter ( ) . map ( |t| build. out . join ( t) ) . collect ( ) ;
151152 // After cross-compilation, artifacts of the host architecture (which may differ from build.host)
@@ -165,16 +166,78 @@ fn clean_default(build: &Build) {
165166 continue ;
166167 }
167168 let path = t ! ( entry. path( ) . canonicalize( ) ) ;
168- remove_dir_recursive ( & path) ;
169+ rm_rf ( & path) ;
169170 }
170171 }
171172}
172173
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+ }
179242 }
180243}
0 commit comments