@@ -4,13 +4,12 @@ use std::{
4
4
collections:: hash_map,
5
5
ffi:: OsString ,
6
6
fmt:: Display ,
7
- fs:: { self , File } ,
7
+ fs,
8
8
hash:: Hasher ,
9
- io:: { self , BufRead , BufReader , Read , Write } ,
9
+ io:: { self , BufRead , BufReader , Read , Stdout , Write } ,
10
10
path:: Path ,
11
11
process:: { Child , Command , Stdio } ,
12
12
sync:: Arc ,
13
- thread:: { self , JoinHandle } ,
14
13
} ;
15
14
16
15
use crate :: { Error , ErrorKind , Object } ;
@@ -41,83 +40,55 @@ impl CargoOutput {
41
40
}
42
41
}
43
42
44
- pub ( crate ) fn print_thread ( & self ) -> Result < Option < PrintThread > , Error > {
45
- self . warnings . then ( PrintThread :: new) . transpose ( )
43
+ fn stdio_for_warnings ( & self ) -> Stdio {
44
+ if self . warnings {
45
+ Stdio :: piped ( )
46
+ } else {
47
+ Stdio :: null ( )
48
+ }
46
49
}
47
- }
48
-
49
- pub ( crate ) struct PrintThread {
50
- handle : Option < JoinHandle < ( ) > > ,
51
- pipe_writer : Option < File > ,
52
- }
53
50
54
- impl PrintThread {
55
- pub ( crate ) fn new ( ) -> Result < Self , Error > {
56
- let ( pipe_reader, pipe_writer) = crate :: os_pipe:: pipe ( ) ?;
51
+ #[ cfg( feature = "parallel" ) ]
52
+ pub ( crate ) fn reader_for_stderr_as_warnings (
53
+ & self ,
54
+ child : & mut Child ,
55
+ ) -> Option < BufReader < std:: process:: ChildStderr > > {
56
+ self . warnings
57
+ . then ( || BufReader :: with_capacity ( 4096 , child. stderr . take ( ) . unwrap ( ) ) )
58
+ }
57
59
58
- // Capture the standard error coming from compilation, and write it out
59
- // with cargo:warning= prefixes. Note that this is a bit wonky to avoid
60
- // requiring the output to be UTF-8, we instead just ship bytes from one
61
- // location to another.
62
- let print = thread:: spawn ( move || {
63
- let mut stderr = BufReader :: with_capacity ( 4096 , pipe_reader) ;
60
+ fn forward_stderr_as_warnings ( & self , child : & mut Child ) {
61
+ if self . warnings {
62
+ let mut stderr = BufReader :: with_capacity ( 4096 , child. stderr . as_mut ( ) . unwrap ( ) ) ;
64
63
let mut line = Vec :: with_capacity ( 20 ) ;
65
64
let stdout = io:: stdout ( ) ;
66
65
67
66
// read_until returns 0 on Eof
68
- while stderr. read_until ( b'\n' , & mut line) . unwrap ( ) != 0 {
69
- {
70
- let mut stdout = stdout. lock ( ) ;
71
-
72
- stdout. write_all ( b"cargo:warning=" ) . unwrap ( ) ;
73
- stdout. write_all ( & line) . unwrap ( ) ;
74
- stdout. write_all ( b"\n " ) . unwrap ( ) ;
75
- }
67
+ while stderr. read_until ( b'\n' , & mut line) . unwrap_or_default ( ) != 0 {
68
+ write_warning ( & stdout, & line) ;
76
69
77
70
// read_until does not clear the buffer
78
71
line. clear ( ) ;
79
72
}
80
- } ) ;
81
-
82
- Ok ( Self {
83
- handle : Some ( print) ,
84
- pipe_writer : Some ( pipe_writer) ,
85
- } )
86
- }
87
-
88
- /// # Panics
89
- ///
90
- /// Will panic if the pipe writer has already been taken.
91
- pub ( crate ) fn take_pipe_writer ( & mut self ) -> File {
92
- self . pipe_writer . take ( ) . unwrap ( )
93
- }
94
-
95
- /// # Panics
96
- ///
97
- /// Will panic if the pipe writer has already been taken.
98
- pub ( crate ) fn clone_pipe_writer ( & self ) -> Result < File , Error > {
99
- self . try_clone_pipe_writer ( ) . map ( Option :: unwrap)
100
- }
101
-
102
- pub ( crate ) fn try_clone_pipe_writer ( & self ) -> Result < Option < File > , Error > {
103
- self . pipe_writer
104
- . as_ref ( )
105
- . map ( File :: try_clone)
106
- . transpose ( )
107
- . map_err ( From :: from)
73
+ }
108
74
}
109
75
}
110
76
111
- impl Drop for PrintThread {
112
- fn drop ( & mut self ) {
113
- // Drop pipe_writer first to avoid deadlock
114
- self . pipe_writer . take ( ) ;
77
+ fn write_warning ( stdout : & Stdout , line : & [ u8 ] ) {
78
+ let mut stdout = stdout. lock ( ) ;
115
79
116
- self . handle . take ( ) . unwrap ( ) . join ( ) . unwrap ( ) ;
117
- }
80
+ stdout. write_all ( b"cargo:warning=" ) . unwrap ( ) ;
81
+ stdout. write_all ( line) . unwrap ( ) ;
82
+ stdout. write_all ( b"\n " ) . unwrap ( ) ;
118
83
}
119
84
120
- fn wait_on_child ( cmd : & Command , program : & str , child : & mut Child ) -> Result < ( ) , Error > {
85
+ fn wait_on_child (
86
+ cmd : & Command ,
87
+ program : & str ,
88
+ child : & mut Child ,
89
+ cargo_output : & CargoOutput ,
90
+ ) -> Result < ( ) , Error > {
91
+ cargo_output. forward_stderr_as_warnings ( child) ;
121
92
let status = match child. wait ( ) {
122
93
Ok ( s) => s,
123
94
Err ( e) => {
@@ -193,20 +164,13 @@ pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<
193
164
Ok ( objects)
194
165
}
195
166
196
- fn run_inner ( cmd : & mut Command , program : & str , pipe_writer : Option < File > ) -> Result < ( ) , Error > {
197
- let mut child = spawn ( cmd, program, pipe_writer) ?;
198
- wait_on_child ( cmd, program, & mut child)
199
- }
200
-
201
167
pub ( crate ) fn run (
202
168
cmd : & mut Command ,
203
169
program : & str ,
204
- print : Option < & PrintThread > ,
170
+ cargo_output : & CargoOutput ,
205
171
) -> Result < ( ) , Error > {
206
- let pipe_writer = print. map ( PrintThread :: clone_pipe_writer) . transpose ( ) ?;
207
- run_inner ( cmd, program, pipe_writer) ?;
208
-
209
- Ok ( ( ) )
172
+ let mut child = spawn ( cmd, program, cargo_output) ?;
173
+ wait_on_child ( cmd, program, & mut child, cargo_output)
210
174
}
211
175
212
176
pub ( crate ) fn run_output (
@@ -216,12 +180,7 @@ pub(crate) fn run_output(
216
180
) -> Result < Vec < u8 > , Error > {
217
181
cmd. stdout ( Stdio :: piped ( ) ) ;
218
182
219
- let mut print = cargo_output. print_thread ( ) ?;
220
- let mut child = spawn (
221
- cmd,
222
- program,
223
- print. as_mut ( ) . map ( PrintThread :: take_pipe_writer) ,
224
- ) ?;
183
+ let mut child = spawn ( cmd, program, cargo_output) ?;
225
184
226
185
let mut stdout = vec ! [ ] ;
227
186
child
@@ -231,15 +190,15 @@ pub(crate) fn run_output(
231
190
. read_to_end ( & mut stdout)
232
191
. unwrap ( ) ;
233
192
234
- wait_on_child ( cmd, program, & mut child) ?;
193
+ wait_on_child ( cmd, program, & mut child, cargo_output ) ?;
235
194
236
195
Ok ( stdout)
237
196
}
238
197
239
198
pub ( crate ) fn spawn (
240
199
cmd : & mut Command ,
241
200
program : & str ,
242
- pipe_writer : Option < File > ,
201
+ cargo_output : & CargoOutput ,
243
202
) -> Result < Child , Error > {
244
203
struct ResetStderr < ' cmd > ( & ' cmd mut Command ) ;
245
204
@@ -254,10 +213,7 @@ pub(crate) fn spawn(
254
213
println ! ( "running: {:?}" , cmd) ;
255
214
256
215
let cmd = ResetStderr ( cmd) ;
257
- let child = cmd
258
- . 0
259
- . stderr ( pipe_writer. map_or_else ( Stdio :: null, Stdio :: from) )
260
- . spawn ( ) ;
216
+ let child = cmd. 0 . stderr ( cargo_output. stdio_for_warnings ( ) ) . spawn ( ) ;
261
217
match child {
262
218
Ok ( child) => Ok ( child) ,
263
219
Err ( ref e) if e. kind ( ) == io:: ErrorKind :: NotFound => {
@@ -307,8 +263,27 @@ pub(crate) fn try_wait_on_child(
307
263
program : & str ,
308
264
child : & mut Child ,
309
265
stdout : & mut dyn io:: Write ,
266
+ child_stderr_reader : & mut Option < BufReader < std:: process:: ChildStderr > > ,
310
267
) -> Result < Option < ( ) > , Error > {
311
- match child. try_wait ( ) {
268
+ // Check the child status THEN forward stderr messages, so that we don't race
269
+ // between the child printing messages and then exiting.
270
+ let wait_result = child. try_wait ( ) ;
271
+
272
+ if let Some ( child_stderr_reader) = child_stderr_reader. as_mut ( ) {
273
+ if let Ok ( mut available) = child_stderr_reader. fill_buf ( ) {
274
+ let stdout = io:: stdout ( ) ;
275
+ let original_size = available. len ( ) ;
276
+ while let Some ( i) = memchr:: memchr ( b'\n' , available) {
277
+ let ( line, remainder) = available. split_at ( i) ;
278
+ write_warning ( & stdout, line) ;
279
+ available = & remainder[ 1 ..] ;
280
+ }
281
+ let consumed = original_size - available. len ( ) ;
282
+ child_stderr_reader. consume ( consumed) ;
283
+ }
284
+ }
285
+
286
+ match wait_result {
312
287
Ok ( Some ( status) ) => {
313
288
let _ = writeln ! ( stdout, "{}" , status) ;
314
289
0 commit comments