From c84edb3d8cbd74af5df2cb06dbcb7c29d9743ff7 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 6 Sep 2026 00:41:07 +0800 Subject: [PATCH] Revert "Auto merge of #161716 - notriddle:remake-bootstrap-doc-mergeable, r=Kobzol,jieyouxu" This reverts commit a69a63265cfd9e006d43137f98301b8d274ad4c9, reversing changes made to c33d8f3b5a50b56466998e8c5ed8a077d2caed84. --- src/bootstrap/src/core/build_steps/compile.rs | 10 +- src/bootstrap/src/core/build_steps/dist.rs | 9 +- src/bootstrap/src/core/build_steps/doc.rs | 467 ++++-------------- src/bootstrap/src/core/builder/cargo.rs | 6 - src/bootstrap/src/core/builder/mod.rs | 1 - src/bootstrap/src/core/builder/tests.rs | 114 +++-- src/bootstrap/src/core/session.rs | 2 +- 7 files changed, 192 insertions(+), 417 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index a85921214fe51..b1157ef381013 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2669,7 +2669,7 @@ pub fn run_cargo( let (filenames_vec, crate_types) = match msg { CargoMessage::CompilerArtifact { filenames, - target: CargoTarget { crate_types, .. }, + target: CargoTarget { crate_types }, .. } => { let mut f: Vec = filenames.into_iter().map(|s| s.into_owned()).collect(); @@ -2876,14 +2876,12 @@ pub fn stream_cargo( status.success() } -#[derive(Deserialize, Debug)] +#[derive(Deserialize)] pub struct CargoTarget<'a> { - pub crate_types: Vec>, - #[serde(default)] - pub doc: bool, + crate_types: Vec>, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize)] #[serde(tag = "reason", rename_all = "kebab-case")] pub enum CargoMessage<'a> { CompilerArtifact { filenames: Vec>, target: CargoTarget<'a> }, diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 2e2b74549912c..a335172631307 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,7 +23,7 @@ use crate::core::backend::CodegenBackendKind; use crate::core::build_steps::compile::{ get_codegen_backend_file, libgccjit_path_relative_to_cg_dir, normalize_codegen_backend_name, }; -use crate::core::build_steps::doc::{CompilerWithTools, DocumentationFormat}; +use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::GccTargetPair; use crate::core::build_steps::llvm::{ LLVM_CI_LINK_TYPE_PATH, LlvmBuildStatus, LlvmKind, get_llvm_build_status, @@ -185,7 +185,7 @@ impl CommandLineStep for JsonDocs { } } -/// Builds the `rustc-docs` component. +/// Builds the `rustc-docs` installer component. /// Apart from the documentation of the `rustc_*` crates, it also includes the documentation of /// various in-tree helper tools (bootstrap, build_helper, tidy), /// and also rustc_private tools like rustdoc, clippy, miri or rustfmt. @@ -214,12 +214,11 @@ impl CommandLineStep for RustcDocs { fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; - let combined_docs = - builder.ensure(CompilerWithTools::for_stage(builder, builder.top_stage, self.target)); + builder.run_default_doc_steps(); let mut tarball = Tarball::new(builder, "rustc-docs", &target.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_bulk_dir(combined_docs, "share/doc/rust/html/rustc-docs"); + tarball.add_bulk_dir(builder.compiler_doc_out(target), "share/doc/rust/html/rustc-docs"); tarball.generate() } } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 127dd3b40f787..81c112db5eee5 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -7,13 +7,11 @@ //! Everything here is basically just a shim around calling either `rustbook` or //! `rustdoc`. -use std::collections::HashSet; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, mem}; use crate::core::build_steps::compile; -use crate::core::build_steps::compile::{CargoMessage, stream_cargo}; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; @@ -22,9 +20,9 @@ use crate::core::builder::{ crate_description, }; use crate::core::compiler::Compiler; -use crate::core::config::TargetSelection; +use crate::core::config::{Config, TargetSelection}; use crate::core::session::{FileType, Mode}; -use crate::utils::helpers::{exit_process, submodule_path_of, symlink_dir, t, up_to_date}; +use crate::utils::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -842,6 +840,7 @@ fn doc_std( .arg("--no-deps") .arg("--target-dir") .arg(&*target_dir.to_string_lossy()) + .arg("-Zskip-rustdoc-fingerprint") .arg("-Zrustdoc-map") .rustdocflag("--extern-html-root-url") .rustdocflag("std_detect=https://docs.rs/std_detect/latest/") @@ -872,159 +871,7 @@ pub fn prepare_doc_compiler( build_compiler } -/// Run rustdoc to merge cross-crate info metadata (like the search index) from individual -/// executions of rustdoc into `out_dir`. -/// The `json_files` parameter should contain paths to JSON file artifacts generated by previous -/// executions of `cargo doc`. -fn merge_rustdoc_cci( - builder: &Builder<'_>, - build_compiler: Compiler, - json_files: &[PathBuf], - out_dir: &Path, -) { - let mut cmd = builder.rustdoc_cmd(build_compiler); - - cmd.arg("--enable-index-page").arg("-Zunstable-options").arg("-o").arg(out_dir); - - if !builder.config.docs_minification { - cmd.arg("--disable-minification"); - } - - for json_file in json_files { - cmd.arg("--read-doc-meta-dir").arg(json_file.parent().unwrap()); - } - - cmd.run(builder); -} - -/// Generate the combined compiler + tools docs for a given toolchain. -/// This contains both the compiler docs, docs of rustc_private tools (miri, clippy, etc.), cargo -/// and also some bootstrap related tools (bootstrap itself, compiletest, tidy, etc.). -/// -/// It gets hosted at https://doc.rust-lang.org/nightly/nightly-rustc/index.html. -/// -/// Compiler documentation is distributed separately, so we make sure -/// we do not merge it with the other documentation from std, test and -/// proc_macros. This is largely just a wrapper around `cargo doc`. -/// -/// Returns a path to a directory with the generated documentation. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct CompilerWithTools { - build_compiler: Compiler, - target: TargetSelection, - stage: u32, -} - -impl CompilerWithTools { - /// Document `stage` compiler for the given `target`. - pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { - let build_compiler = prepare_doc_compiler(builder, target, stage); - Self { build_compiler, target, stage } - } -} - -impl CommandLineStep for CompilerWithTools { - type Output = PathBuf; - const IS_HOST: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.alias("compiler-with-tools") - } - - fn is_default_step(builder: &Builder<'_>) -> bool { - builder.config.compiler_docs - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CompilerWithTools::for_stage( - run.builder, - run.builder.top_stage, - run.target, - )); - } - - fn run(self, builder: &Builder<'_>) -> Self::Output { - let CompilerWithTools { target, build_compiler, stage } = self; - - // This is the intended out directory for combined compiler documentation. - let out = builder.compiler_doc_out(target); - let _ = fs::remove_dir_all(&out); - - let _guard = - builder.msg(Kind::Doc, "compiler-with-tools", Mode::Rustc, build_compiler, target); - - let combined_docs = vec![ - builder.ensure(Rustc::for_stage(builder, stage, target)), - builder.ensure(Rustdoc::new(builder, target)), - builder.ensure(Rustfmt::new(builder, target)), - builder.ensure(Clippy::new(builder, target)), - builder.ensure(Miri::new(builder, target)), - builder.ensure(Cargo::new(builder, target)), - builder.ensure(Tidy::new(builder, target)), - builder.ensure(Bootstrap::new(builder, target)), - builder.ensure(BuildHelper::new(builder, target)), - builder.ensure(Compiletest::new(builder, target)), - builder.ensure(RunMakeSupport::new(builder, target)), - ]; - - if !builder.config.dry_run() { - // Now copy all the individual docs into a single directory - let mut json_files = vec![]; - for docs in combined_docs { - json_files.extend(docs.artifacts.json_files); - - // Doc directories to link to the shared output directory - // We add the host doc dirs, which should already be symlinked in the target - // docs dir at this point (see `merge_host_and_target_docs`). - let dirs_to_copy: Vec<_> = docs - .artifacts - .target_dirs - .iter() - .chain(docs.artifacts.host_dirs.iter()) - .map(|d| d.file_name().unwrap().to_str().unwrap()) - .collect(); - for dir in dirs_to_copy { - // Link the docs dir - let docs_dir = docs.out_dir.join(dir); - assert!(docs_dir.exists(), "Docs directory {docs_dir:?} does not exist."); - let out_docs_dir = out.join(dir); - builder.create_dir(&out_docs_dir); - builder.cp_link_r(&docs_dir, &out_docs_dir); - - // And the src dir - let src_dir = docs.out_dir.join("src").join(dir); - assert!(src_dir.exists(), "Docs source directory {src_dir:?} does not exist."); - let out_src_dir = out.join("src").join(dir); - builder.create_dir(&out_src_dir); - builder.cp_link_r(&src_dir, &out_src_dir); - } - } - // And finally merge all the CCI metadata - merge_rustdoc_cci(builder, build_compiler, &json_files, &out); - } - - // Handle `--open`. - builder.open_in_browser(out.join("index.html")); - out - } - - fn metadata(&self) -> Option { - Some(StepMetadata::doc("CompilerWithTools", self.target).built_by(self.build_compiler)) - } -} - -/// Output of a Doc step. -#[derive(Clone)] -pub struct BuiltDocs { - /// Target doc directory with the generated documentation. - out_dir: PathBuf, - /// Doc artifacts gathered from Cargo during the doc build. - artifacts: DocArtifacts, -} - /// Document the compiler for the given `target` using rustdoc from `build_compiler`. -/// -/// Return the path to the generated rustc documentation directory. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustc { build_compiler: Compiler, @@ -1054,7 +901,7 @@ impl Rustc { } impl CommandLineStep for Rustc { - type Output = BuiltDocs; + type Output = (); const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1075,9 +922,13 @@ impl CommandLineStep for Rustc { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) -> Self::Output { + fn run(self, builder: &Builder<'_>) { let target = self.target; + // This is the intended out directory for compiler documentation. + let out = builder.compiler_doc_out(target); + t!(fs::create_dir_all(&out)); + // Build the standard library, so that proc-macros can use it. // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.) let build_compiler = self.build_compiler; @@ -1114,6 +965,7 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--generate-macro-expansion"); compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); + cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. // Do link to dependencies on `docs.rs` however using `rustdoc-map`. @@ -1125,42 +977,53 @@ impl CommandLineStep for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - let cargo_target_dir = builder.stage_out(build_compiler, Mode::Rustc); - let target_doc_dir = cargo_target_dir.join(target).join("doc"); - let host_doc_dir = cargo_target_dir.join("doc"); + let mut to_open = None; + + let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. - let dir_name = normalize_doc_crate_name(krate); - t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); + let dir_name = krate.replace('-', "_"); + t!(fs::create_dir_all(out_dir.join(&*dir_name))); cargo.arg("-p").arg(krate); + if to_open.is_none() { + to_open = Some(dir_name); + } } - let artifacts = create_docs_and_gather_artifacts(builder, cargo); - artifacts.sanity_check_crates(builder, self.crates.iter()); + // This uses a shared directory so that librustdoc documentation gets + // correctly built and merged with the rustc documentation. + // + // This is needed because rustdoc is built in a different directory from + // rustc. rustdoc needs to be able to see everything, for example when + // merging the search index, or generating local (relative) links. + symlink_dir_force(&builder.config, &out, &out_dir); + // Cargo puts proc macros in `target/doc` even if you pass `--target` + // explicitly (https://github.com/rust-lang/cargo/issues/7677). + let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc"); + symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); + + cargo.into_cmd().run(builder); if !builder.config.dry_run() { - merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); - merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); + // Sanity check on linked compiler crates + for krate in &*self.crates { + let dir_name = krate.replace('-', "_"); + // Making sure the directory exists and is not empty. + assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); + } } - // We open rustc_middle as the default if invoked as `x.py doc --open RELEASES.md` - // with no particular explicit doc requested (e.g. library/core). - if builder.was_invoked_explicitly::(Kind::Doc) { - let index = if builder.paths.iter().any(|path| path.ends_with("compiler")) { - // For `x.py doc compiler --open`, open `rustc_middle` by default. - target_doc_dir.join("rustc_middle").join("index.html") - } else if let Some(krate) = self.crates.first() { - // Let's open the first crate documentation page: - target_doc_dir.join(normalize_doc_crate_name(krate)).join("index.html") - } else { - target_doc_dir.clone() - }; + if builder.paths.iter().any(|path| path.ends_with("compiler")) { + // For `x.py doc compiler --open`, open `rustc_middle` by default. + let index = out.join("rustc_middle").join("index.html"); + builder.open_in_browser(index); + } else if let Some(krate) = to_open { + // Let's open the first crate documentation page: + let index = out.join(krate).join("index.html"); builder.open_in_browser(index); } - - BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1168,145 +1031,6 @@ impl CommandLineStep for Rustc { } } -/// Stores generated documentation artifacts. -#[derive(Clone, Debug)] -struct DocArtifacts { - /// Directories with HTML docs for host (proc-macro) crates. - host_dirs: Vec, - /// Directories with HTML docs for target crates. - target_dirs: Vec, - /// JSON files used to create the final CCI index - json_files: Vec, -} - -impl DocArtifacts { - /// Ensure that all passed crates were documented. - fn sanity_check_crates(&self, builder: &Builder<'_>, crates: impl Iterator) - where - S: AsRef, - { - if builder.config.dry_run() { - return; - } - let crate_names: HashSet<&str> = self - .host_dirs - .iter() - .chain(self.target_dirs.iter()) - .filter_map(|d| d.file_name().and_then(|d| d.to_str())) - .collect(); - for krate in crates { - let krate = krate.as_ref(); - let krate = normalize_doc_crate_name(krate); - if !crate_names.contains(krate.as_str()) { - eprintln!("ERROR: crate {krate} was not documented!"); - exit_process(1); - } - } - } -} - -/// Run `cargo doc` and gather generated documentation artifacts. -fn create_docs_and_gather_artifacts(builder: &Builder<'_>, cargo: builder::Cargo) -> DocArtifacts { - let mut json_files = vec![]; - let mut host_dirs = vec![]; - let mut target_dirs = vec![]; - stream_cargo(builder, cargo, vec![], &mut |msg| { - let CargoMessage::CompilerArtifact { filenames, target } = msg else { - return; - }; - if !target.doc { - return; - } - // Note: An alternative way to check host docs would be to check whether the generated - // output is a child of the host doc directory (which we would have to pass to this - // function). - let is_host = target.crate_types.iter().any(|t| t == "proc-macro"); - for filename in filenames { - let path = Path::new(filename.as_ref()); - let Some(extension) = path.extension().and_then(|ext| ext.to_str()) else { - continue; - }; - let path = path.to_path_buf(); - if extension == "json" { - json_files.push(path.to_path_buf()); - } else if extension == "html" { - if is_host { - // doc//index.html -> doc/ - host_dirs.push(path.parent().unwrap().to_path_buf()); - } else { - target_dirs.push(path.parent().unwrap().to_path_buf()); - } - } - } - }); - DocArtifacts { host_dirs, target_dirs, json_files } -} - -/// Merge host and target documentation for a set of crates. -/// We pass `--target` when documenting, so Cargo will put the built documentation into two places: -/// - `target/doc` - contains documentation of host code, so proc macros -/// - `target//doc` - contains documentation of "normal" code -/// -/// See https://github.com/rust-lang/cargo/issues/7677. -/// -/// To produce a single unified documentation, we want to merge them together. -/// We do that by creating symlinks into the target doc dir that will point to the host doc -/// directories. -/// The target doc directory will then contain the combined docs. -fn merge_host_and_target_docs( - builder: &Builder<'_>, - docs: &DocArtifacts, - host_doc_dir: &Path, - target_doc_dir: &Path, -) { - // Sanity check that there is no host/target overlap - for dir in &docs.host_dirs { - let name = dir.file_name().and_then(|d| d.to_str()).unwrap(); - if let Some(target_dir) = docs.target_dirs.iter().find_map(|d| { - let dirname = d.file_name().and_then(|d| d.to_str())?; - if dirname == name { Some(d) } else { None } - }) { - eprintln!( - "ERROR: host docs directory `{name}` ({dir:?}) is also contained in target doc directory ({target_dir:?})" - ); - exit_process(1); - } - } - - let target_src_dir = target_doc_dir.join("src"); - let host_src_dir = host_doc_dir.join("src"); - - // Ideally, we would remove all previous symlinks here. - // However, some of the tools actually share the same build docs directory, so we shouldn't do - // that, otherwise they will invalidate one another. - - for host_docs_crate in &docs.host_dirs { - let dir_name = host_docs_crate.file_name().unwrap().to_str().unwrap(); - // Normalize crate name - let dir_name = normalize_doc_crate_name(dir_name); - - t!(symlink_dir(&builder.config, host_docs_crate, &target_doc_dir.join(&dir_name))); - - // Also symlink its source directory - let target_src_out = target_src_dir.join(&dir_name); - let host_src_out = host_src_dir.join(&dir_name); - t!(symlink_dir(&builder.config, &host_src_out, &target_src_out)); - } - - // Sanity check that all directories contain some documentation - for dir in docs.target_dirs.iter().chain(docs.host_dirs.iter()) { - // Making sure the directory exists and is not empty. - assert!(dir.exists(), "Doc directory {dir:?} does not exist"); - assert!(t!(dir.read_dir()).next().is_some(), "Doc directory {dir:?} is empty"); - } -} - -/// Normalizes crate name to get a name that is used to generate documentation on disk. -/// Turns `rustc-main` into `rustc_main`. -fn normalize_doc_crate_name(name: &str) -> String { - name.replace("-", "_") -} - macro_rules! tool_doc { ( $tool: ident, @@ -1324,53 +1048,46 @@ macro_rules! tool_doc { target: TargetSelection, } - impl $tool { - fn new(builder: &Builder<'_>, target: TargetSelection) -> $tool { + impl CommandLineStep for $tool { + type Output = (); + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path($path) + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + builder.config.compiler_docs + } + + fn make_run(run: RunConfig<'_>) { + let target = run.target; let build_compiler = match $mode { Mode::ToolRustcPrivate => { // Rustdoc needs the rustc sysroot available to build. - let compilers = RustcPrivateCompilers::new(builder, builder.top_stage, target); + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); // Build rustc docs so that we generate relative links. - builder.ensure(Rustc::from_build_compiler(builder, compilers.build_compiler(), target)); + run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); compilers.build_compiler() } Mode::ToolTarget => { // when shipping multiple docs together in one folder, // they all need to use the same rustdoc version - prepare_doc_compiler(builder, builder.host_target, builder.top_stage) + prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage) } _ => { panic!("Unexpected tool mode for documenting: {:?}", $mode); } }; - $tool { build_compiler, mode: $mode, target } - } - fn crates() -> &'static [&'static str] { - &$($crates)?[..] - } - } - - impl CommandLineStep for $tool { - type Output = BuiltDocs; - const IS_HOST: bool = true; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path($path) - } - - fn is_default_step(builder: &Builder<'_>) -> bool { - builder.config.compiler_docs - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure($tool::new(run.builder, run.target)); + run.builder.ensure($tool { build_compiler, mode: $mode, target }); } /// Generates documentation for a tool. /// /// This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) -> Self::Output { + fn run(self, builder: &Builder<'_>) { let mut source_type = SourceType::InTree; if let Some(submodule_path) = submodule_path_of(&builder, $path) { @@ -1380,6 +1097,10 @@ macro_rules! tool_doc { let $tool { build_compiler, mode, target } = self; + // This is the intended out directory for compiler documentation. + let out = builder.compiler_doc_out(target); + t!(fs::create_dir_all(&out)); + // Build cargo command. let mut cargo = prepare_tool_cargo( builder, @@ -1401,6 +1122,7 @@ macro_rules! tool_doc { cargo.allow_features(allow_features); } + cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. cargo.arg("--no-deps"); @@ -1408,9 +1130,9 @@ macro_rules! tool_doc { cargo.arg("--lib"); } - for krate in $tool::crates() { + $(for krate in $crates { cargo.arg("-p").arg(krate); - } + })? cargo.rustdocflag("--document-private-items"); // Since we always pass --document-private-items, there's no need to warn about linking to private items. @@ -1419,23 +1141,28 @@ macro_rules! tool_doc { cargo.rustdocflag("--show-type-layout"); cargo.rustdocflag("--generate-link-to-definition"); - let cargo_target_dir = builder.stage_out(build_compiler, mode); - let target_doc_dir = cargo_target_dir.join(target).join("doc"); - let host_doc_dir = cargo_target_dir.join("doc"); - for krate in $tool::crates() { - let dir_name = normalize_doc_crate_name(krate); - t!(fs::create_dir_all(target_doc_dir.join(&*dir_name))); - } + let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc"); + $(for krate in $crates { + let dir_name = krate.replace("-", "_"); + t!(fs::create_dir_all(out_dir.join(&*dir_name))); + })? + + // Symlink compiler docs to the output directory of rustdoc documentation. + symlink_dir_force(&builder.config, &out, &out_dir); + let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc"); + symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target); - let artifacts = create_docs_and_gather_artifacts(builder, cargo); - artifacts.sanity_check_crates(builder, $tool::crates().iter()); + cargo.into_cmd().run(builder); if !builder.config.dry_run() { - merge_host_and_target_docs(builder, &artifacts, &host_doc_dir, &target_doc_dir); - merge_rustdoc_cci(builder, build_compiler, &artifacts.json_files, &target_doc_dir); + // Sanity check on linked doc directories + $(for krate in $crates { + let dir_name = krate.replace("-", "_"); + // Making sure the directory exists and is not empty. + assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some()); + })? } - BuiltDocs { out_dir: target_doc_dir, artifacts } } fn metadata(&self) -> Option { @@ -1617,6 +1344,26 @@ impl CommandLineStep for UnstableBookGen { } } +fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { + if config.dry_run() { + return; + } + if let Ok(m) = fs::symlink_metadata(link) { + if m.file_type().is_dir() { + t!(fs::remove_dir_all(link)); + } else { + // handle directory junctions on windows by falling back to + // `remove_dir`. + t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link))); + } + } + + t!( + symlink_dir(config, original, link), + format!("failed to create link from {} -> {}", link.display(), original.display()) + ); +} + /// Builds the Rust compiler book. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcBook { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 0a6ae88316f56..288e147a26615 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -723,12 +723,6 @@ impl Builder<'_> { } if cmd_kind == Kind::Doc { - // Will be stabilized soon -> let's dogfood it. - // No effect on doc output but massive doc-generation time improvements. - cargo.arg("-Zrustdoc-mergeable-info"); - - // FIXME: remove this directory clearing here, and do it explicitly in individua doc - // steps, to reduce dependency on implicit doc output paths. let my_out = match mode { // This is the intended out directory for compiler documentation. Mode::Rustc | Mode::ToolRustcPrivate | Mode::ToolBootstrap | Mode::ToolTarget => { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 2f5bd1ec64d28..0bd0781133684 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -961,7 +961,6 @@ impl<'a> Builder<'a> { doc::CargoBook, doc::Clippy, doc::ClippyBook, - doc::CompilerWithTools, doc::Miri, doc::EmbeddedBook, doc::EditionGuide, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 6ef38d07e45bb..9c613811c8ccc 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -989,6 +989,58 @@ mod snapshot { ); } + #[test] + fn dist_compiler_docs() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("dist") + .path("rustc-docs") + .args(&["--set", "build.compiler-docs=true"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> rustc 2 + [build] rustc 1 -> rustc 2 + [doc] rustc 1 -> Rustdoc 2 + [doc] rustc 1 -> Rustfmt 2 + [build] rustc 1 -> error-index 2 + [doc] rustc 1 -> error-index 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] rustc 1 -> Cargo 2 + [doc] cargo (book) + [doc] rustc 1 -> Clippy 2 + [doc] clippy (book) + [doc] rustc 1 -> Miri 2 + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> Tidy 2 + [doc] rustc 1 -> Bootstrap 2 + [doc] rustc 1 -> releases 2 + [doc] rustc 1 -> RunMakeSupport 2 + [doc] rustc 1 -> BuildHelper 2 + [doc] rustc 1 -> Compiletest 2 + [build] rustc 0 -> RustInstaller 1 + " + ); + } + #[test] fn dist_extended() { let ctx = TestCtx::new(); @@ -1572,24 +1624,35 @@ mod snapshot { ctx .config("dist") .path("rustc-docs") - .render_steps(), @" + .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) [build] rustdoc 1 - [doc] rustc 1 -> rustc 2 + [doc] rustc 1 -> standalone 2 + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> rustc 2 - [doc] rustc 1 -> Rustdoc 2 - [doc] rustc 1 -> Rustfmt 2 - [doc] rustc 1 -> Clippy 2 - [doc] rustc 1 -> Miri 2 - [doc] rustc 1 -> Cargo 2 - [doc] rustc 1 -> Tidy 2 - [doc] rustc 1 -> Bootstrap 2 - [doc] rustc 1 -> BuildHelper 2 - [doc] rustc 1 -> Compiletest 2 - [doc] rustc 1 -> RunMakeSupport 2 - [doc] rustc 1 -> CompilerWithTools 2 + [build] rustc 1 -> error-index 2 + [doc] rustc 1 -> error-index 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 "); } @@ -2377,31 +2440,6 @@ mod snapshot { "); } - #[test] - fn doc_compiler_with_tools() { - let ctx = TestCtx::new(); - insta::assert_snapshot!( - ctx.config("doc") - .arg("compiler-with-tools") - .render_steps(), @" - [build] rustdoc 0 - [doc] rustc 0 -> rustc 1 - [build] llvm - [build] rustc 0 -> rustc 1 - [doc] rustc 0 -> Rustdoc 1 - [doc] rustc 0 -> Rustfmt 1 - [doc] rustc 0 -> Clippy 1 - [doc] rustc 0 -> Miri 1 - [doc] rustc 0 -> Cargo 1 - [doc] rustc 0 -> Tidy 1 - [doc] rustc 0 -> Bootstrap 1 - [doc] rustc 0 -> BuildHelper 1 - [doc] rustc 0 -> Compiletest 1 - [doc] rustc 0 -> RunMakeSupport 1 - [doc] rustc 0 -> CompilerWithTools 1 - "); - } - #[test] fn doc_cargo_stage_1() { let ctx = TestCtx::new(); diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 1de879118301f..7207f7dd033ea 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -806,7 +806,7 @@ impl Session { self.out.join(target).join("json-doc") } - /// Output directory for combined compiler + tools docs. + /// Output directory for all documentation for a target pub(crate) fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("compiler-doc") }