@@ -126,6 +126,10 @@ struct DrainState<'cfg> {
126126 total_units : usize ,
127127
128128 queue : DependencyQueue < Unit , Artifact , Job > ,
129+ /// Dependency map that is like JobQueue::dep_map, except with Job information removed.
130+ /// Used to determine if a unit's dependencies have failed, see
131+ /// [`DrainState::spawn_work_if_possible`].
132+ dep_map : HashMap < Unit , HashSet < ( Unit , Artifact ) > > ,
129133 messages : Arc < Queue < Message > > ,
130134 /// Diagnostic deduplication support.
131135 diag_dedupe : DiagDedupe < ' cfg > ,
@@ -506,8 +510,15 @@ impl<'cfg> JobQueue<'cfg> {
506510 self . queue . queue_finished ( ) ;
507511
508512 let progress = Progress :: with_style ( "Building" , ProgressStyle :: Ratio , cx. bcx . config ) ;
513+ let dep_map = self
514+ . queue
515+ . dep_map ( )
516+ . iter ( )
517+ . map ( |( unit, ( deps, _) ) | ( unit. clone ( ) , deps. clone ( ) ) )
518+ . collect ( ) ;
509519 let state = DrainState {
510520 total_units : self . queue . len ( ) ,
521+ dep_map,
511522 queue : self . queue ,
512523 // 100 here is somewhat arbitrary. It is a few screenfulls of
513524 // output, and hopefully at most a few megabytes of memory for
@@ -578,6 +589,32 @@ impl<'cfg> DrainState<'cfg> {
578589 // start requesting job tokens. Each job after the first needs to
579590 // request a token.
580591 while let Some ( ( unit, job) ) = self . queue . dequeue ( ) {
592+ // First, we handle the special case of fallible units. If
593+ // this unit is allowed to fail, and any one of its dependencies
594+ // has failed, then we should immediately mark it as failed and
595+ // skip executing it.
596+ if unit. can_fail {
597+ let mut completed_units = cx. completed_units . lock ( ) . unwrap ( ) ;
598+ let failed_deps = self . dep_map [ & unit]
599+ . iter ( )
600+ . filter ( |( dep_unit, _) | {
601+ let dep_meta = cx. files ( ) . metadata ( dep_unit) ;
602+ !completed_units[ & dep_meta]
603+ } )
604+ . map ( |( _, artifact) | artifact)
605+ . collect :: < HashSet < _ > > ( ) ;
606+ if !failed_deps. is_empty ( ) {
607+ // TODO: should put a warning here saying which units were skipped
608+ // due to failed dependencies.
609+ for artifact in failed_deps {
610+ self . queue . finish ( & unit, artifact) ;
611+ }
612+ let unit_meta = cx. files ( ) . metadata ( & unit) ;
613+ completed_units. insert ( unit_meta, false ) ;
614+ continue ;
615+ }
616+ }
617+
581618 self . pending_queue . push ( ( unit, job) ) ;
582619 if self . active . len ( ) + self . pending_queue . len ( ) > 1 {
583620 jobserver_helper. request_token ( ) ;
@@ -713,7 +750,8 @@ impl<'cfg> DrainState<'cfg> {
713750 } ;
714751 debug ! ( "end ({:?}): {:?}" , unit, result) ;
715752 match result {
716- Ok ( ( ) ) => self . finish ( id, & unit, artifact, cx) ?,
753+ Ok ( ( ) ) => self . finish ( id, & unit, artifact, cx, true ) ?,
754+ Err ( _) if unit. can_fail => self . finish ( id, & unit, artifact, cx, false ) ?,
717755 Err ( error) => {
718756 let msg = "The following warnings were emitted during compilation:" ;
719757 self . emit_warnings ( Some ( msg) , & unit, cx) ?;
@@ -1161,6 +1199,7 @@ impl<'cfg> DrainState<'cfg> {
11611199 unit : & Unit ,
11621200 artifact : Artifact ,
11631201 cx : & mut Context < ' _ , ' _ > ,
1202+ success : bool ,
11641203 ) -> CargoResult < ( ) > {
11651204 if unit. mode . is_run_custom_build ( ) && unit. show_warnings ( cx. bcx . config ) {
11661205 self . emit_warnings ( None , unit, cx) ?;
@@ -1170,6 +1209,11 @@ impl<'cfg> DrainState<'cfg> {
11701209 Artifact :: All => self . timings . unit_finished ( id, unlocked) ,
11711210 Artifact :: Metadata => self . timings . unit_rmeta_finished ( id, unlocked) ,
11721211 }
1212+ cx. completed_units
1213+ . lock ( )
1214+ . unwrap ( )
1215+ . insert ( cx. files ( ) . metadata ( unit) , success) ;
1216+
11731217 Ok ( ( ) )
11741218 }
11751219
0 commit comments