Skip to content

Commit 7b2f44a

Browse files
authored
Rollup merge of #73883 - ehuss:rustdoc-stage-previous, r=Mark-Simulacrum
Compile rustdoc less often. Previously rustdoc was built 3 times with `x.py test`: 1. stage2 (using stage1 compiler) for compiletest tests (stage1-tools copied to stage2). 2. stage1 (using stage0 compiler) for std crate tests (stage0-tools copied to stage1). 3. stage2 test (using stage2 compiler) for rustdoc crate tests and error_index_generator (stage2-tools). This PR removes the majority of number 3, where it will instead use the stage1 compiler, which will share the artifacts from number 1. This matches the behavior of the libstd crate tests. I don't think it is entirely necessary to run the tests using stage2. At `-j2`, the last build step goes from about 300s to 70s on my machine. It's not a huge win, but shaving 4 minutes isn't bad. The other two builds would be pretty difficult (or undesired or impossible) to unify. It looks like std tests use stage1 very intentionally (see `force_use_stage1` and its history), and compiletests use the top stage very intentionally. Unfortunately the linkchecker builds all docs at stage2 (stage2-tools), which means a few build script artifacts are not shared. It's not really clear to me how to fix that (because it uses `default_doc`, there doesn't seem to be any control over the stages). --- For `x.py doc`, rustdoc was previously built three times (with compiler-docs): 1. stage2 (using stage1 compiler) for normal documentation output (stage1-tools copied to stage2). 2. stage1 (using stage0 compiler) for compiler-docs 3. stage2 (using stage2 compiler) for error_index_generator (stage2-tools) This PR combines these so that they consistently use the "top stage" rustdoc. I don't know why the compiler-docs was written to use stage minus one, but it seems better to be consistent across the doc steps. --- I've tried to test this with a variety of commands (`x.py doc`, `x.py test`, different `--stage` flags, `full-bootstrap`, setting `--target`, etc.) to try to make sure there aren't significant regressions here. It's tricky since there are so many variables, and this stuff is difficult for me to fully understand. Closes #70799 (I think)
2 parents 294b972 + 0b9bc79 commit 7b2f44a

File tree

4 files changed

+146
-34
lines changed

4 files changed

+146
-34
lines changed

src/bootstrap/builder/tests.rs

+79
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ fn dist_baseline() {
5454
&[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },]
5555
);
5656
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
57+
// Make sure rustdoc is only built once.
58+
assert_eq!(
59+
first(builder.cache.all::<tool::Rustdoc>()),
60+
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
61+
);
5762
}
5863

5964
#[test]
@@ -414,3 +419,77 @@ fn test_exclude() {
414419
// Ensure other tests are not affected.
415420
assert!(builder.cache.contains::<test::RustdocUi>());
416421
}
422+
423+
#[test]
424+
fn doc_default() {
425+
let mut config = configure(&[], &[]);
426+
config.compiler_docs = true;
427+
config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
428+
let build = Build::new(config);
429+
let mut builder = Builder::new(&build);
430+
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
431+
let a = INTERNER.intern_str("A");
432+
433+
// error_index_generator uses stage 1 to share rustdoc artifacts with the
434+
// rustdoc tool.
435+
assert_eq!(
436+
first(builder.cache.all::<doc::ErrorIndex>()),
437+
&[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
438+
);
439+
assert_eq!(
440+
first(builder.cache.all::<tool::ErrorIndex>()),
441+
&[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
442+
);
443+
// This is actually stage 1, but Rustdoc::run swaps out the compiler with
444+
// stage minus 1 if --stage is not 0. Very confusing!
445+
assert_eq!(
446+
first(builder.cache.all::<tool::Rustdoc>()),
447+
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
448+
);
449+
}
450+
451+
#[test]
452+
fn test_docs() {
453+
// Behavior of `x.py test` doing various documentation tests.
454+
let mut config = configure(&[], &[]);
455+
config.cmd = Subcommand::Test {
456+
paths: vec![],
457+
test_args: vec![],
458+
rustc_args: vec![],
459+
fail_fast: true,
460+
doc_tests: DocTests::Yes,
461+
bless: false,
462+
compare_mode: None,
463+
rustfix_coverage: false,
464+
pass: None,
465+
};
466+
let build = Build::new(config);
467+
let mut builder = Builder::new(&build);
468+
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
469+
let a = INTERNER.intern_str("A");
470+
471+
// error_index_generator uses stage 1 to share rustdoc artifacts with the
472+
// rustdoc tool.
473+
assert_eq!(
474+
first(builder.cache.all::<doc::ErrorIndex>()),
475+
&[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
476+
);
477+
assert_eq!(
478+
first(builder.cache.all::<tool::ErrorIndex>()),
479+
&[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
480+
);
481+
// Unfortunately rustdoc is built twice. Once from stage1 for compiletest
482+
// (and other things), and once from stage0 for std crates. Ideally it
483+
// would only be built once. If someone wants to fix this, it might be
484+
// worth investigating if it would be possible to test std from stage1.
485+
// Note that the stages here are +1 than what they actually are because
486+
// Rustdoc::run swaps out the compiler with stage minus 1 if --stage is
487+
// not 0.
488+
assert_eq!(
489+
first(builder.cache.all::<tool::Rustdoc>()),
490+
&[
491+
tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
492+
tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },
493+
]
494+
);
495+
}

src/bootstrap/doc.rs

+15-16
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,7 @@ impl Step for Rustc {
518518
let out = builder.compiler_doc_out(target);
519519
t!(fs::create_dir_all(&out));
520520

521-
// Get the correct compiler for this stage.
522-
let compiler = builder.compiler_for(stage, builder.config.build, target);
521+
let compiler = builder.compiler(stage, builder.config.build);
523522

524523
if !builder.config.compiler_docs {
525524
builder.info("\tskipping - compiler/librustdoc docs disabled");
@@ -599,8 +598,7 @@ impl Step for Rustdoc {
599598
let out = builder.compiler_doc_out(target);
600599
t!(fs::create_dir_all(&out));
601600

602-
// Get the correct compiler for this stage.
603-
let compiler = builder.compiler_for(stage, builder.config.build, target);
601+
let compiler = builder.compiler(stage, builder.config.build);
604602

605603
if !builder.config.compiler_docs {
606604
builder.info("\tskipping - compiler/librustdoc docs disabled");
@@ -639,9 +637,10 @@ impl Step for Rustdoc {
639637
}
640638
}
641639

642-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
640+
#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
643641
pub struct ErrorIndex {
644-
target: Interned<String>,
642+
pub compiler: Compiler,
643+
pub target: Interned<String>,
645644
}
646645

647646
impl Step for ErrorIndex {
@@ -655,26 +654,26 @@ impl Step for ErrorIndex {
655654
}
656655

657656
fn make_run(run: RunConfig<'_>) {
658-
run.builder.ensure(ErrorIndex { target: run.target });
657+
let target = run.target;
658+
// error_index_generator depends on librustdoc. Use the compiler that
659+
// is normally used to build rustdoc for other documentation so that
660+
// it shares the same artifacts.
661+
let compiler =
662+
run.builder.compiler_for(run.builder.top_stage, run.builder.config.build, target);
663+
run.builder.ensure(ErrorIndex { compiler, target });
659664
}
660665

661666
/// Generates the HTML rendered error-index by running the
662667
/// `error_index_generator` tool.
663668
fn run(self, builder: &Builder<'_>) {
664-
let target = self.target;
665-
666-
builder.info(&format!("Documenting error index ({})", target));
667-
let out = builder.doc_out(target);
669+
builder.info(&format!("Documenting error index ({})", self.target));
670+
let out = builder.doc_out(self.target);
668671
t!(fs::create_dir_all(&out));
669-
let compiler = builder.compiler(2, builder.config.build);
670-
let mut index = tool::ErrorIndex::command(builder, compiler);
672+
let mut index = tool::ErrorIndex::command(builder, self.compiler);
671673
index.arg("html");
672674
index.arg(out.join("error-index.html"));
673675
index.arg(crate::channel::CFG_RELEASE_NUM);
674676

675-
// FIXME: shouldn't have to pass this env var
676-
index.env("CFG_BUILD", &builder.config.build);
677-
678677
builder.run(&mut index);
679678
}
680679
}

src/bootstrap/test.rs

+47-13
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,11 @@ impl Step for ErrorIndex {
14541454
}
14551455

14561456
fn make_run(run: RunConfig<'_>) {
1457-
run.builder
1458-
.ensure(ErrorIndex { compiler: run.builder.compiler(run.builder.top_stage, run.host) });
1457+
// error_index_generator depends on librustdoc. Use the compiler that
1458+
// is normally used to build rustdoc for other tests (like compiletest
1459+
// tests in src/test/rustdoc) so that it shares the same artifacts.
1460+
let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host);
1461+
run.builder.ensure(ErrorIndex { compiler });
14591462
}
14601463

14611464
/// Runs the error index generator tool to execute the tests located in the error
@@ -1467,22 +1470,23 @@ impl Step for ErrorIndex {
14671470
fn run(self, builder: &Builder<'_>) {
14681471
let compiler = self.compiler;
14691472

1470-
builder.ensure(compile::Std { compiler, target: compiler.host });
1471-
14721473
let dir = testdir(builder, compiler.host);
14731474
t!(fs::create_dir_all(&dir));
14741475
let output = dir.join("error-index.md");
14751476

1476-
let mut tool = tool::ErrorIndex::command(
1477-
builder,
1478-
builder.compiler(compiler.stage, builder.config.build),
1479-
);
1480-
tool.arg("markdown").arg(&output).env("CFG_BUILD", &builder.config.build);
1477+
let mut tool = tool::ErrorIndex::command(builder, compiler);
1478+
tool.arg("markdown").arg(&output);
14811479

1482-
builder.info(&format!("Testing error-index stage{}", compiler.stage));
1480+
// Use the rustdoc that was built by self.compiler. This copy of
1481+
// rustdoc is shared with other tests (like compiletest tests in
1482+
// src/test/rustdoc). This helps avoid building rustdoc multiple
1483+
// times.
1484+
let rustdoc_compiler = builder.compiler(builder.top_stage, builder.config.build);
1485+
builder.info(&format!("Testing error-index stage{}", rustdoc_compiler.stage));
14831486
let _time = util::timeit(&builder);
14841487
builder.run_quiet(&mut tool);
1485-
markdown_test(builder, compiler, &output);
1488+
builder.ensure(compile::Std { compiler: rustdoc_compiler, target: rustdoc_compiler.host });
1489+
markdown_test(builder, rustdoc_compiler, &output);
14861490
}
14871491
}
14881492

@@ -1797,9 +1801,13 @@ impl Step for CrateRustdoc {
17971801

17981802
fn run(self, builder: &Builder<'_>) {
17991803
let test_kind = self.test_kind;
1804+
let target = self.host;
18001805

1801-
let compiler = builder.compiler(builder.top_stage, self.host);
1802-
let target = compiler.host;
1806+
// Use the previous stage compiler to reuse the artifacts that are
1807+
// created when running compiletest for src/test/rustdoc. If this used
1808+
// `compiler`, then it would cause rustdoc to be built *again*, which
1809+
// isn't really necessary.
1810+
let compiler = builder.compiler_for(builder.top_stage, target, target);
18031811
builder.ensure(compile::Rustc { compiler, target });
18041812

18051813
let mut cargo = tool::prepare_tool_cargo(
@@ -1825,6 +1833,32 @@ impl Step for CrateRustdoc {
18251833
cargo.arg("'-Ctarget-feature=-crt-static'");
18261834
}
18271835

1836+
// This is needed for running doctests on librustdoc. This is a bit of
1837+
// an unfortunate interaction with how bootstrap works and how cargo
1838+
// sets up the dylib path, and the fact that the doctest (in
1839+
// html/markdown.rs) links to rustc-private libs. For stage1, the
1840+
// compiler host dylibs (in stage1/lib) are not the same as the target
1841+
// dylibs (in stage1/lib/rustlib/...). This is different from a normal
1842+
// rust distribution where they are the same.
1843+
//
1844+
// On the cargo side, normal tests use `target_process` which handles
1845+
// setting up the dylib for a *target* (stage1/lib/rustlib/... in this
1846+
// case). However, for doctests it uses `rustdoc_process` which only
1847+
// sets up the dylib path for the *host* (stage1/lib), which is the
1848+
// wrong directory.
1849+
//
1850+
// It should be considered to just stop running doctests on
1851+
// librustdoc. There is only one test, and it doesn't look too
1852+
// important. There might be other ways to avoid this, but it seems
1853+
// pretty convoluted.
1854+
//
1855+
// See also https://github.com/rust-lang/rust/issues/13983 where the
1856+
// host vs target dylibs for rustdoc are consistently tricky to deal
1857+
// with.
1858+
let mut dylib_path = dylib_path();
1859+
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target)));
1860+
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
1861+
18281862
if !builder.config.verbose_tests {
18291863
cargo.arg("--quiet");
18301864
}

src/bootstrap/tool.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ bootstrap_tool!(
366366
ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors";
367367
);
368368

369-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
369+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
370370
pub struct ErrorIndex {
371371
pub compiler: Compiler,
372372
}
@@ -392,9 +392,9 @@ impl Step for ErrorIndex {
392392
fn make_run(run: RunConfig<'_>) {
393393
// Compile the error-index in the same stage as rustdoc to avoid
394394
// recompiling rustdoc twice if we can.
395-
let stage = if run.builder.top_stage >= 2 { run.builder.top_stage } else { 0 };
396-
run.builder
397-
.ensure(ErrorIndex { compiler: run.builder.compiler(stage, run.builder.config.build) });
395+
let host = run.builder.config.build;
396+
let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
397+
run.builder.ensure(ErrorIndex { compiler });
398398
}
399399

400400
fn run(self, builder: &Builder<'_>) -> PathBuf {
@@ -449,7 +449,7 @@ impl Step for RemoteTestServer {
449449
}
450450
}
451451

452-
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
452+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
453453
pub struct Rustdoc {
454454
/// This should only ever be 0 or 2.
455455
/// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here.

0 commit comments

Comments
 (0)