"Fossies" - the Fresh Open Source Software Archive

Member "rustc-1.61.0-src/src/bootstrap/doc.rs" (18 May 2022, 33040 Bytes) of package /linux/misc/rustc-1.61.0-src.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Rust source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "doc.rs": 1.60.0_vs_1.61.0.

    1 //! Documentation generation for rustbuilder.
    2 //!
    3 //! This module implements generation for all bits and pieces of documentation
    4 //! for the Rust project. This notably includes suites like the rust book, the
    5 //! nomicon, rust by example, standalone documentation, etc.
    6 //!
    7 //! Everything here is basically just a shim around calling either `rustbook` or
    8 //! `rustdoc`.
    9 
   10 use std::collections::HashSet;
   11 use std::fs;
   12 use std::io;
   13 use std::path::{Path, PathBuf};
   14 
   15 use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
   16 use crate::cache::{Interned, INTERNER};
   17 use crate::compile;
   18 use crate::config::{Config, TargetSelection};
   19 use crate::tool::{self, prepare_tool_cargo, SourceType, Tool};
   20 use crate::util::{symlink_dir, t, up_to_date};
   21 use crate::Mode;
   22 
   23 macro_rules! submodule_helper {
   24     ($path:expr, submodule) => {
   25         $path
   26     };
   27     ($path:expr, submodule = $submodule:literal) => {
   28         $submodule
   29     };
   30 }
   31 
   32 macro_rules! book {
   33     ($($name:ident, $path:expr, $book_name:expr $(, submodule $(= $submodule:literal)? )? ;)+) => {
   34         $(
   35             #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
   36         pub struct $name {
   37             target: TargetSelection,
   38         }
   39 
   40         impl Step for $name {
   41             type Output = ();
   42             const DEFAULT: bool = true;
   43 
   44             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
   45                 let builder = run.builder;
   46                 run.path($path).default_condition(builder.config.docs)
   47             }
   48 
   49             fn make_run(run: RunConfig<'_>) {
   50                 run.builder.ensure($name {
   51                     target: run.target,
   52                 });
   53             }
   54 
   55             fn run(self, builder: &Builder<'_>) {
   56                 $(
   57                     let path = Path::new(submodule_helper!( $path, submodule $( = $submodule )? ));
   58                     builder.update_submodule(&path);
   59                 )?
   60                 builder.ensure(RustbookSrc {
   61                     target: self.target,
   62                     name: INTERNER.intern_str($book_name),
   63                     src: INTERNER.intern_path(builder.src.join($path)),
   64                 })
   65             }
   66         }
   67         )+
   68     }
   69 }
   70 
   71 // NOTE: When adding a book here, make sure to ALSO build the book by
   72 // adding a build step in `src/bootstrap/builder.rs`!
   73 // NOTE: Make sure to add the corresponding submodule when adding a new book.
   74 // FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules
   75 // and checking against it?).
   76 book!(
   77     CargoBook, "src/tools/cargo/src/doc", "cargo", submodule = "src/tools/cargo";
   78     EditionGuide, "src/doc/edition-guide", "edition-guide", submodule;
   79     EmbeddedBook, "src/doc/embedded-book", "embedded-book", submodule;
   80     Nomicon, "src/doc/nomicon", "nomicon", submodule;
   81     Reference, "src/doc/reference", "reference", submodule;
   82     RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule;
   83     RustdocBook, "src/doc/rustdoc", "rustdoc";
   84 );
   85 
   86 fn open(builder: &Builder<'_>, path: impl AsRef<Path>) {
   87     if builder.config.dry_run || !builder.config.cmd.open() {
   88         return;
   89     }
   90 
   91     let path = path.as_ref();
   92     builder.info(&format!("Opening doc {}", path.display()));
   93     if let Err(err) = opener::open(path) {
   94         builder.info(&format!("{}\n", err));
   95     }
   96 }
   97 
   98 // "library/std" -> ["library", "std"]
   99 //
  100 // Used for deciding whether a particular step is one requested by the user on
  101 // the `x.py doc` command line, which determines whether `--open` will open that
  102 // page.
  103 pub(crate) fn components_simplified(path: &PathBuf) -> Vec<&str> {
  104     path.iter().map(|component| component.to_str().unwrap_or("???")).collect()
  105 }
  106 
  107 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  108 pub struct UnstableBook {
  109     target: TargetSelection,
  110 }
  111 
  112 impl Step for UnstableBook {
  113     type Output = ();
  114     const DEFAULT: bool = true;
  115 
  116     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  117         let builder = run.builder;
  118         run.path("src/doc/unstable-book").default_condition(builder.config.docs)
  119     }
  120 
  121     fn make_run(run: RunConfig<'_>) {
  122         run.builder.ensure(UnstableBook { target: run.target });
  123     }
  124 
  125     fn run(self, builder: &Builder<'_>) {
  126         builder.ensure(UnstableBookGen { target: self.target });
  127         builder.ensure(RustbookSrc {
  128             target: self.target,
  129             name: INTERNER.intern_str("unstable-book"),
  130             src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
  131         })
  132     }
  133 }
  134 
  135 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  136 struct RustbookSrc {
  137     target: TargetSelection,
  138     name: Interned<String>,
  139     src: Interned<PathBuf>,
  140 }
  141 
  142 impl Step for RustbookSrc {
  143     type Output = ();
  144 
  145     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  146         run.never()
  147     }
  148 
  149     /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
  150     ///
  151     /// This will not actually generate any documentation if the documentation has
  152     /// already been generated.
  153     fn run(self, builder: &Builder<'_>) {
  154         let target = self.target;
  155         let name = self.name;
  156         let src = self.src;
  157         let out = builder.doc_out(target);
  158         t!(fs::create_dir_all(&out));
  159 
  160         let out = out.join(name);
  161         let index = out.join("index.html");
  162         let rustbook = builder.tool_exe(Tool::Rustbook);
  163         let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
  164         if builder.config.dry_run || up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
  165             return;
  166         }
  167         builder.info(&format!("Rustbook ({}) - {}", target, name));
  168         let _ = fs::remove_dir_all(&out);
  169 
  170         builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
  171     }
  172 }
  173 
  174 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  175 pub struct TheBook {
  176     compiler: Compiler,
  177     target: TargetSelection,
  178 }
  179 
  180 impl Step for TheBook {
  181     type Output = ();
  182     const DEFAULT: bool = true;
  183 
  184     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  185         let builder = run.builder;
  186         run.path("src/doc/book").default_condition(builder.config.docs)
  187     }
  188 
  189     fn make_run(run: RunConfig<'_>) {
  190         run.builder.ensure(TheBook {
  191             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
  192             target: run.target,
  193         });
  194     }
  195 
  196     /// Builds the book and associated stuff.
  197     ///
  198     /// We need to build:
  199     ///
  200     /// * Book
  201     /// * Older edition redirects
  202     /// * Version info and CSS
  203     /// * Index page
  204     /// * Redirect pages
  205     fn run(self, builder: &Builder<'_>) {
  206         let relative_path = Path::new("src").join("doc").join("book");
  207         builder.update_submodule(&relative_path);
  208 
  209         let compiler = self.compiler;
  210         let target = self.target;
  211 
  212         // build book
  213         builder.ensure(RustbookSrc {
  214             target,
  215             name: INTERNER.intern_str("book"),
  216             src: INTERNER.intern_path(builder.src.join(&relative_path)),
  217         });
  218 
  219         // building older edition redirects
  220         for edition in &["first-edition", "second-edition", "2018-edition"] {
  221             builder.ensure(RustbookSrc {
  222                 target,
  223                 name: INTERNER.intern_string(format!("book/{}", edition)),
  224                 src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)),
  225             });
  226         }
  227 
  228         // build the version info page and CSS
  229         builder.ensure(Standalone { compiler, target });
  230 
  231         // build the redirect pages
  232         builder.info(&format!("Documenting book redirect pages ({})", target));
  233         for file in t!(fs::read_dir(builder.src.join(&relative_path).join("redirects"))) {
  234             let file = t!(file);
  235             let path = file.path();
  236             let path = path.to_str().unwrap();
  237 
  238             invoke_rustdoc(builder, compiler, target, path);
  239         }
  240 
  241         if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
  242             let out = builder.doc_out(target);
  243             let index = out.join("book").join("index.html");
  244             open(builder, &index);
  245         }
  246     }
  247 }
  248 
  249 fn invoke_rustdoc(
  250     builder: &Builder<'_>,
  251     compiler: Compiler,
  252     target: TargetSelection,
  253     markdown: &str,
  254 ) {
  255     let out = builder.doc_out(target);
  256 
  257     let path = builder.src.join("src/doc").join(markdown);
  258 
  259     let header = builder.src.join("src/doc/redirect.inc");
  260     let footer = builder.src.join("src/doc/footer.inc");
  261     let version_info = out.join("version_info.html");
  262 
  263     let mut cmd = builder.rustdoc_cmd(compiler);
  264 
  265     let out = out.join("book");
  266 
  267     cmd.arg("--html-after-content")
  268         .arg(&footer)
  269         .arg("--html-before-content")
  270         .arg(&version_info)
  271         .arg("--html-in-header")
  272         .arg(&header)
  273         .arg("--markdown-no-toc")
  274         .arg("--markdown-playground-url")
  275         .arg("https://play.rust-lang.org/")
  276         .arg("-o")
  277         .arg(&out)
  278         .arg(&path)
  279         .arg("--markdown-css")
  280         .arg("../rust.css");
  281 
  282     if !builder.config.docs_minification {
  283         cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
  284     }
  285 
  286     builder.run(&mut cmd);
  287 }
  288 
  289 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  290 pub struct Standalone {
  291     compiler: Compiler,
  292     target: TargetSelection,
  293 }
  294 
  295 impl Step for Standalone {
  296     type Output = ();
  297     const DEFAULT: bool = true;
  298 
  299     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  300         let builder = run.builder;
  301         run.path("src/doc").default_condition(builder.config.docs)
  302     }
  303 
  304     fn make_run(run: RunConfig<'_>) {
  305         run.builder.ensure(Standalone {
  306             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
  307             target: run.target,
  308         });
  309     }
  310 
  311     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
  312     /// for the `target` into `out`.
  313     ///
  314     /// This will list all of `src/doc` looking for markdown files and appropriately
  315     /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
  316     /// `STAMP` along with providing the various header/footer HTML we've customized.
  317     ///
  318     /// In the end, this is just a glorified wrapper around rustdoc!
  319     fn run(self, builder: &Builder<'_>) {
  320         let target = self.target;
  321         let compiler = self.compiler;
  322         builder.info(&format!("Documenting standalone ({})", target));
  323         let out = builder.doc_out(target);
  324         t!(fs::create_dir_all(&out));
  325 
  326         let favicon = builder.src.join("src/doc/favicon.inc");
  327         let footer = builder.src.join("src/doc/footer.inc");
  328         let full_toc = builder.src.join("src/doc/full-toc.inc");
  329         t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
  330 
  331         let version_input = builder.src.join("src/doc/version_info.html.template");
  332         let version_info = out.join("version_info.html");
  333 
  334         if !builder.config.dry_run && !up_to_date(&version_input, &version_info) {
  335             let info = t!(fs::read_to_string(&version_input))
  336                 .replace("VERSION", &builder.rust_release())
  337                 .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or(""))
  338                 .replace("STAMP", builder.rust_info.sha().unwrap_or(""));
  339             t!(fs::write(&version_info, &info));
  340         }
  341 
  342         for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
  343             let file = t!(file);
  344             let path = file.path();
  345             let filename = path.file_name().unwrap().to_str().unwrap();
  346             if !filename.ends_with(".md") || filename == "README.md" {
  347                 continue;
  348             }
  349 
  350             let html = out.join(filename).with_extension("html");
  351             let rustdoc = builder.rustdoc(compiler);
  352             if up_to_date(&path, &html)
  353                 && up_to_date(&footer, &html)
  354                 && up_to_date(&favicon, &html)
  355                 && up_to_date(&full_toc, &html)
  356                 && (builder.config.dry_run || up_to_date(&version_info, &html))
  357                 && (builder.config.dry_run || up_to_date(&rustdoc, &html))
  358             {
  359                 continue;
  360             }
  361 
  362             let mut cmd = builder.rustdoc_cmd(compiler);
  363             // Needed for --index-page flag
  364             cmd.arg("-Z").arg("unstable-options");
  365 
  366             cmd.arg("--html-after-content")
  367                 .arg(&footer)
  368                 .arg("--html-before-content")
  369                 .arg(&version_info)
  370                 .arg("--html-in-header")
  371                 .arg(&favicon)
  372                 .arg("--markdown-no-toc")
  373                 .arg("--index-page")
  374                 .arg(&builder.src.join("src/doc/index.md"))
  375                 .arg("--markdown-playground-url")
  376                 .arg("https://play.rust-lang.org/")
  377                 .arg("-o")
  378                 .arg(&out)
  379                 .arg(&path);
  380 
  381             if !builder.config.docs_minification {
  382                 cmd.arg("--disable-minification");
  383             }
  384 
  385             if filename == "not_found.md" {
  386                 cmd.arg("--markdown-css")
  387                     .arg(format!("https://doc.rust-lang.org/rustdoc{}.css", &builder.version))
  388                     .arg("--markdown-css")
  389                     .arg("https://doc.rust-lang.org/rust.css");
  390             } else {
  391                 cmd.arg("--markdown-css")
  392                     .arg(format!("rustdoc{}.css", &builder.version))
  393                     .arg("--markdown-css")
  394                     .arg("rust.css");
  395             }
  396             builder.run(&mut cmd);
  397         }
  398 
  399         // We open doc/index.html as the default if invoked as `x.py doc --open`
  400         // with no particular explicit doc requested (e.g. library/core).
  401         if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
  402             let index = out.join("index.html");
  403             open(builder, &index);
  404         }
  405     }
  406 }
  407 
  408 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  409 pub struct Std {
  410     pub stage: u32,
  411     pub target: TargetSelection,
  412 }
  413 
  414 impl Step for Std {
  415     type Output = ();
  416     const DEFAULT: bool = true;
  417 
  418     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  419         let builder = run.builder;
  420         run.all_krates("test").default_condition(builder.config.docs)
  421     }
  422 
  423     fn make_run(run: RunConfig<'_>) {
  424         run.builder.ensure(Std { stage: run.builder.top_stage, target: run.target });
  425     }
  426 
  427     /// Compile all standard library documentation.
  428     ///
  429     /// This will generate all documentation for the standard library and its
  430     /// dependencies. This is largely just a wrapper around `cargo doc`.
  431     fn run(self, builder: &Builder<'_>) {
  432         let stage = self.stage;
  433         let target = self.target;
  434         builder.info(&format!("Documenting stage{} std ({})", stage, target));
  435         if builder.no_std(target) == Some(true) {
  436             panic!(
  437                 "building std documentation for no_std target {target} is not supported\n\
  438                  Set `docs = false` in the config to disable documentation."
  439             );
  440         }
  441         let out = builder.doc_out(target);
  442         t!(fs::create_dir_all(&out));
  443         let compiler = builder.compiler(stage, builder.config.build);
  444 
  445         builder.ensure(compile::Std { compiler, target });
  446         let out_dir = builder.stage_out(compiler, Mode::Std).join(target.triple).join("doc");
  447 
  448         t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
  449 
  450         let run_cargo_rustdoc_for = |package: &str| {
  451             let mut cargo =
  452                 builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustdoc");
  453             compile::std_cargo(builder, target, compiler.stage, &mut cargo);
  454 
  455             cargo
  456                 .arg("-p")
  457                 .arg(package)
  458                 .arg("-Zskip-rustdoc-fingerprint")
  459                 .arg("--")
  460                 .arg("--markdown-css")
  461                 .arg("rust.css")
  462                 .arg("--markdown-no-toc")
  463                 .arg("-Z")
  464                 .arg("unstable-options")
  465                 .arg("--resource-suffix")
  466                 .arg(&builder.version)
  467                 .arg("--index-page")
  468                 .arg(&builder.src.join("src/doc/index.md"));
  469 
  470             if !builder.config.docs_minification {
  471                 cargo.arg("--disable-minification");
  472             }
  473 
  474             builder.run(&mut cargo.into());
  475         };
  476 
  477         let paths = builder
  478             .paths
  479             .iter()
  480             .map(components_simplified)
  481             .filter_map(|path| {
  482                 if path.get(0) == Some(&"library") {
  483                     Some(path[1].to_owned())
  484                 } else if !path.is_empty() {
  485                     Some(path[0].to_owned())
  486                 } else {
  487                     None
  488                 }
  489             })
  490             .collect::<Vec<_>>();
  491 
  492         // Only build the following crates. While we could just iterate over the
  493         // folder structure, that would also build internal crates that we do
  494         // not want to show in documentation. These crates will later be visited
  495         // by the rustc step, so internal documentation will show them.
  496         //
  497         // Note that the order here is important! The crates need to be
  498         // processed starting from the leaves, otherwise rustdoc will not
  499         // create correct links between crates because rustdoc depends on the
  500         // existence of the output directories to know if it should be a local
  501         // or remote link.
  502         let krates = ["core", "alloc", "std", "proc_macro", "test"];
  503         for krate in &krates {
  504             run_cargo_rustdoc_for(krate);
  505             if paths.iter().any(|p| p == krate) {
  506                 // No need to document more of the libraries if we have the one we want.
  507                 break;
  508             }
  509         }
  510         builder.cp_r(&out_dir, &out);
  511 
  512         // Look for library/std, library/core etc in the `x.py doc` arguments and
  513         // open the corresponding rendered docs.
  514         for requested_crate in paths {
  515             if krates.iter().any(|k| *k == requested_crate.as_str()) {
  516                 let index = out.join(requested_crate).join("index.html");
  517                 open(builder, &index);
  518             }
  519         }
  520     }
  521 }
  522 
  523 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  524 pub struct Rustc {
  525     pub stage: u32,
  526     pub target: TargetSelection,
  527 }
  528 
  529 impl Step for Rustc {
  530     type Output = ();
  531     const DEFAULT: bool = true;
  532     const ONLY_HOSTS: bool = true;
  533 
  534     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  535         let builder = run.builder;
  536         run.krate("rustc-main").path("compiler").default_condition(builder.config.compiler_docs)
  537     }
  538 
  539     fn make_run(run: RunConfig<'_>) {
  540         run.builder.ensure(Rustc { stage: run.builder.top_stage, target: run.target });
  541     }
  542 
  543     /// Generates compiler documentation.
  544     ///
  545     /// This will generate all documentation for compiler and dependencies.
  546     /// Compiler documentation is distributed separately, so we make sure
  547     /// we do not merge it with the other documentation from std, test and
  548     /// proc_macros. This is largely just a wrapper around `cargo doc`.
  549     fn run(self, builder: &Builder<'_>) {
  550         let stage = self.stage;
  551         let target = self.target;
  552         builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
  553 
  554         let paths = builder
  555             .paths
  556             .iter()
  557             .map(components_simplified)
  558             .filter_map(|path| {
  559                 if path.get(0) == Some(&"compiler") {
  560                     path.get(1).map(|p| p.to_owned())
  561                 } else {
  562                     None
  563                 }
  564             })
  565             .collect::<Vec<_>>();
  566 
  567         // This is the intended out directory for compiler documentation.
  568         let out = builder.compiler_doc_out(target);
  569         t!(fs::create_dir_all(&out));
  570 
  571         // Build rustc.
  572         let compiler = builder.compiler(stage, builder.config.build);
  573         builder.ensure(compile::Rustc { compiler, target });
  574 
  575         // This uses a shared directory so that librustdoc documentation gets
  576         // correctly built and merged with the rustc documentation. This is
  577         // needed because rustdoc is built in a different directory from
  578         // rustc. rustdoc needs to be able to see everything, for example when
  579         // merging the search index, or generating local (relative) links.
  580         let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
  581         t!(symlink_dir_force(&builder.config, &out, &out_dir));
  582         // Cargo puts proc macros in `target/doc` even if you pass `--target`
  583         // explicitly (https://github.com/rust-lang/cargo/issues/7677).
  584         let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
  585         t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir));
  586 
  587         // Build cargo command.
  588         let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
  589         cargo.rustdocflag("--document-private-items");
  590         // Since we always pass --document-private-items, there's no need to warn about linking to private items.
  591         cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
  592         cargo.rustdocflag("--enable-index-page");
  593         cargo.rustdocflag("-Zunstable-options");
  594         cargo.rustdocflag("-Znormalize-docs");
  595         cargo.rustdocflag("--show-type-layout");
  596         cargo.rustdocflag("--generate-link-to-definition");
  597         compile::rustc_cargo(builder, &mut cargo, target);
  598         cargo.arg("-Zunstable-options");
  599         cargo.arg("-Zskip-rustdoc-fingerprint");
  600 
  601         // Only include compiler crates, no dependencies of those, such as `libc`.
  602         // Do link to dependencies on `docs.rs` however using `rustdoc-map`.
  603         cargo.arg("--no-deps");
  604         cargo.arg("-Zrustdoc-map");
  605 
  606         // FIXME: `-Zrustdoc-map` does not yet correctly work for transitive dependencies,
  607         // once this is no longer an issue the special case for `ena` can be removed.
  608         cargo.rustdocflag("--extern-html-root-url");
  609         cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
  610 
  611         let mut compiler_crates = HashSet::new();
  612 
  613         if paths.is_empty() {
  614             // Find dependencies for top level crates.
  615             for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] {
  616                 compiler_crates.extend(
  617                     builder
  618                         .in_tree_crates(root_crate, Some(target))
  619                         .into_iter()
  620                         .map(|krate| krate.name),
  621                 );
  622             }
  623         } else {
  624             for root_crate in paths {
  625                 if !builder.src.join("compiler").join(&root_crate).exists() {
  626                     builder.info(&format!(
  627                         "\tskipping - compiler/{} (unknown compiler crate)",
  628                         root_crate
  629                     ));
  630                 } else {
  631                     compiler_crates.extend(
  632                         builder
  633                             .in_tree_crates(root_crate, Some(target))
  634                             .into_iter()
  635                             .map(|krate| krate.name),
  636                     );
  637                 }
  638             }
  639         }
  640 
  641         let mut to_open = None;
  642         for krate in &compiler_crates {
  643             // Create all crate output directories first to make sure rustdoc uses
  644             // relative links.
  645             // FIXME: Cargo should probably do this itself.
  646             t!(fs::create_dir_all(out_dir.join(krate)));
  647             cargo.arg("-p").arg(krate);
  648             if to_open.is_none() {
  649                 to_open = Some(krate);
  650             }
  651         }
  652 
  653         builder.run(&mut cargo.into());
  654         // Let's open the first crate documentation page:
  655         if let Some(krate) = to_open {
  656             let index = out.join(krate).join("index.html");
  657             open(builder, &index);
  658         }
  659     }
  660 }
  661 
  662 macro_rules! tool_doc {
  663     ($tool: ident, $should_run: literal, $path: literal, [$($krate: literal),+ $(,)?] $(,)?) => {
  664         #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  665         pub struct $tool {
  666             stage: u32,
  667             target: TargetSelection,
  668         }
  669 
  670         impl Step for $tool {
  671             type Output = ();
  672             const DEFAULT: bool = true;
  673             const ONLY_HOSTS: bool = true;
  674 
  675             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  676                 let builder = run.builder;
  677                 run.krate($should_run).default_condition(builder.config.compiler_docs)
  678             }
  679 
  680             fn make_run(run: RunConfig<'_>) {
  681                 run.builder.ensure($tool { stage: run.builder.top_stage, target: run.target });
  682             }
  683 
  684             /// Generates compiler documentation.
  685             ///
  686             /// This will generate all documentation for compiler and dependencies.
  687             /// Compiler documentation is distributed separately, so we make sure
  688             /// we do not merge it with the other documentation from std, test and
  689             /// proc_macros. This is largely just a wrapper around `cargo doc`.
  690             fn run(self, builder: &Builder<'_>) {
  691                 let stage = self.stage;
  692                 let target = self.target;
  693                 builder.info(
  694                     &format!(
  695                         "Documenting stage{} {} ({})",
  696                         stage,
  697                         stringify!($tool).to_lowercase(),
  698                         target,
  699                     ),
  700                 );
  701 
  702                 // This is the intended out directory for compiler documentation.
  703                 let out = builder.compiler_doc_out(target);
  704                 t!(fs::create_dir_all(&out));
  705 
  706                 let compiler = builder.compiler(stage, builder.config.build);
  707 
  708                 // Build rustc docs so that we generate relative links.
  709                 builder.ensure(Rustc { stage, target });
  710 
  711                 // Symlink compiler docs to the output directory of rustdoc documentation.
  712                 let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
  713                 t!(fs::create_dir_all(&out_dir));
  714                 t!(symlink_dir_force(&builder.config, &out, &out_dir));
  715 
  716                 // Build cargo command.
  717                 let mut cargo = prepare_tool_cargo(
  718                     builder,
  719                     compiler,
  720                     Mode::ToolRustc,
  721                     target,
  722                     "doc",
  723                     $path,
  724                     SourceType::InTree,
  725                     &[],
  726                 );
  727 
  728                 cargo.arg("-Zskip-rustdoc-fingerprint");
  729                 // Only include compiler crates, no dependencies of those, such as `libc`.
  730                 cargo.arg("--no-deps");
  731                 $(
  732                     cargo.arg("-p").arg($krate);
  733                 )+
  734 
  735                 cargo.rustdocflag("--document-private-items");
  736                 cargo.rustdocflag("--enable-index-page");
  737                 cargo.rustdocflag("--show-type-layout");
  738                 cargo.rustdocflag("--generate-link-to-definition");
  739                 cargo.rustdocflag("-Zunstable-options");
  740                 builder.run(&mut cargo.into());
  741             }
  742         }
  743     }
  744 }
  745 
  746 tool_doc!(Rustdoc, "rustdoc-tool", "src/tools/rustdoc", ["rustdoc", "rustdoc-json-types"]);
  747 tool_doc!(
  748     Rustfmt,
  749     "rustfmt-nightly",
  750     "src/tools/rustfmt",
  751     ["rustfmt-nightly", "rustfmt-config_proc_macro"],
  752 );
  753 tool_doc!(Clippy, "clippy", "src/tools/clippy", ["clippy_utils"]);
  754 
  755 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
  756 pub struct ErrorIndex {
  757     pub target: TargetSelection,
  758 }
  759 
  760 impl Step for ErrorIndex {
  761     type Output = ();
  762     const DEFAULT: bool = true;
  763     const ONLY_HOSTS: bool = true;
  764 
  765     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  766         let builder = run.builder;
  767         run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
  768     }
  769 
  770     fn make_run(run: RunConfig<'_>) {
  771         let target = run.target;
  772         run.builder.ensure(ErrorIndex { target });
  773     }
  774 
  775     /// Generates the HTML rendered error-index by running the
  776     /// `error_index_generator` tool.
  777     fn run(self, builder: &Builder<'_>) {
  778         builder.info(&format!("Documenting error index ({})", self.target));
  779         let out = builder.doc_out(self.target);
  780         t!(fs::create_dir_all(&out));
  781         let mut index = tool::ErrorIndex::command(builder);
  782         index.arg("html");
  783         index.arg(out.join("error-index.html"));
  784         index.arg(&builder.version);
  785 
  786         builder.run(&mut index);
  787     }
  788 }
  789 
  790 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  791 pub struct UnstableBookGen {
  792     target: TargetSelection,
  793 }
  794 
  795 impl Step for UnstableBookGen {
  796     type Output = ();
  797     const DEFAULT: bool = true;
  798     const ONLY_HOSTS: bool = true;
  799 
  800     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  801         let builder = run.builder;
  802         run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
  803     }
  804 
  805     fn make_run(run: RunConfig<'_>) {
  806         run.builder.ensure(UnstableBookGen { target: run.target });
  807     }
  808 
  809     fn run(self, builder: &Builder<'_>) {
  810         let target = self.target;
  811 
  812         builder.info(&format!("Generating unstable book md files ({})", target));
  813         let out = builder.md_doc_out(target).join("unstable-book");
  814         builder.create_dir(&out);
  815         builder.remove_dir(&out);
  816         let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
  817         cmd.arg(builder.src.join("library"));
  818         cmd.arg(builder.src.join("compiler"));
  819         cmd.arg(builder.src.join("src"));
  820         cmd.arg(out);
  821 
  822         builder.run(&mut cmd);
  823     }
  824 }
  825 
  826 fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
  827     if config.dry_run {
  828         return Ok(());
  829     }
  830     if let Ok(m) = fs::symlink_metadata(dst) {
  831         if m.file_type().is_dir() {
  832             fs::remove_dir_all(dst)?;
  833         } else {
  834             // handle directory junctions on windows by falling back to
  835             // `remove_dir`.
  836             fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?;
  837         }
  838     }
  839 
  840     symlink_dir(config, src, dst)
  841 }
  842 
  843 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
  844 pub struct RustcBook {
  845     pub compiler: Compiler,
  846     pub target: TargetSelection,
  847     pub validate: bool,
  848 }
  849 
  850 impl Step for RustcBook {
  851     type Output = ();
  852     const DEFAULT: bool = true;
  853     const ONLY_HOSTS: bool = true;
  854 
  855     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  856         let builder = run.builder;
  857         run.path("src/doc/rustc").default_condition(builder.config.docs)
  858     }
  859 
  860     fn make_run(run: RunConfig<'_>) {
  861         run.builder.ensure(RustcBook {
  862             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
  863             target: run.target,
  864             validate: false,
  865         });
  866     }
  867 
  868     /// Builds the rustc book.
  869     ///
  870     /// The lints are auto-generated by a tool, and then merged into the book
  871     /// in the "md-doc" directory in the build output directory. Then
  872     /// "rustbook" is used to convert it to HTML.
  873     fn run(self, builder: &Builder<'_>) {
  874         let out_base = builder.md_doc_out(self.target).join("rustc");
  875         t!(fs::create_dir_all(&out_base));
  876         let out_listing = out_base.join("src/lints");
  877         builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base);
  878         builder.info(&format!("Generating lint docs ({})", self.target));
  879 
  880         let rustc = builder.rustc(self.compiler);
  881         // The tool runs `rustc` for extracting output examples, so it needs a
  882         // functional sysroot.
  883         builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
  884         let mut cmd = builder.tool_cmd(Tool::LintDocs);
  885         cmd.arg("--src");
  886         cmd.arg(builder.src.join("compiler"));
  887         cmd.arg("--out");
  888         cmd.arg(&out_listing);
  889         cmd.arg("--rustc");
  890         cmd.arg(&rustc);
  891         cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
  892         if builder.config.verbose() {
  893             cmd.arg("--verbose");
  894         }
  895         if self.validate {
  896             cmd.arg("--validate");
  897         }
  898         // If the lib directories are in an unusual location (changed in
  899         // config.toml), then this needs to explicitly update the dylib search
  900         // path.
  901         builder.add_rustc_lib_path(self.compiler, &mut cmd);
  902         builder.run(&mut cmd);
  903         // Run rustbook/mdbook to generate the HTML pages.
  904         builder.ensure(RustbookSrc {
  905             target: self.target,
  906             name: INTERNER.intern_str("rustc"),
  907             src: INTERNER.intern_path(out_base),
  908         });
  909         if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
  910             let out = builder.doc_out(self.target);
  911             let index = out.join("rustc").join("index.html");
  912             open(builder, &index);
  913         }
  914     }
  915 }