16
16
//! directory as we want that cached between builds.
17
17
18
18
use std:: fs;
19
+ use std:: io:: { self , ErrorKind } ;
19
20
use std:: path:: Path ;
20
21
21
22
use Build ;
@@ -35,14 +36,47 @@ pub fn clean(build: &Build) {
35
36
if entry. file_name ( ) . to_str ( ) == Some ( "llvm" ) {
36
37
continue
37
38
}
38
- t ! ( fs :: remove_dir_all ( & entry. path( ) ) ) ;
39
+ rm_rf ( build , & entry. path ( ) ) ;
39
40
}
40
41
}
41
42
}
42
43
43
44
fn rm_rf ( build : & Build , path : & Path ) {
44
- if path. exists ( ) {
45
- build. verbose ( & format ! ( "removing `{}`" , path. display( ) ) ) ;
46
- t ! ( fs:: remove_dir_all( path) ) ;
45
+ if !path. exists ( ) {
46
+ return
47
+ }
48
+
49
+ for file in t ! ( fs:: read_dir( path) ) {
50
+ let file = t ! ( file) . path ( ) ;
51
+
52
+ if file. is_dir ( ) {
53
+ rm_rf ( build, & file) ;
54
+ } else {
55
+ // On windows we can't remove a readonly file, and git will
56
+ // often clone files as readonly. As a result, we have some
57
+ // special logic to remove readonly files on windows.
58
+ do_op ( & file, "remove file" , |p| fs:: remove_file ( p) ) ;
59
+ }
60
+ }
61
+ do_op ( path, "remove dir" , |p| fs:: remove_dir ( p) ) ;
62
+ }
63
+
64
+ fn do_op < F > ( path : & Path , desc : & str , mut f : F )
65
+ where F : FnMut ( & Path ) -> io:: Result < ( ) >
66
+ {
67
+ match f ( path) {
68
+ Ok ( ( ) ) => { }
69
+ Err ( ref e) if cfg ! ( windows) &&
70
+ e. kind ( ) == ErrorKind :: PermissionDenied => {
71
+ let mut p = t ! ( path. metadata( ) ) . permissions ( ) ;
72
+ p. set_readonly ( false ) ;
73
+ t ! ( fs:: set_permissions( path, p) ) ;
74
+ f ( path) . unwrap_or_else ( |e| {
75
+ panic ! ( "failed to {} {}: {}" , desc, path. display( ) , e) ;
76
+ } )
77
+ }
78
+ Err ( e) => {
79
+ panic ! ( "failed to {} {}: {}" , desc, path. display( ) , e) ;
80
+ }
47
81
}
48
82
}
0 commit comments