@@ -69,7 +69,7 @@ use super::job::{
69
69
} ;
70
70
use super :: timings:: Timings ;
71
71
use super :: { BuildContext , BuildPlan , CompileMode , Context , Unit } ;
72
- use crate :: core:: { PackageId , TargetKind } ;
72
+ use crate :: core:: { PackageId , Shell , TargetKind } ;
73
73
use crate :: util:: diagnostic_server:: { self , DiagnosticPrinter } ;
74
74
use crate :: util:: machine_message:: { self , Message as _} ;
75
75
use crate :: util:: { self , internal, profile} ;
@@ -401,8 +401,13 @@ impl<'cfg> JobQueue<'cfg> {
401
401
. take ( )
402
402
. map ( move |srv| srv. start ( move |msg| messages. push ( Message :: FixDiagnostic ( msg) ) ) ) ;
403
403
404
- crossbeam_utils:: thread:: scope ( move |scope| state. drain_the_queue ( cx, plan, scope, & helper) )
405
- . expect ( "child threads shouldn't panic" )
404
+ crossbeam_utils:: thread:: scope ( move |scope| {
405
+ match state. drain_the_queue ( cx, plan, scope, & helper) {
406
+ Some ( err) => Err ( err) ,
407
+ None => Ok ( ( ) ) ,
408
+ }
409
+ } )
410
+ . expect ( "child threads shouldn't panic" )
406
411
}
407
412
}
408
413
@@ -412,7 +417,6 @@ impl<'cfg> DrainState<'cfg> {
412
417
cx : & mut Context < ' _ , ' _ > ,
413
418
jobserver_helper : & HelperThread ,
414
419
scope : & Scope < ' _ > ,
415
- has_errored : bool ,
416
420
) -> CargoResult < ( ) > {
417
421
// Dequeue as much work as we can, learning about everything
418
422
// possible that can run. Note that this is also the point where we
@@ -425,11 +429,6 @@ impl<'cfg> DrainState<'cfg> {
425
429
}
426
430
}
427
431
428
- // Do not actually spawn the new work if we've errored out
429
- if has_errored {
430
- return Ok ( ( ) ) ;
431
- }
432
-
433
432
// Now that we've learned of all possible work that we can execute
434
433
// try to spawn it so long as we've got a jobserver token which says
435
434
// we're able to perform some parallel work.
@@ -487,7 +486,7 @@ impl<'cfg> DrainState<'cfg> {
487
486
jobserver_helper : & HelperThread ,
488
487
plan : & mut BuildPlan ,
489
488
event : Message ,
490
- ) -> CargoResult < Option < anyhow :: Error > > {
489
+ ) -> CargoResult < ( ) > {
491
490
match event {
492
491
Message :: Run ( id, cmd) => {
493
492
cx. bcx
@@ -545,17 +544,7 @@ impl<'cfg> DrainState<'cfg> {
545
544
Err ( e) => {
546
545
let msg = "The following warnings were emitted during compilation:" ;
547
546
self . emit_warnings ( Some ( msg) , & unit, cx) ?;
548
-
549
- if !self . active . is_empty ( ) {
550
- crate :: display_error ( & e, & mut * cx. bcx . config . shell ( ) ) ;
551
- cx. bcx . config . shell ( ) . warn (
552
- "build failed, waiting for other \
553
- jobs to finish...",
554
- ) ?;
555
- return Ok ( Some ( anyhow:: format_err!( "build failed" ) ) ) ;
556
- } else {
557
- return Ok ( Some ( e) ) ;
558
- }
547
+ return Err ( e) ;
559
548
}
560
549
}
561
550
}
@@ -590,7 +579,7 @@ impl<'cfg> DrainState<'cfg> {
590
579
}
591
580
}
592
581
593
- Ok ( None )
582
+ Ok ( ( ) )
594
583
}
595
584
596
585
// This will also tick the progress bar as appropriate
@@ -631,13 +620,18 @@ impl<'cfg> DrainState<'cfg> {
631
620
events
632
621
}
633
622
623
+ /// This is the "main" loop, where Cargo does all work to run the
624
+ /// compiler.
625
+ ///
626
+ /// This returns an Option to prevent the use of `?` on `Result` types
627
+ /// because it is important for the loop to carefully handle errors.
634
628
fn drain_the_queue (
635
629
mut self ,
636
630
cx : & mut Context < ' _ , ' _ > ,
637
631
plan : & mut BuildPlan ,
638
632
scope : & Scope < ' _ > ,
639
633
jobserver_helper : & HelperThread ,
640
- ) -> CargoResult < ( ) > {
634
+ ) -> Option < anyhow :: Error > {
641
635
trace ! ( "queue: {:#?}" , self . queue) ;
642
636
643
637
// Iteratively execute the entire dependency graph. Each turn of the
@@ -651,25 +645,34 @@ impl<'cfg> DrainState<'cfg> {
651
645
// successful and otherwise wait for pending work to finish if it failed
652
646
// and then immediately return.
653
647
let mut error = None ;
648
+ // CAUTION! Do not use `?` or break out of the loop early. Every error
649
+ // must be handled in such a way that the loop is still allowed to
650
+ // drain event messages.
654
651
loop {
655
- self . spawn_work_if_possible ( cx, jobserver_helper, scope, error. is_some ( ) ) ?;
652
+ if error. is_none ( ) {
653
+ if let Err ( e) = self . spawn_work_if_possible ( cx, jobserver_helper, scope) {
654
+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut error, e) ;
655
+ }
656
+ }
656
657
657
658
// If after all that we're not actually running anything then we're
658
659
// done!
659
660
if self . active . is_empty ( ) {
660
661
break ;
661
662
}
662
663
663
- self . grant_rustc_token_requests ( ) ?;
664
+ if let Err ( e) = self . grant_rustc_token_requests ( ) {
665
+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut error, e) ;
666
+ }
664
667
665
668
// And finally, before we block waiting for the next event, drop any
666
669
// excess tokens we may have accidentally acquired. Due to how our
667
670
// jobserver interface is architected we may acquire a token that we
668
671
// don't actually use, and if this happens just relinquish it back
669
672
// to the jobserver itself.
670
673
for event in self . wait_for_events ( ) {
671
- if let Some ( err ) = self . handle_event ( cx, jobserver_helper, plan, event) ? {
672
- error = Some ( err ) ;
674
+ if let Err ( event_err ) = self . handle_event ( cx, jobserver_helper, plan, event) {
675
+ self . handle_error ( & mut cx . bcx . config . shell ( ) , & mut error , event_err ) ;
673
676
}
674
677
}
675
678
}
@@ -694,29 +697,62 @@ impl<'cfg> DrainState<'cfg> {
694
697
}
695
698
696
699
let time_elapsed = util:: elapsed ( cx. bcx . config . creation_time ( ) . elapsed ( ) ) ;
697
- self . timings . finished ( cx. bcx , & error) ?;
700
+ if let Err ( e) = self . timings . finished ( cx. bcx , & error) {
701
+ if error. is_some ( ) {
702
+ crate :: display_error ( & e, & mut cx. bcx . config . shell ( ) ) ;
703
+ } else {
704
+ return Some ( e) ;
705
+ }
706
+ }
698
707
if cx. bcx . build_config . emit_json ( ) {
699
708
let msg = machine_message:: BuildFinished {
700
709
success : error. is_none ( ) ,
701
710
}
702
711
. to_json_string ( ) ;
703
- writeln ! ( cx. bcx. config. shell( ) . out( ) , "{}" , msg) ?;
712
+ if let Err ( e) = writeln ! ( cx. bcx. config. shell( ) . out( ) , "{}" , msg) {
713
+ if error. is_some ( ) {
714
+ crate :: display_error ( & e. into ( ) , & mut cx. bcx . config . shell ( ) ) ;
715
+ } else {
716
+ return Some ( e. into ( ) ) ;
717
+ }
718
+ }
704
719
}
705
720
706
721
if let Some ( e) = error {
707
- Err ( e)
722
+ Some ( e)
708
723
} else if self . queue . is_empty ( ) && self . pending_queue . is_empty ( ) {
709
724
let message = format ! (
710
725
"{} [{}] target(s) in {}" ,
711
726
profile_name, opt_type, time_elapsed
712
727
) ;
713
728
if !cx. bcx . build_config . build_plan {
714
- cx. bcx . config . shell ( ) . status ( "Finished" , message) ?;
729
+ // It doesn't really matter if this fails.
730
+ drop ( cx. bcx . config . shell ( ) . status ( "Finished" , message) ) ;
715
731
}
716
- Ok ( ( ) )
732
+ None
717
733
} else {
718
734
debug ! ( "queue: {:#?}" , self . queue) ;
719
- Err ( internal ( "finished with jobs still left in the queue" ) )
735
+ Some ( internal ( "finished with jobs still left in the queue" ) )
736
+ }
737
+ }
738
+
739
+ fn handle_error (
740
+ & self ,
741
+ shell : & mut Shell ,
742
+ err_state : & mut Option < anyhow:: Error > ,
743
+ new_err : anyhow:: Error ,
744
+ ) {
745
+ if err_state. is_some ( ) {
746
+ // Already encountered one error.
747
+ log:: warn!( "{:?}" , new_err) ;
748
+ } else {
749
+ if !self . active . is_empty ( ) {
750
+ crate :: display_error ( & new_err, shell) ;
751
+ drop ( shell. warn ( "build failed, waiting for other jobs to finish..." ) ) ;
752
+ * err_state = Some ( anyhow:: format_err!( "build failed" ) ) ;
753
+ } else {
754
+ * err_state = Some ( new_err) ;
755
+ }
720
756
}
721
757
}
722
758
0 commit comments