@@ -4,13 +4,12 @@ use std::{
44 collections:: hash_map,
55 ffi:: OsString ,
66 fmt:: Display ,
7- fs:: { self , File } ,
7+ fs,
88 hash:: Hasher ,
9- io:: { self , BufRead , BufReader , Read , Write } ,
9+ io:: { self , BufRead , BufReader , Read , Stdout , Write } ,
1010 path:: Path ,
1111 process:: { Child , Command , Stdio } ,
1212 sync:: Arc ,
13- thread:: { self , JoinHandle } ,
1413} ;
1514
1615use crate :: { Error , ErrorKind , Object } ;
@@ -41,83 +40,55 @@ impl CargoOutput {
4140 }
4241 }
4342
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+ }
4649 }
47- }
48-
49- pub ( crate ) struct PrintThread {
50- handle : Option < JoinHandle < ( ) > > ,
51- pipe_writer : Option < File > ,
52- }
5350
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+ }
5759
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 ( ) ) ;
6463 let mut line = Vec :: with_capacity ( 20 ) ;
6564 let stdout = io:: stdout ( ) ;
6665
6766 // 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) ;
7669
7770 // read_until does not clear the buffer
7871 line. clear ( ) ;
7972 }
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+ }
10874 }
10975}
11076
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 ( ) ;
11579
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 ( ) ;
11883}
11984
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) ;
12192 let status = match child. wait ( ) {
12293 Ok ( s) => s,
12394 Err ( e) => {
@@ -193,20 +164,13 @@ pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<
193164 Ok ( objects)
194165}
195166
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-
201167pub ( crate ) fn run (
202168 cmd : & mut Command ,
203169 program : & str ,
204- print : Option < & PrintThread > ,
170+ cargo_output : & CargoOutput ,
205171) -> 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)
210174}
211175
212176pub ( crate ) fn run_output (
@@ -216,12 +180,7 @@ pub(crate) fn run_output(
216180) -> Result < Vec < u8 > , Error > {
217181 cmd. stdout ( Stdio :: piped ( ) ) ;
218182
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) ?;
225184
226185 let mut stdout = vec ! [ ] ;
227186 child
@@ -231,15 +190,15 @@ pub(crate) fn run_output(
231190 . read_to_end ( & mut stdout)
232191 . unwrap ( ) ;
233192
234- wait_on_child ( cmd, program, & mut child) ?;
193+ wait_on_child ( cmd, program, & mut child, cargo_output ) ?;
235194
236195 Ok ( stdout)
237196}
238197
239198pub ( crate ) fn spawn (
240199 cmd : & mut Command ,
241200 program : & str ,
242- pipe_writer : Option < File > ,
201+ cargo_output : & CargoOutput ,
243202) -> Result < Child , Error > {
244203 struct ResetStderr < ' cmd > ( & ' cmd mut Command ) ;
245204
@@ -254,10 +213,7 @@ pub(crate) fn spawn(
254213 println ! ( "running: {:?}" , cmd) ;
255214
256215 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 ( ) ;
261217 match child {
262218 Ok ( child) => Ok ( child) ,
263219 Err ( ref e) if e. kind ( ) == io:: ErrorKind :: NotFound => {
@@ -307,8 +263,27 @@ pub(crate) fn try_wait_on_child(
307263 program : & str ,
308264 child : & mut Child ,
309265 stdout : & mut dyn io:: Write ,
266+ child_stderr_reader : & mut Option < BufReader < std:: process:: ChildStderr > > ,
310267) -> 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 {
312287 Ok ( Some ( status) ) => {
313288 let _ = writeln ! ( stdout, "{}" , status) ;
314289
0 commit comments