2626//! along with the actual implementation elsewhere. You can find more comments
2727//! about how to define rules themselves below.
2828
29- use std:: collections:: { BTreeMap , HashSet } ;
29+ use std:: collections:: { BTreeMap , HashSet , HashMap } ;
3030use std:: mem;
3131
3232use check:: { self , TestKind } ;
@@ -533,34 +533,44 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
533533 //
534534 // Tools used during the build system but not shipped
535535 rules. build ( "tool-rustbook" , "src/tools/rustbook" )
536- . dep ( |s| s. name ( "librustc" ) )
536+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
537+ . dep ( |s| s. name ( "librustc-tool" ) )
537538 . run ( move |s| compile:: tool ( build, s. stage , s. target , "rustbook" ) ) ;
538539 rules. build ( "tool-error-index" , "src/tools/error_index_generator" )
539- . dep ( |s| s. name ( "librustc" ) )
540+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
541+ . dep ( |s| s. name ( "librustc-tool" ) )
540542 . run ( move |s| compile:: tool ( build, s. stage , s. target , "error_index_generator" ) ) ;
541543 rules. build ( "tool-tidy" , "src/tools/tidy" )
542- . dep ( |s| s. name ( "libstd" ) )
544+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
545+ . dep ( |s| s. name ( "libstd-tool" ) )
543546 . run ( move |s| compile:: tool ( build, s. stage , s. target , "tidy" ) ) ;
544547 rules. build ( "tool-linkchecker" , "src/tools/linkchecker" )
545- . dep ( |s| s. name ( "libstd" ) )
548+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
549+ . dep ( |s| s. name ( "libstd-tool" ) )
546550 . run ( move |s| compile:: tool ( build, s. stage , s. target , "linkchecker" ) ) ;
547551 rules. build ( "tool-cargotest" , "src/tools/cargotest" )
548- . dep ( |s| s. name ( "libstd" ) )
552+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
553+ . dep ( |s| s. name ( "libstd-tool" ) )
549554 . run ( move |s| compile:: tool ( build, s. stage , s. target , "cargotest" ) ) ;
550555 rules. build ( "tool-compiletest" , "src/tools/compiletest" )
551- . dep ( |s| s. name ( "libtest" ) )
556+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
557+ . dep ( |s| s. name ( "libtest-tool" ) )
552558 . run ( move |s| compile:: tool ( build, s. stage , s. target , "compiletest" ) ) ;
553559 rules. build ( "tool-build-manifest" , "src/tools/build-manifest" )
554- . dep ( |s| s. name ( "libstd" ) )
560+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
561+ . dep ( |s| s. name ( "libstd-tool" ) )
555562 . run ( move |s| compile:: tool ( build, s. stage , s. target , "build-manifest" ) ) ;
556563 rules. build ( "tool-qemu-test-server" , "src/tools/qemu-test-server" )
557- . dep ( |s| s. name ( "libstd" ) )
564+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
565+ . dep ( |s| s. name ( "libstd-tool" ) )
558566 . run ( move |s| compile:: tool ( build, s. stage , s. target , "qemu-test-server" ) ) ;
559567 rules. build ( "tool-qemu-test-client" , "src/tools/qemu-test-client" )
560- . dep ( |s| s. name ( "libstd" ) )
568+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
569+ . dep ( |s| s. name ( "libstd-tool" ) )
561570 . run ( move |s| compile:: tool ( build, s. stage , s. target , "qemu-test-client" ) ) ;
562571 rules. build ( "tool-cargo" , "cargo" )
563- . dep ( |s| s. name ( "libstd" ) )
572+ . dep ( |s| s. name ( "maybe-clean-tools" ) )
573+ . dep ( |s| s. name ( "libstd-tool" ) )
564574 . dep ( |s| s. stage ( 0 ) . host ( s. target ) . name ( "openssl" ) )
565575 . dep ( move |s| {
566576 // Cargo depends on procedural macros, which requires a full host
@@ -572,7 +582,8 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
572582 . run ( move |s| compile:: tool ( build, s. stage , s. target , "cargo" ) ) ;
573583 rules. build ( "tool-rls" , "rls" )
574584 . host ( true )
575- . dep ( |s| s. name ( "librustc" ) )
585+ . dep ( |s| s. name ( "librustc-tool" ) )
586+ . dep ( |s| s. stage ( 0 ) . host ( s. target ) . name ( "openssl" ) )
576587 . dep ( move |s| {
577588 // rls, like cargo, uses procedural macros
578589 s. name ( "librustc-link" )
@@ -581,6 +592,25 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
581592 } )
582593 . run ( move |s| compile:: tool ( build, s. stage , s. target , "rls" ) ) ;
583594
595+ // "pseudo rule" which represents completely cleaning out the tools dir in
596+ // one stage. This needs to happen whenever a dependency changes (e.g.
597+ // libstd, libtest, librustc) and all of the tool compilations above will
598+ // be sequenced after this rule.
599+ rules. build ( "maybe-clean-tools" , "path/to/nowhere" )
600+ . after ( "librustc-tool" )
601+ . after ( "libtest-tool" )
602+ . after ( "libstd-tool" ) ;
603+
604+ rules. build ( "librustc-tool" , "path/to/nowhere" )
605+ . dep ( |s| s. name ( "librustc" ) )
606+ . run ( move |s| compile:: maybe_clean_tools ( build, s. stage , s. target , Mode :: Librustc ) ) ;
607+ rules. build ( "libtest-tool" , "path/to/nowhere" )
608+ . dep ( |s| s. name ( "libtest" ) )
609+ . run ( move |s| compile:: maybe_clean_tools ( build, s. stage , s. target , Mode :: Libtest ) ) ;
610+ rules. build ( "libstd-tool" , "path/to/nowhere" )
611+ . dep ( |s| s. name ( "libstd" ) )
612+ . run ( move |s| compile:: maybe_clean_tools ( build, s. stage , s. target , Mode :: Libstd ) ) ;
613+
584614 // ========================================================================
585615 // Documentation targets
586616 rules. doc ( "doc-book" , "src/doc/book" )
@@ -828,6 +858,11 @@ struct Rule<'a> {
828858 /// Whether this rule is only for the build triple, not anything in hosts or
829859 /// targets.
830860 only_build : bool ,
861+
862+ /// A list of "order only" dependencies. This rules does not actually
863+ /// depend on these rules, but if they show up in the dependency graph then
864+ /// this rule must be executed after all these rules.
865+ after : Vec < & ' a str > ,
831866}
832867
833868#[ derive( PartialEq ) ]
@@ -851,6 +886,7 @@ impl<'a> Rule<'a> {
851886 host : false ,
852887 only_host_build : false ,
853888 only_build : false ,
889+ after : Vec :: new ( ) ,
854890 }
855891 }
856892}
@@ -870,6 +906,11 @@ impl<'a, 'b> RuleBuilder<'a, 'b> {
870906 self
871907 }
872908
909+ fn after ( & mut self , step : & ' a str ) -> & mut Self {
910+ self . rule . after . push ( step) ;
911+ self
912+ }
913+
873914 fn run < F > ( & mut self , f : F ) -> & mut Self
874915 where F : Fn ( & Step < ' a > ) + ' a ,
875916 {
@@ -1153,31 +1194,52 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
11531194 /// From the top level targets `steps` generate a topological ordering of
11541195 /// all steps needed to run those steps.
11551196 fn expand ( & self , steps : & [ Step < ' a > ] ) -> Vec < Step < ' a > > {
1197+ // First up build a graph of steps and their dependencies. The `nodes`
1198+ // map is a map from step to a unique number. The `edges` map is a
1199+ // map from these unique numbers to a list of other numbers,
1200+ // representing dependencies.
1201+ let mut nodes = HashMap :: new ( ) ;
1202+ nodes. insert ( Step :: noop ( ) , 0 ) ;
1203+ let mut edges = HashMap :: new ( ) ;
1204+ edges. insert ( 0 , HashSet :: new ( ) ) ;
1205+ for step in steps {
1206+ self . build_graph ( step. clone ( ) , & mut nodes, & mut edges) ;
1207+ }
1208+
1209+ // Now that we've built up the actual dependency graph, draw more
1210+ // dependency edges to satisfy the `after` dependencies field for each
1211+ // rule.
1212+ self . satisfy_after_deps ( & nodes, & mut edges) ;
1213+
1214+ // And finally, perform a topological sort to return a list of steps to
1215+ // execute.
11561216 let mut order = Vec :: new ( ) ;
1157- let mut added = HashSet :: new ( ) ;
1158- added. insert ( Step :: noop ( ) ) ;
1159- for step in steps. iter ( ) . cloned ( ) {
1160- self . fill ( step, & mut order, & mut added) ;
1217+ let mut visited = HashSet :: new ( ) ;
1218+ visited. insert ( 0 ) ;
1219+ let idx_to_node = nodes. iter ( ) . map ( |p| ( * p. 1 , p. 0 ) ) . collect :: < HashMap < _ , _ > > ( ) ;
1220+ for idx in nodes. values ( ) {
1221+ self . topo_sort ( * idx, & idx_to_node, & edges, & mut visited, & mut order) ;
11611222 }
11621223 return order
11631224 }
11641225
1165- /// Performs topological sort of dependencies rooted at the `step`
1166- /// specified, pushing all results onto the `order` vector provided.
1226+ /// Builds the dependency graph rooted at `step`.
11671227 ///
1168- /// In other words, when this method returns, the `order` vector will
1169- /// contain a list of steps which if executed in order will eventually
1170- /// complete the `step` specified as well.
1171- ///
1172- /// The `added` set specified here is the set of steps that are already
1173- /// present in `order` (and hence don't need to be added again).
1174- fn fill ( & self ,
1175- step : Step < ' a > ,
1176- order : & mut Vec < Step < ' a > > ,
1177- added : & mut HashSet < Step < ' a > > ) {
1178- if !added . insert ( step . clone ( ) ) {
1179- return
1228+ /// The `nodes` and `edges` maps are filled out according to the rule
1229+ /// described by `step.name`.
1230+ fn build_graph ( & self ,
1231+ step : Step < ' a > ,
1232+ nodes : & mut HashMap < Step < ' a > , usize > ,
1233+ edges : & mut HashMap < usize , HashSet < usize > > ) -> usize {
1234+ use std :: collections :: hash_map :: Entry ;
1235+
1236+ let idx = nodes . len ( ) ;
1237+ match nodes . entry ( step . clone ( ) ) {
1238+ Entry :: Vacant ( e ) => { e . insert ( idx ) ; }
1239+ Entry :: Occupied ( e ) => return * e . get ( ) ,
11801240 }
1241+
1242+ let mut deps = Vec :: new ( ) ;
11811243 for dep in self . rules [ step. name ] . deps . iter ( ) {
11821244 let dep = dep ( & step) ;
11831245 if dep. name . starts_with ( "default:" ) {
@@ -1189,13 +1251,61 @@ invalid rule dependency graph detected, was a rule added and maybe typo'd?
11891251 let host = self . build . config . host . iter ( ) . any ( |h| h == dep. target ) ;
11901252 let rules = self . rules . values ( ) . filter ( |r| r. default ) ;
11911253 for rule in rules. filter ( |r| r. kind == kind && ( !r. host || host) ) {
1192- self . fill ( dep. name ( rule. name ) , order , added ) ;
1254+ deps . push ( self . build_graph ( dep. name ( rule. name ) , nodes , edges ) ) ;
11931255 }
11941256 } else {
1195- self . fill ( dep, order, added) ;
1257+ deps. push ( self . build_graph ( dep, nodes, edges) ) ;
1258+ }
1259+ }
1260+
1261+ edges. entry ( idx) . or_insert ( HashSet :: new ( ) ) . extend ( deps) ;
1262+ return idx
1263+ }
1264+
1265+ /// Given a dependency graph with a finished list of `nodes`, fill out more
1266+ /// dependency `edges`.
1267+ ///
1268+ /// This is the step which satisfies all `after` listed dependencies in
1269+ /// `Rule` above.
1270+ fn satisfy_after_deps ( & self ,
1271+ nodes : & HashMap < Step < ' a > , usize > ,
1272+ edges : & mut HashMap < usize , HashSet < usize > > ) {
1273+ // Reverse map from the name of a step to the node indices that it
1274+ // appears at.
1275+ let mut name_to_idx = HashMap :: new ( ) ;
1276+ for ( step, & idx) in nodes {
1277+ name_to_idx. entry ( step. name ) . or_insert ( Vec :: new ( ) ) . push ( idx) ;
1278+ }
1279+
1280+ for ( step, idx) in nodes {
1281+ if * step == Step :: noop ( ) {
1282+ continue
1283+ }
1284+ for after in self . rules [ step. name ] . after . iter ( ) {
1285+ // This is the critical piece of an `after` dependency. If the
1286+ // dependency isn't actually in our graph then no edge is drawn,
1287+ // only if it's already present do we draw the edges.
1288+ if let Some ( idxs) = name_to_idx. get ( after) {
1289+ edges. get_mut ( idx) . unwrap ( )
1290+ . extend ( idxs. iter ( ) . cloned ( ) ) ;
1291+ }
11961292 }
11971293 }
1198- order. push ( step) ;
1294+ }
1295+
1296+ fn topo_sort ( & self ,
1297+ cur : usize ,
1298+ nodes : & HashMap < usize , & Step < ' a > > ,
1299+ edges : & HashMap < usize , HashSet < usize > > ,
1300+ visited : & mut HashSet < usize > ,
1301+ order : & mut Vec < Step < ' a > > ) {
1302+ if !visited. insert ( cur) {
1303+ return
1304+ }
1305+ for dep in edges[ & cur] . iter ( ) {
1306+ self . topo_sort ( * dep, nodes, edges, visited, order) ;
1307+ }
1308+ order. push ( nodes[ & cur] . clone ( ) ) ;
11991309 }
12001310}
12011311
0 commit comments