@@ -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,49 @@ 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 }
4750}
4851
49- pub ( crate ) struct PrintThread {
50- handle : Option < JoinHandle < ( ) > > ,
51- pipe_writer : Option < File > ,
52+ pub ( crate ) fn reader_for_stderr_as_warnings (
53+ child : & mut Child ,
54+ ) -> Option < BufReader < std:: process:: ChildStderr > > {
55+ child
56+ . stderr
57+ . take ( )
58+ . map ( BufReader :: new)
5259}
5360
54- impl PrintThread {
55- pub ( crate ) fn new ( ) -> Result < Self , Error > {
56- let ( pipe_reader, pipe_writer) = crate :: os_pipe:: pipe ( ) ?;
57-
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) ;
64- let mut line = Vec :: with_capacity ( 20 ) ;
65- let stdout = io:: stdout ( ) ;
66-
67- // 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- }
76-
77- // read_until does not clear the buffer
78- line. clear ( ) ;
79- }
80- } ) ;
61+ fn forward_all_stderr_as_warnings ( child : & mut Child ) {
62+ if let Some ( mut stderr) = reader_for_stderr_as_warnings ( child) {
63+ let mut line = Vec :: new ( ) ;
64+ let stdout = io:: stdout ( ) ;
8165
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- }
66+ // read_until returns 0 on Eof
67+ while stderr. read_until ( b'\n' , & mut line) . unwrap_or_default ( ) != 0 {
68+ write_warning ( & stdout, & line) ;
9469
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)
70+ // read_until does not clear the buffer
71+ line. clear ( ) ;
72+ }
10873 }
10974}
11075
111- impl Drop for PrintThread {
112- fn drop ( & mut self ) {
113- // Drop pipe_writer first to avoid deadlock
114- self . pipe_writer . take ( ) ;
76+ fn write_warning ( stdout : & Stdout , line : & [ u8 ] ) {
77+ let mut stdout = stdout. lock ( ) ;
11578
116- self . handle . take ( ) . unwrap ( ) . join ( ) . unwrap ( ) ;
117- }
79+ stdout. write_all ( b"cargo:warning=" ) . unwrap ( ) ;
80+ stdout. write_all ( line) . unwrap ( ) ;
81+ stdout. write_all ( b"\n " ) . unwrap ( ) ;
11882}
11983
12084fn wait_on_child ( cmd : & Command , program : & str , child : & mut Child ) -> Result < ( ) , Error > {
85+ forward_all_stderr_as_warnings ( child) ;
12186 let status = match child. wait ( ) {
12287 Ok ( s) => s,
12388 Err ( e) => {
@@ -193,20 +158,13 @@ pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<
193158 Ok ( objects)
194159}
195160
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-
201161pub ( crate ) fn run (
202162 cmd : & mut Command ,
203163 program : & str ,
204- print : Option < & PrintThread > ,
164+ cargo_output : & CargoOutput ,
205165) -> Result < ( ) , Error > {
206- let pipe_writer = print. map ( PrintThread :: clone_pipe_writer) . transpose ( ) ?;
207- run_inner ( cmd, program, pipe_writer) ?;
208-
209- Ok ( ( ) )
166+ let mut child = spawn ( cmd, program, cargo_output) ?;
167+ wait_on_child ( cmd, program, & mut child)
210168}
211169
212170pub ( crate ) fn run_output (
@@ -216,12 +174,7 @@ pub(crate) fn run_output(
216174) -> Result < Vec < u8 > , Error > {
217175 cmd. stdout ( Stdio :: piped ( ) ) ;
218176
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- ) ?;
177+ let mut child = spawn ( cmd, program, cargo_output) ?;
225178
226179 let mut stdout = vec ! [ ] ;
227180 child
@@ -239,7 +192,7 @@ pub(crate) fn run_output(
239192pub ( crate ) fn spawn (
240193 cmd : & mut Command ,
241194 program : & str ,
242- pipe_writer : Option < File > ,
195+ cargo_output : & CargoOutput ,
243196) -> Result < Child , Error > {
244197 struct ResetStderr < ' cmd > ( & ' cmd mut Command ) ;
245198
@@ -254,10 +207,7 @@ pub(crate) fn spawn(
254207 println ! ( "running: {:?}" , cmd) ;
255208
256209 let cmd = ResetStderr ( cmd) ;
257- let child = cmd
258- . 0
259- . stderr ( pipe_writer. map_or_else ( Stdio :: null, Stdio :: from) )
260- . spawn ( ) ;
210+ let child = cmd. 0 . stderr ( cargo_output. stdio_for_warnings ( ) ) . spawn ( ) ;
261211 match child {
262212 Ok ( child) => Ok ( child) ,
263213 Err ( ref e) if e. kind ( ) == io:: ErrorKind :: NotFound => {
@@ -301,14 +251,41 @@ pub(crate) fn command_add_output_file(
301251 }
302252}
303253
254+ #[ cfg( feature = "parallel" ) ]
255+ fn forward_available_stderr_as_warnings (
256+ child_stderr_reader : & mut Option < BufReader < std:: process:: ChildStderr > > ,
257+ ) {
258+ if let Some ( child_stderr_reader) = child_stderr_reader. as_mut ( ) {
259+ if let Ok ( available) = child_stderr_reader. fill_buf ( ) {
260+ let stdout = io:: stdout ( ) ;
261+ let mut consumed = 0 ;
262+ for line in available. split_inclusive ( |& b| b == b'\n' ) {
263+ // Only forward complete lines, leave the rest in the buffer.
264+ if let Some ( ( b'\n' , line) ) = line. split_last ( ) {
265+ consumed += line. len ( ) + 1 ;
266+ write_warning ( & stdout, line) ;
267+ }
268+ }
269+ child_stderr_reader. consume ( consumed) ;
270+ }
271+ }
272+ }
273+
304274#[ cfg( feature = "parallel" ) ]
305275pub ( crate ) fn try_wait_on_child (
306276 cmd : & Command ,
307277 program : & str ,
308278 child : & mut Child ,
309279 stdout : & mut dyn io:: Write ,
280+ child_stderr_reader : & mut Option < BufReader < std:: process:: ChildStderr > > ,
310281) -> Result < Option < ( ) > , Error > {
311- match child. try_wait ( ) {
282+ // Check the child status THEN forward stderr messages, so that we don't race
283+ // between the child printing messages and then exiting.
284+ let wait_result = child. try_wait ( ) ;
285+
286+ forward_available_stderr_as_warnings ( child_stderr_reader) ;
287+
288+ match wait_result {
312289 Ok ( Some ( status) ) => {
313290 let _ = writeln ! ( stdout, "{}" , status) ;
314291
0 commit comments