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