@@ -342,22 +342,72 @@ struct StderrThread {
342
342
impl StderrThread {
343
343
fn new ( cmd : & str , file : & str , line : u32 , stderr : Option < PipeReader > , capture : bool ) -> Self {
344
344
if let Some ( stderr) = stderr {
345
+ let file_ = file. to_owned ( ) ;
345
346
let thread = std:: thread:: spawn ( move || {
346
- let mut output = String :: new ( ) ;
347
- BufReader :: new ( stderr)
348
- . lines ( )
349
- . map_while ( Result :: ok)
350
- . for_each ( |line| {
351
- if !capture {
352
- info ! ( "{line}" ) ;
353
- } else {
354
- if !output. is_empty ( ) {
355
- output. push ( '\n' ) ;
347
+ if capture {
348
+ let mut output = String :: new ( ) ;
349
+ BufReader :: new ( stderr)
350
+ . lines ( )
351
+ . map_while ( Result :: ok)
352
+ . for_each ( |line| {
353
+ if !capture {
354
+ info ! ( "{line}" ) ;
355
+ } else {
356
+ if !output. is_empty ( ) {
357
+ output. push ( '\n' ) ;
358
+ }
359
+ output. push_str ( & line) ;
356
360
}
357
- output. push_str ( & line) ;
361
+ } ) ;
362
+ return output;
363
+ }
364
+
365
+ // Log output one line at a time, including progress output separated by CR
366
+ let mut reader = BufReader :: new ( stderr) ;
367
+ let mut buffer = vec ! [ ] ;
368
+ loop {
369
+ // Unconditionally try to read more data, since the BufReader buffer is empty
370
+ let result = match reader. fill_buf ( ) {
371
+ Ok ( buffer) => buffer,
372
+ Err ( error) => {
373
+ warn ! ( "Error reading from child process: {error:?} at {file_}:{line}" ) ;
374
+ break ;
375
+ }
376
+ } ;
377
+ // Add the result onto our own buffer
378
+ buffer. extend ( result) ;
379
+ // Empty the BufReader
380
+ let read_len = result. len ( ) ;
381
+ reader. consume ( read_len) ;
382
+
383
+ // Log output. Take whole “lines” at every LF or CR (for progress bars etc),
384
+ // but leave any incomplete lines in our buffer so we can try to complete them.
385
+ while let Some ( offset) = buffer. iter ( ) . position ( |& b| b == b'\n' || b == b'\r' ) {
386
+ let line = & buffer[ ..offset] ;
387
+ let line = str:: from_utf8 ( line) . map_err ( |_| line) ;
388
+ match line {
389
+ Ok ( string) => info ! ( "{string}" ) ,
390
+ Err ( bytes) => info ! ( "{bytes:?}" ) ,
358
391
}
359
- } ) ;
360
- output
392
+ buffer = buffer. split_off ( offset + 1 ) ;
393
+ }
394
+
395
+ if read_len == 0 {
396
+ break ;
397
+ }
398
+ }
399
+
400
+ // Log any remaining incomplete line
401
+ if !buffer. is_empty ( ) {
402
+ let line = & buffer;
403
+ let line = str:: from_utf8 ( line) . map_err ( |_| line) ;
404
+ match line {
405
+ Ok ( string) => info ! ( "{string}" ) ,
406
+ Err ( bytes) => info ! ( "{bytes:?}" ) ,
407
+ }
408
+ }
409
+
410
+ "" . to_owned ( )
361
411
} ) ;
362
412
Self {
363
413
cmd : cmd. into ( ) ,
0 commit comments