@@ -77,6 +77,7 @@ use crate::core::compiler::future_incompat::{
7777use crate :: core:: resolver:: ResolveBehavior ;
7878use crate :: core:: { PackageId , Shell , TargetKind } ;
7979use crate :: util:: diagnostic_server:: { self , DiagnosticPrinter } ;
80+ use crate :: util:: errors:: AlreadyPrintedError ;
8081use crate :: util:: machine_message:: { self , Message as _} ;
8182use crate :: util:: CargoResult ;
8283use crate :: util:: { self , internal, profile} ;
@@ -169,6 +170,41 @@ struct DrainState<'cfg> {
169170 per_package_future_incompat_reports : Vec < FutureIncompatReportPackage > ,
170171}
171172
173+ pub struct ErrorsDuringDrain {
174+ pub count : usize ,
175+ }
176+
177+ struct ErrorToHandle {
178+ error : anyhow:: Error ,
179+
180+ /// This field is true for "interesting" errors and false for "mundane"
181+ /// errors. If false, we print the above error only if it's the first one
182+ /// encountered so far while draining the job queue.
183+ ///
184+ /// At most places that an error is propagated, we set this to false to
185+ /// avoid scenarios where Cargo might end up spewing tons of redundant error
186+ /// messages. For example if an i/o stream got closed somewhere, we don't
187+ /// care about individually reporting every thread that it broke; just the
188+ /// first is enough.
189+ ///
190+ /// The exception where print_always is true is that we do report every
191+ /// instance of a rustc invocation that failed with diagnostics. This
192+ /// corresponds to errors from Message::Finish.
193+ print_always : bool ,
194+ }
195+
196+ impl < E > From < E > for ErrorToHandle
197+ where
198+ anyhow:: Error : From < E > ,
199+ {
200+ fn from ( error : E ) -> Self {
201+ ErrorToHandle {
202+ error : anyhow:: Error :: from ( error) ,
203+ print_always : false ,
204+ }
205+ }
206+ }
207+
172208#[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
173209pub struct JobId ( pub u32 ) ;
174210
@@ -613,7 +649,7 @@ impl<'cfg> DrainState<'cfg> {
613649 jobserver_helper : & HelperThread ,
614650 plan : & mut BuildPlan ,
615651 event : Message ,
616- ) -> CargoResult < ( ) > {
652+ ) -> Result < ( ) , ErrorToHandle > {
617653 match event {
618654 Message :: Run ( id, cmd) => {
619655 cx. bcx
@@ -678,11 +714,14 @@ impl<'cfg> DrainState<'cfg> {
678714 debug ! ( "end ({:?}): {:?}" , unit, result) ;
679715 match result {
680716 Ok ( ( ) ) => self . finish ( id, & unit, artifact, cx) ?,
681- Err ( e ) => {
717+ Err ( error ) => {
682718 let msg = "The following warnings were emitted during compilation:" ;
683719 self . emit_warnings ( Some ( msg) , & unit, cx) ?;
684720 self . back_compat_notice ( cx, & unit) ?;
685- return Err ( e) ;
721+ return Err ( ErrorToHandle {
722+ error,
723+ print_always : true ,
724+ } ) ;
686725 }
687726 }
688727 }
@@ -787,14 +826,14 @@ impl<'cfg> DrainState<'cfg> {
787826 // After a job has finished we update our internal state if it was
788827 // successful and otherwise wait for pending work to finish if it failed
789828 // and then immediately return.
790- let mut error = None ;
829+ let mut errors = ErrorsDuringDrain { count : 0 } ;
791830 // CAUTION! Do not use `?` or break out of the loop early. Every error
792831 // must be handled in such a way that the loop is still allowed to
793832 // drain event messages.
794833 loop {
795- if error . is_none ( ) {
834+ if errors . count == 0 {
796835 if let Err ( e) = self . spawn_work_if_possible ( cx, jobserver_helper, scope) {
797- self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut error , e) ;
836+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut errors , e) ;
798837 }
799838 }
800839
@@ -805,7 +844,7 @@ impl<'cfg> DrainState<'cfg> {
805844 }
806845
807846 if let Err ( e) = self . grant_rustc_token_requests ( ) {
808- self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut error , e) ;
847+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut errors , e) ;
809848 }
810849
811850 // And finally, before we block waiting for the next event, drop any
@@ -815,7 +854,7 @@ impl<'cfg> DrainState<'cfg> {
815854 // to the jobserver itself.
816855 for event in self . wait_for_events ( ) {
817856 if let Err ( event_err) = self . handle_event ( cx, jobserver_helper, plan, event) {
818- self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut error , event_err) ;
857+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut errors , event_err) ;
819858 }
820859 }
821860 }
@@ -840,30 +879,24 @@ impl<'cfg> DrainState<'cfg> {
840879 }
841880
842881 let time_elapsed = util:: elapsed ( cx. bcx . config . creation_time ( ) . elapsed ( ) ) ;
843- if let Err ( e) = self . timings . finished ( cx, & error) {
844- if error. is_some ( ) {
845- crate :: display_error ( & e, & mut cx. bcx . config . shell ( ) ) ;
846- } else {
847- return Some ( e) ;
848- }
882+ if let Err ( e) = self . timings . finished ( cx, & errors. to_error ( ) ) {
883+ self . handle_error ( & mut cx. bcx . config . shell ( ) , & mut errors, e) ;
849884 }
850885 if cx. bcx . build_config . emit_json ( ) {
851886 let mut shell = cx. bcx . config . shell ( ) ;
852887 let msg = machine_message:: BuildFinished {
853- success : error . is_none ( ) ,
888+ success : errors . count == 0 ,
854889 }
855890 . to_json_string ( ) ;
856891 if let Err ( e) = writeln ! ( shell. out( ) , "{}" , msg) {
857- if error. is_some ( ) {
858- crate :: display_error ( & e. into ( ) , & mut shell) ;
859- } else {
860- return Some ( e. into ( ) ) ;
861- }
892+ self . handle_error ( & mut shell, & mut errors, e) ;
862893 }
863894 }
864895
865- if let Some ( e) = error {
866- Some ( e)
896+ if let Some ( error) = errors. to_error ( ) {
897+ // Any errors up to this point have already been printed via the
898+ // `display_error` inside `handle_error`.
899+ Some ( anyhow:: Error :: new ( AlreadyPrintedError :: new ( error) ) )
867900 } else if self . queue . is_empty ( ) && self . pending_queue . is_empty ( ) {
868901 let message = format ! (
869902 "{} [{}] target(s) in {}" ,
@@ -888,18 +921,18 @@ impl<'cfg> DrainState<'cfg> {
888921 fn handle_error (
889922 & self ,
890923 shell : & mut Shell ,
891- err_state : & mut Option < anyhow :: Error > ,
892- new_err : anyhow :: Error ,
924+ err_state : & mut ErrorsDuringDrain ,
925+ new_err : impl Into < ErrorToHandle > ,
893926 ) {
894- if err_state . is_some ( ) {
895- // Already encountered one error.
896- log :: warn! ( "{:?}" , new_err ) ;
897- } else if !self . active . is_empty ( ) {
898- crate :: display_error ( & new_err , shell ) ;
899- drop ( shell . warn ( "build failed, waiting for other jobs to finish..." ) ) ;
900- * err_state = Some ( anyhow :: format_err! ( "build failed" ) ) ;
927+ let new_err = new_err . into ( ) ;
928+ if new_err . print_always || err_state . count == 0 {
929+ crate :: display_error ( & new_err . error , shell ) ;
930+ if err_state . count == 0 && !self . active . is_empty ( ) {
931+ drop ( shell . warn ( "build failed, waiting for other jobs to finish..." ) ) ;
932+ }
933+ err_state. count += 1 ;
901934 } else {
902- * err_state = Some ( new_err) ;
935+ log :: warn! ( "{:?}" , new_err. error ) ;
903936 }
904937 }
905938
@@ -1217,3 +1250,13 @@ feature resolver. Try updating to diesel 1.4.8 to fix this error.
12171250 Ok ( ( ) )
12181251 }
12191252}
1253+
1254+ impl ErrorsDuringDrain {
1255+ fn to_error ( & self ) -> Option < anyhow:: Error > {
1256+ match self . count {
1257+ 0 => None ,
1258+ 1 => Some ( format_err ! ( "1 job failed" ) ) ,
1259+ n => Some ( format_err ! ( "{} jobs failed" , n) ) ,
1260+ }
1261+ }
1262+ }
0 commit comments