Skip to content

Commit 28a7429

Browse files
committed
Auto merge of #41227 - alexcrichton:compiletest, r=aturon
rustbuild: Fix recompilation of stage0 tools dir This commit knocks out a longstanding FIXME in rustbuild which should correctly recompile stage0 compiletest and such whenever libstd itself changes. The solution implemented here was to implement a notion of "order only" dependencies and then add a new dependency stage for clearing out the tools dir, using order-only deps to ensure that it happens correctly. The dependency drawing for tools is a bit wonky now but I think this'll get the job done. Closes #39396
2 parents ea37682 + 2a33559 commit 28a7429

File tree

2 files changed

+167
-42
lines changed

2 files changed

+167
-42
lines changed

src/bootstrap/compile.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ pub fn rustc(build: &Build, target: &str, compiler: &Compiler) {
275275
cargo.env("CFG_DEFAULT_AR", s);
276276
}
277277
build.run(&mut cargo);
278+
update_mtime(build, &librustc_stamp(build, compiler, target));
278279
}
279280

280281
/// Same as `std_link`, only for librustc
@@ -305,6 +306,12 @@ fn libtest_stamp(build: &Build, compiler: &Compiler, target: &str) -> PathBuf {
305306
build.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
306307
}
307308

309+
/// Cargo's output path for librustc in a given stage, compiled by a particular
310+
/// compiler for the specified target.
311+
fn librustc_stamp(build: &Build, compiler: &Compiler, target: &str) -> PathBuf {
312+
build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
313+
}
314+
308315
fn compiler_file(compiler: &Path, file: &str) -> PathBuf {
309316
let out = output(Command::new(compiler)
310317
.arg(format!("-print-file-name={}", file)));
@@ -407,6 +414,23 @@ fn add_to_sysroot(out_dir: &Path, sysroot_dst: &Path) {
407414
}
408415
}
409416

417+
/// Build a tool in `src/tools`
418+
///
419+
/// This will build the specified tool with the specified `host` compiler in
420+
/// `stage` into the normal cargo output directory.
421+
pub fn maybe_clean_tools(build: &Build, stage: u32, target: &str, mode: Mode) {
422+
let compiler = Compiler::new(stage, &build.config.build);
423+
424+
let stamp = match mode {
425+
Mode::Libstd => libstd_stamp(build, &compiler, target),
426+
Mode::Libtest => libtest_stamp(build, &compiler, target),
427+
Mode::Librustc => librustc_stamp(build, &compiler, target),
428+
_ => panic!(),
429+
};
430+
let out_dir = build.cargo_out(&compiler, Mode::Tool, target);
431+
build.clear_if_dirty(&out_dir, &stamp);
432+
}
433+
410434
/// Build a tool in `src/tools`
411435
///
412436
/// This will build the specified tool with the specified `host` compiler in
@@ -416,15 +440,6 @@ pub fn tool(build: &Build, stage: u32, target: &str, tool: &str) {
416440

417441
let compiler = Compiler::new(stage, &build.config.build);
418442

419-
// FIXME: need to clear out previous tool and ideally deps, may require
420-
// isolating output directories or require a pseudo shim step to
421-
// clear out all the info.
422-
//
423-
// Maybe when libstd is compiled it should clear out the rustc of the
424-
// corresponding stage?
425-
// let out_dir = build.cargo_out(stage, &host, Mode::Librustc, target);
426-
// build.clear_if_dirty(&out_dir, &libstd_stamp(build, stage, &host, target));
427-
428443
let mut cargo = build.cargo(&compiler, Mode::Tool, target, "build");
429444
let mut dir = build.src.join(tool);
430445
if !dir.exists() {

src/bootstrap/step.rs

+143-33
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
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};
3030
use std::mem;
3131

3232
use 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

Comments
 (0)