@@ -5,20 +5,110 @@ extern crate shlex;
5
5
6
6
use bindgen:: { Builder , builder, clang_version} ;
7
7
use std:: fs;
8
- use std:: io:: { BufRead , BufReader , Error , ErrorKind , Read , Write } ;
8
+ use std:: io:: { self , BufRead , BufReader , Error , ErrorKind , Read , Write } ;
9
9
use std:: path:: PathBuf ;
10
+ use std:: process;
11
+ use std:: sync:: { Once , ONCE_INIT } ;
10
12
11
13
#[ path = "../src/options.rs" ]
12
14
mod options;
13
15
use options:: builder_from_flags;
14
16
17
+ // Run `rustfmt` on the given source string and return a tuple of the formatted
18
+ // bindings, and rustfmt's stderr.
19
+ fn rustfmt ( source : String ) -> ( String , String ) {
20
+ static INSTALL_RUSTFMT : Once = ONCE_INIT ;
21
+
22
+ INSTALL_RUSTFMT . call_once ( || {
23
+ let have_working_rustfmt = process:: Command :: new ( "rustup" )
24
+ . args ( & [ "run" , "nightly" , "rustfmt" , "--version" ] )
25
+ . stdout ( process:: Stdio :: null ( ) )
26
+ . stderr ( process:: Stdio :: null ( ) )
27
+ . status ( )
28
+ . expect ( "should run `rustup run nightly rustfmt --version` OK" )
29
+ . success ( ) ;
30
+
31
+ if have_working_rustfmt {
32
+ return ;
33
+ }
34
+
35
+ // Because `rustfmt` needs to match its exact nightly version, we update
36
+ // both at the same time.
37
+
38
+ let status = process:: Command :: new ( "rustup" )
39
+ . args ( & [ "update" , "nightly" ] )
40
+ . status ( )
41
+ . expect ( "should run `rustup update nightly` OK" ) ;
42
+ assert ! ( status. success( ) , "should run `rustup update nightly` OK" ) ;
43
+
44
+ let status = process:: Command :: new ( "rustup" )
45
+ . args ( & [ "run" , "nightly" , "cargo" , "install" , "-f" , "rustfmt-nightly" ] )
46
+ . status ( )
47
+ . expect ( "should run `rustup run nightly cargo install rustfmt-nightly` OK" ) ;
48
+ assert ! ( status. success( ) , "should install rustfmt OK" ) ;
49
+ } ) ;
50
+
51
+ let mut child = process:: Command :: new ( "rustup" )
52
+ . args ( & [
53
+ "run" ,
54
+ "nightly" ,
55
+ "rustfmt" ,
56
+ "--config-path" ,
57
+ concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/tests/rustfmt.toml" )
58
+ ] )
59
+ . stdin ( process:: Stdio :: piped ( ) )
60
+ . stdout ( process:: Stdio :: piped ( ) )
61
+ . stderr ( process:: Stdio :: piped ( ) )
62
+ . spawn ( )
63
+ . expect ( "should spawn `rustup run nightly rustfmt`" ) ;
64
+
65
+ let mut stdin = child. stdin . take ( ) . unwrap ( ) ;
66
+ let mut stdout = child. stdout . take ( ) . unwrap ( ) ;
67
+ let mut stderr = child. stderr . take ( ) . unwrap ( ) ;
68
+
69
+ // Write to stdin in a new thread, so that we can read from stdout on this
70
+ // thread. This keeps the child from blocking on writing to its stdout which
71
+ // might block us from writing to its stdin.
72
+ let stdin_handle = :: std:: thread:: spawn ( move || {
73
+ stdin. write_all ( source. as_bytes ( ) )
74
+ } ) ;
75
+
76
+ // Read stderr on a new thread for similar reasons.
77
+ let stderr_handle = :: std:: thread:: spawn ( move || {
78
+ let mut output = vec ! [ ] ;
79
+ io:: copy ( & mut stderr, & mut output)
80
+ . map ( |_| String :: from_utf8_lossy ( & output) . to_string ( ) )
81
+ } ) ;
82
+
83
+ let mut output = vec ! [ ] ;
84
+ io:: copy ( & mut stdout, & mut output)
85
+ . expect ( "Should copy stdout into vec OK" ) ;
86
+
87
+ // Ignore actual rustfmt status because it is often non-zero for trivial
88
+ // things.
89
+ let _ = child. wait ( ) . expect ( "should wait on rustfmt child OK" ) ;
90
+
91
+ stdin_handle. join ( )
92
+ . expect ( "writer thread should not have panicked" )
93
+ . expect ( "should have written to child rustfmt's stdin OK" ) ;
94
+
95
+ let bindings = String :: from_utf8 ( output)
96
+ . expect ( "rustfmt should only emit valid utf-8" ) ;
97
+
98
+ let stderr = stderr_handle. join ( )
99
+ . expect ( "stderr reader thread should not have panicked" )
100
+ . expect ( "should have read child rustfmt's stderr OK" ) ;
101
+
102
+ ( bindings, stderr)
103
+ }
104
+
15
105
fn compare_generated_header (
16
106
header : & PathBuf ,
17
107
builder : Builder ,
18
108
) -> Result < ( ) , Error > {
19
109
let file_name = try!( header. file_name ( ) . ok_or ( Error :: new (
20
110
ErrorKind :: Other ,
21
- "spawn_bindgen expects a file" ,
111
+ "compare_generated_header expects a file" ,
22
112
) ) ) ;
23
113
24
114
let mut expected = PathBuf :: from ( header) ;
@@ -69,9 +159,12 @@ fn compare_generated_header(
69
159
}
70
160
71
161
// We skip the generate() error here so we get a full diff below
72
- let output = match builder. generate ( ) {
73
- Ok ( bindings) => bindings. to_string ( ) ,
74
- Err ( _) => "" . to_string ( ) ,
162
+ let ( bindings, rustfmt_stderr) = match builder. generate ( ) {
163
+ Ok ( bindings) => {
164
+ let bindings = bindings. to_string ( ) ;
165
+ rustfmt ( bindings)
166
+ }
167
+ Err ( ( ) ) => ( "<error generating bindings>" . to_string ( ) , "" . to_string ( ) ) ,
75
168
} ;
76
169
77
170
let mut buffer = String :: new ( ) ;
@@ -80,9 +173,10 @@ fn compare_generated_header(
80
173
try!( BufReader :: new ( expected_file) . read_to_string ( & mut buffer) ) ;
81
174
}
82
175
}
176
+ let ( buffer, _) = rustfmt ( buffer) ;
83
177
84
- if output == buffer {
85
- if !output . is_empty ( ) {
178
+ if bindings == buffer {
179
+ if !bindings . is_empty ( ) {
86
180
return Ok ( ( ) ) ;
87
181
}
88
182
return Err ( Error :: new (
@@ -91,11 +185,13 @@ fn compare_generated_header(
91
185
) ) ;
92
186
}
93
187
188
+ println ! ( "{}" , rustfmt_stderr) ;
189
+
94
190
println ! ( "diff expected generated" ) ;
95
191
println ! ( "--- expected: {:?}" , expected) ;
96
192
println ! ( "+++ generated from: {:?}" , header) ;
97
193
98
- for diff in diff:: lines ( & buffer, & output ) {
194
+ for diff in diff:: lines ( & buffer, & bindings ) {
99
195
match diff {
100
196
diff:: Result :: Left ( l) => println ! ( "-{}" , l) ,
101
197
diff:: Result :: Both ( l, _) => println ! ( " {}" , l) ,
@@ -106,7 +202,7 @@ fn compare_generated_header(
106
202
// Override the diff.
107
203
{
108
204
let mut expected_file = try!( fs:: File :: create ( & expected) ) ;
109
- try!( expected_file. write_all ( output . as_bytes ( ) ) ) ;
205
+ try!( expected_file. write_all ( bindings . as_bytes ( ) ) ) ;
110
206
}
111
207
112
208
Err ( Error :: new ( ErrorKind :: Other , "Header and binding differ!" ) )
0 commit comments