@@ -126,6 +126,10 @@ struct DrainState<'cfg> {
126
126
total_units : usize ,
127
127
128
128
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 ) > > ,
129
133
messages : Arc < Queue < Message > > ,
130
134
/// Diagnostic deduplication support.
131
135
diag_dedupe : DiagDedupe < ' cfg > ,
@@ -506,8 +510,15 @@ impl<'cfg> JobQueue<'cfg> {
506
510
self . queue . queue_finished ( ) ;
507
511
508
512
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 ( ) ;
509
519
let state = DrainState {
510
520
total_units : self . queue . len ( ) ,
521
+ dep_map,
511
522
queue : self . queue ,
512
523
// 100 here is somewhat arbitrary. It is a few screenfulls of
513
524
// output, and hopefully at most a few megabytes of memory for
@@ -578,6 +589,32 @@ impl<'cfg> DrainState<'cfg> {
578
589
// start requesting job tokens. Each job after the first needs to
579
590
// request a token.
580
591
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
+
581
618
self . pending_queue . push ( ( unit, job) ) ;
582
619
if self . active . len ( ) + self . pending_queue . len ( ) > 1 {
583
620
jobserver_helper. request_token ( ) ;
@@ -713,7 +750,8 @@ impl<'cfg> DrainState<'cfg> {
713
750
} ;
714
751
debug ! ( "end ({:?}): {:?}" , unit, result) ;
715
752
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 ) ?,
717
755
Err ( error) => {
718
756
let msg = "The following warnings were emitted during compilation:" ;
719
757
self . emit_warnings ( Some ( msg) , & unit, cx) ?;
@@ -1161,6 +1199,7 @@ impl<'cfg> DrainState<'cfg> {
1161
1199
unit : & Unit ,
1162
1200
artifact : Artifact ,
1163
1201
cx : & mut Context < ' _ , ' _ > ,
1202
+ success : bool ,
1164
1203
) -> CargoResult < ( ) > {
1165
1204
if unit. mode . is_run_custom_build ( ) && unit. show_warnings ( cx. bcx . config ) {
1166
1205
self . emit_warnings ( None , unit, cx) ?;
@@ -1170,6 +1209,11 @@ impl<'cfg> DrainState<'cfg> {
1170
1209
Artifact :: All => self . timings . unit_finished ( id, unlocked) ,
1171
1210
Artifact :: Metadata => self . timings . unit_rmeta_finished ( id, unlocked) ,
1172
1211
}
1212
+ cx. completed_units
1213
+ . lock ( )
1214
+ . unwrap ( )
1215
+ . insert ( cx. files ( ) . metadata ( unit) , success) ;
1216
+
1173
1217
Ok ( ( ) )
1174
1218
}
1175
1219
0 commit comments