"Fossies" - the Fresh Open Source Software Archive

Member "rustc-1.72.1-src/src/bootstrap/doc.rs" (13 Sep 2023, 38066 Bytes) of package /linux/misc/rustc-1.72.1-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 last Fossies "Diffs" side-by-side code changes report for "doc.rs": 1.71.1_vs_1.72.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::fs;
   11 use std::path::{Path, PathBuf};
   12 
   13 use crate::builder::crate_description;
   14 use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
   15 use crate::cache::{Interned, INTERNER};
   16 use crate::compile;
   17 use crate::compile::make_run_crates;
   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                     parent: Some(self),
   65                 })
   66             }
   67         }
   68         )+
   69     }
   70 }
   71 
   72 // NOTE: When adding a book here, make sure to ALSO build the book by
   73 // adding a build step in `src/bootstrap/builder.rs`!
   74 // NOTE: Make sure to add the corresponding submodule when adding a new book.
   75 // FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules
   76 // and checking against it?).
   77 book!(
   78     CargoBook, "src/tools/cargo/src/doc", "cargo", submodule = "src/tools/cargo";
   79     ClippyBook, "src/tools/clippy/book", "clippy";
   80     EditionGuide, "src/doc/edition-guide", "edition-guide", submodule;
   81     EmbeddedBook, "src/doc/embedded-book", "embedded-book", submodule;
   82     Nomicon, "src/doc/nomicon", "nomicon", submodule;
   83     Reference, "src/doc/reference", "reference", submodule;
   84     RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule;
   85     RustdocBook, "src/doc/rustdoc", "rustdoc";
   86     StyleGuide, "src/doc/style-guide", "style-guide";
   87 );
   88 
   89 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
   90 pub struct UnstableBook {
   91     target: TargetSelection,
   92 }
   93 
   94 impl Step for UnstableBook {
   95     type Output = ();
   96     const DEFAULT: bool = true;
   97 
   98     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
   99         let builder = run.builder;
  100         run.path("src/doc/unstable-book").default_condition(builder.config.docs)
  101     }
  102 
  103     fn make_run(run: RunConfig<'_>) {
  104         run.builder.ensure(UnstableBook { target: run.target });
  105     }
  106 
  107     fn run(self, builder: &Builder<'_>) {
  108         builder.ensure(UnstableBookGen { target: self.target });
  109         builder.ensure(RustbookSrc {
  110             target: self.target,
  111             name: INTERNER.intern_str("unstable-book"),
  112             src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
  113             parent: Some(self),
  114         })
  115     }
  116 }
  117 
  118 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  119 struct RustbookSrc<P: Step> {
  120     target: TargetSelection,
  121     name: Interned<String>,
  122     src: Interned<PathBuf>,
  123     parent: Option<P>,
  124 }
  125 
  126 impl<P: Step> Step for RustbookSrc<P> {
  127     type Output = ();
  128 
  129     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  130         run.never()
  131     }
  132 
  133     /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
  134     ///
  135     /// This will not actually generate any documentation if the documentation has
  136     /// already been generated.
  137     fn run(self, builder: &Builder<'_>) {
  138         let target = self.target;
  139         let name = self.name;
  140         let src = self.src;
  141         let out = builder.doc_out(target);
  142         t!(fs::create_dir_all(&out));
  143 
  144         let out = out.join(name);
  145         let index = out.join("index.html");
  146         let rustbook = builder.tool_exe(Tool::Rustbook);
  147         let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
  148 
  149         if !builder.config.dry_run() && !(up_to_date(&src, &index) || up_to_date(&rustbook, &index))
  150         {
  151             builder.info(&format!("Rustbook ({}) - {}", target, name));
  152             let _ = fs::remove_dir_all(&out);
  153 
  154             builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
  155         }
  156 
  157         if self.parent.is_some() {
  158             builder.maybe_open_in_browser::<P>(index)
  159         }
  160     }
  161 }
  162 
  163 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  164 pub struct TheBook {
  165     compiler: Compiler,
  166     target: TargetSelection,
  167 }
  168 
  169 impl Step for TheBook {
  170     type Output = ();
  171     const DEFAULT: bool = true;
  172 
  173     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  174         let builder = run.builder;
  175         run.path("src/doc/book").default_condition(builder.config.docs)
  176     }
  177 
  178     fn make_run(run: RunConfig<'_>) {
  179         run.builder.ensure(TheBook {
  180             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
  181             target: run.target,
  182         });
  183     }
  184 
  185     /// Builds the book and associated stuff.
  186     ///
  187     /// We need to build:
  188     ///
  189     /// * Book
  190     /// * Older edition redirects
  191     /// * Version info and CSS
  192     /// * Index page
  193     /// * Redirect pages
  194     fn run(self, builder: &Builder<'_>) {
  195         let relative_path = Path::new("src").join("doc").join("book");
  196         builder.update_submodule(&relative_path);
  197 
  198         let compiler = self.compiler;
  199         let target = self.target;
  200 
  201         // build book
  202         builder.ensure(RustbookSrc {
  203             target,
  204             name: INTERNER.intern_str("book"),
  205             src: INTERNER.intern_path(builder.src.join(&relative_path)),
  206             parent: Some(self),
  207         });
  208 
  209         // building older edition redirects
  210         for edition in &["first-edition", "second-edition", "2018-edition"] {
  211             builder.ensure(RustbookSrc {
  212                 target,
  213                 name: INTERNER.intern_string(format!("book/{}", edition)),
  214                 src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)),
  215                 // There should only be one book that is marked as the parent for each target, so
  216                 // treat the other editions as not having a parent.
  217                 parent: Option::<Self>::None,
  218             });
  219         }
  220 
  221         // build the version info page and CSS
  222         let shared_assets = builder.ensure(SharedAssets { target });
  223 
  224         // build the redirect pages
  225         builder.msg_doc(compiler, "book redirect pages", target);
  226         for file in t!(fs::read_dir(builder.src.join(&relative_path).join("redirects"))) {
  227             let file = t!(file);
  228             let path = file.path();
  229             let path = path.to_str().unwrap();
  230 
  231             invoke_rustdoc(builder, compiler, &shared_assets, target, path);
  232         }
  233     }
  234 }
  235 
  236 fn invoke_rustdoc(
  237     builder: &Builder<'_>,
  238     compiler: Compiler,
  239     shared_assets: &SharedAssetsPaths,
  240     target: TargetSelection,
  241     markdown: &str,
  242 ) {
  243     let out = builder.doc_out(target);
  244 
  245     let path = builder.src.join("src/doc").join(markdown);
  246 
  247     let header = builder.src.join("src/doc/redirect.inc");
  248     let footer = builder.src.join("src/doc/footer.inc");
  249 
  250     let mut cmd = builder.rustdoc_cmd(compiler);
  251 
  252     let out = out.join("book");
  253 
  254     cmd.arg("--html-after-content")
  255         .arg(&footer)
  256         .arg("--html-before-content")
  257         .arg(&shared_assets.version_info)
  258         .arg("--html-in-header")
  259         .arg(&header)
  260         .arg("--markdown-no-toc")
  261         .arg("--markdown-playground-url")
  262         .arg("https://play.rust-lang.org/")
  263         .arg("-o")
  264         .arg(&out)
  265         .arg(&path)
  266         .arg("--markdown-css")
  267         .arg("../rust.css");
  268 
  269     if !builder.config.docs_minification {
  270         cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
  271     }
  272 
  273     builder.run(&mut cmd);
  274 }
  275 
  276 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  277 pub struct Standalone {
  278     compiler: Compiler,
  279     target: TargetSelection,
  280 }
  281 
  282 impl Step for Standalone {
  283     type Output = ();
  284     const DEFAULT: bool = true;
  285 
  286     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  287         let builder = run.builder;
  288         run.path("src/doc").alias("standalone").default_condition(builder.config.docs)
  289     }
  290 
  291     fn make_run(run: RunConfig<'_>) {
  292         run.builder.ensure(Standalone {
  293             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
  294             target: run.target,
  295         });
  296     }
  297 
  298     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
  299     /// for the `target` into `out`.
  300     ///
  301     /// This will list all of `src/doc` looking for markdown files and appropriately
  302     /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
  303     /// `STAMP` along with providing the various header/footer HTML we've customized.
  304     ///
  305     /// In the end, this is just a glorified wrapper around rustdoc!
  306     fn run(self, builder: &Builder<'_>) {
  307         let target = self.target;
  308         let compiler = self.compiler;
  309         builder.msg_doc(compiler, "standalone", target);
  310         let out = builder.doc_out(target);
  311         t!(fs::create_dir_all(&out));
  312 
  313         let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
  314 
  315         let favicon = builder.src.join("src/doc/favicon.inc");
  316         let footer = builder.src.join("src/doc/footer.inc");
  317         let full_toc = builder.src.join("src/doc/full-toc.inc");
  318 
  319         for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
  320             let file = t!(file);
  321             let path = file.path();
  322             let filename = path.file_name().unwrap().to_str().unwrap();
  323             if !filename.ends_with(".md") || filename == "README.md" {
  324                 continue;
  325             }
  326 
  327             let html = out.join(filename).with_extension("html");
  328             let rustdoc = builder.rustdoc(compiler);
  329             if up_to_date(&path, &html)
  330                 && up_to_date(&footer, &html)
  331                 && up_to_date(&favicon, &html)
  332                 && up_to_date(&full_toc, &html)
  333                 && (builder.config.dry_run() || up_to_date(&version_info, &html))
  334                 && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
  335             {
  336                 continue;
  337             }
  338 
  339             let mut cmd = builder.rustdoc_cmd(compiler);
  340             // Needed for --index-page flag
  341             cmd.arg("-Z").arg("unstable-options");
  342 
  343             cmd.arg("--html-after-content")
  344                 .arg(&footer)
  345                 .arg("--html-before-content")
  346                 .arg(&version_info)
  347                 .arg("--html-in-header")
  348                 .arg(&favicon)
  349                 .arg("--markdown-no-toc")
  350                 .arg("--index-page")
  351                 .arg(&builder.src.join("src/doc/index.md"))
  352                 .arg("--markdown-playground-url")
  353                 .arg("https://play.rust-lang.org/")
  354                 .arg("-o")
  355                 .arg(&out)
  356                 .arg(&path);
  357 
  358             if !builder.config.docs_minification {
  359                 cmd.arg("--disable-minification");
  360             }
  361 
  362             if filename == "not_found.md" {
  363                 cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
  364             } else {
  365                 cmd.arg("--markdown-css").arg("rust.css");
  366             }
  367             builder.run(&mut cmd);
  368         }
  369 
  370         // We open doc/index.html as the default if invoked as `x.py doc --open`
  371         // with no particular explicit doc requested (e.g. library/core).
  372         if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
  373             let index = out.join("index.html");
  374             builder.open_in_browser(&index);
  375         }
  376     }
  377 }
  378 
  379 #[derive(Debug, Clone)]
  380 pub struct SharedAssetsPaths {
  381     pub version_info: PathBuf,
  382 }
  383 
  384 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  385 pub struct SharedAssets {
  386     target: TargetSelection,
  387 }
  388 
  389 impl Step for SharedAssets {
  390     type Output = SharedAssetsPaths;
  391     const DEFAULT: bool = false;
  392 
  393     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  394         // Other tasks depend on this, no need to execute it on its own
  395         run.never()
  396     }
  397 
  398     // Generate shared resources used by other pieces of documentation.
  399     fn run(self, builder: &Builder<'_>) -> Self::Output {
  400         let out = builder.doc_out(self.target);
  401 
  402         let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
  403         let version_info = out.join("version_info.html");
  404         if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
  405             let info = t!(fs::read_to_string(&version_input))
  406                 .replace("VERSION", &builder.rust_release())
  407                 .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
  408                 .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
  409             t!(fs::write(&version_info, &info));
  410         }
  411 
  412         builder.copy(&builder.src.join("src").join("doc").join("rust.css"), &out.join("rust.css"));
  413 
  414         SharedAssetsPaths { version_info }
  415     }
  416 }
  417 
  418 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
  419 pub struct Std {
  420     pub stage: u32,
  421     pub target: TargetSelection,
  422     pub format: DocumentationFormat,
  423     crates: Interned<Vec<String>>,
  424 }
  425 
  426 impl Std {
  427     pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self {
  428         Std { stage, target, format, crates: INTERNER.intern_list(vec![]) }
  429     }
  430 }
  431 
  432 impl Step for Std {
  433     type Output = ();
  434     const DEFAULT: bool = true;
  435 
  436     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  437         let builder = run.builder;
  438         run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
  439     }
  440 
  441     fn make_run(run: RunConfig<'_>) {
  442         run.builder.ensure(Std {
  443             stage: run.builder.top_stage,
  444             target: run.target,
  445             format: if run.builder.config.cmd.json() {
  446                 DocumentationFormat::JSON
  447             } else {
  448                 DocumentationFormat::HTML
  449             },
  450             crates: make_run_crates(&run, "library"),
  451         });
  452     }
  453 
  454     /// Compile all standard library documentation.
  455     ///
  456     /// This will generate all documentation for the standard library and its
  457     /// dependencies. This is largely just a wrapper around `cargo doc`.
  458     fn run(mut self, builder: &Builder<'_>) {
  459         let stage = self.stage;
  460         let target = self.target;
  461         let out = match self.format {
  462             DocumentationFormat::HTML => builder.doc_out(target),
  463             DocumentationFormat::JSON => builder.json_doc_out(target),
  464         };
  465 
  466         t!(fs::create_dir_all(&out));
  467 
  468         if self.format == DocumentationFormat::HTML {
  469             builder.ensure(SharedAssets { target: self.target });
  470         }
  471 
  472         let index_page = builder
  473             .src
  474             .join("src/doc/index.md")
  475             .into_os_string()
  476             .into_string()
  477             .expect("non-utf8 paths are unsupported");
  478         let mut extra_args = match self.format {
  479             DocumentationFormat::HTML => {
  480                 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
  481             }
  482             DocumentationFormat::JSON => vec!["--output-format", "json"],
  483         };
  484 
  485         if !builder.config.docs_minification {
  486             extra_args.push("--disable-minification");
  487         }
  488 
  489         doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates);
  490 
  491         // Don't open if the format is json
  492         if let DocumentationFormat::JSON = self.format {
  493             return;
  494         }
  495 
  496         // Look for library/std, library/core etc in the `x.py doc` arguments and
  497         // open the corresponding rendered docs.
  498         if self.crates.is_empty() {
  499             self.crates = INTERNER.intern_list(vec!["library".to_owned()]);
  500         };
  501 
  502         for requested_crate in &*self.crates {
  503             if requested_crate == "library" {
  504                 // For `x.py doc library --open`, open `std` by default.
  505                 let index = out.join("std").join("index.html");
  506                 builder.open_in_browser(index);
  507             } else if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
  508                 let index = out.join(requested_crate).join("index.html");
  509                 builder.open_in_browser(index);
  510             }
  511         }
  512     }
  513 }
  514 
  515 /// Name of the crates that are visible to consumers of the standard library.
  516 /// Documentation for internal crates is handled by the rustc step, so internal crates will show
  517 /// up there.
  518 ///
  519 /// Order here is important!
  520 /// Crates need to be processed starting from the leaves, otherwise rustdoc will not
  521 /// create correct links between crates because rustdoc depends on the
  522 /// existence of the output directories to know if it should be a local
  523 /// or remote link.
  524 const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
  525 
  526 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
  527 pub enum DocumentationFormat {
  528     HTML,
  529     JSON,
  530 }
  531 
  532 impl DocumentationFormat {
  533     fn as_str(&self) -> &str {
  534         match self {
  535             DocumentationFormat::HTML => "HTML",
  536             DocumentationFormat::JSON => "JSON",
  537         }
  538     }
  539 }
  540 
  541 /// Build the documentation for public standard library crates.
  542 ///
  543 /// `requested_crates` can be used to build only a subset of the crates. If empty, all crates will
  544 /// be built.
  545 fn doc_std(
  546     builder: &Builder<'_>,
  547     format: DocumentationFormat,
  548     stage: u32,
  549     target: TargetSelection,
  550     out: &Path,
  551     extra_args: &[&str],
  552     requested_crates: &[String],
  553 ) {
  554     if builder.no_std(target) == Some(true) {
  555         panic!(
  556             "building std documentation for no_std target {target} is not supported\n\
  557              Set `docs = false` in the config to disable documentation, or pass `--exclude doc::library`."
  558         );
  559     }
  560 
  561     let compiler = builder.compiler(stage, builder.config.build);
  562 
  563     let description =
  564         format!("library{} in {} format", crate_description(&requested_crates), format.as_str());
  565     let _guard = builder.msg_doc(compiler, &description, target);
  566 
  567     let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" };
  568     let target_dir =
  569         builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name);
  570 
  571     // This is directory where the compiler will place the output of the command.
  572     // We will then copy the files from this directory into the final `out` directory, the specified
  573     // as a function parameter.
  574     let out_dir = target_dir.join(target.triple).join("doc");
  575 
  576     let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "doc");
  577     compile::std_cargo(builder, target, compiler.stage, &mut cargo);
  578     cargo
  579         .arg("--no-deps")
  580         .arg("--target-dir")
  581         .arg(&*target_dir.to_string_lossy())
  582         .arg("-Zskip-rustdoc-fingerprint")
  583         .rustdocflag("-Z")
  584         .rustdocflag("unstable-options")
  585         .rustdocflag("--resource-suffix")
  586         .rustdocflag(&builder.version);
  587     for arg in extra_args {
  588         cargo.rustdocflag(arg);
  589     }
  590 
  591     if builder.config.library_docs_private_items {
  592         cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
  593     }
  594 
  595     // HACK: because we use `--manifest-path library/sysroot/Cargo.toml`, cargo thinks we only want to document that specific crate, not its dependencies.
  596     // Override its default.
  597     let built_crates = if requested_crates.is_empty() {
  598         builder
  599             .in_tree_crates("sysroot", None)
  600             .into_iter()
  601             .map(|krate| krate.name.to_string())
  602             .collect()
  603     } else {
  604         requested_crates.to_vec()
  605     };
  606 
  607     for krate in built_crates {
  608         cargo.arg("-p").arg(krate);
  609     }
  610 
  611     builder.run(&mut cargo.into());
  612     builder.cp_r(&out_dir, &out);
  613 }
  614 
  615 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  616 pub struct Rustc {
  617     pub stage: u32,
  618     pub target: TargetSelection,
  619     crates: Interned<Vec<String>>,
  620 }
  621 
  622 impl Rustc {
  623     pub(crate) fn new(stage: u32, target: TargetSelection, builder: &Builder<'_>) -> Self {
  624         // Find dependencies for top level crates.
  625         let root_crates = vec![
  626             INTERNER.intern_str("rustc_driver"),
  627             INTERNER.intern_str("rustc_codegen_llvm"),
  628             INTERNER.intern_str("rustc_codegen_ssa"),
  629         ];
  630         let crates: Vec<_> = root_crates
  631             .iter()
  632             .flat_map(|krate| {
  633                 builder
  634                     .in_tree_crates(krate, Some(target))
  635                     .into_iter()
  636                     .map(|krate| krate.name.to_string())
  637             })
  638             .collect();
  639         Self { stage, target, crates: INTERNER.intern_list(crates) }
  640     }
  641 }
  642 
  643 impl Step for Rustc {
  644     type Output = ();
  645     const DEFAULT: bool = true;
  646     const ONLY_HOSTS: bool = true;
  647 
  648     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  649         let builder = run.builder;
  650         run.crate_or_deps("rustc-main")
  651             .path("compiler")
  652             .default_condition(builder.config.compiler_docs)
  653     }
  654 
  655     fn make_run(run: RunConfig<'_>) {
  656         run.builder.ensure(Rustc {
  657             stage: run.builder.top_stage,
  658             target: run.target,
  659             crates: make_run_crates(&run, "compiler"),
  660         });
  661     }
  662 
  663     /// Generates compiler documentation.
  664     ///
  665     /// This will generate all documentation for compiler and dependencies.
  666     /// Compiler documentation is distributed separately, so we make sure
  667     /// we do not merge it with the other documentation from std, test and
  668     /// proc_macros. This is largely just a wrapper around `cargo doc`.
  669     fn run(mut self, builder: &Builder<'_>) {
  670         let stage = self.stage;
  671         let target = self.target;
  672 
  673         // This is the intended out directory for compiler documentation.
  674         let out = builder.compiler_doc_out(target);
  675         t!(fs::create_dir_all(&out));
  676 
  677         // Build the standard library, so that proc-macros can use it.
  678         // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.)
  679         let compiler = builder.compiler(stage, builder.config.build);
  680         builder.ensure(compile::Std::new(compiler, builder.config.build));
  681 
  682         let _guard = builder.msg_sysroot_tool(
  683             Kind::Doc,
  684             stage,
  685             &format!("compiler{}", crate_description(&self.crates)),
  686             compiler.host,
  687             target,
  688         );
  689 
  690         // This uses a shared directory so that librustdoc documentation gets
  691         // correctly built and merged with the rustc documentation. This is
  692         // needed because rustdoc is built in a different directory from
  693         // rustc. rustdoc needs to be able to see everything, for example when
  694         // merging the search index, or generating local (relative) links.
  695         let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
  696         t!(fs::create_dir_all(out_dir.parent().unwrap()));
  697         symlink_dir_force(&builder.config, &out, &out_dir);
  698         // Cargo puts proc macros in `target/doc` even if you pass `--target`
  699         // explicitly (https://github.com/rust-lang/cargo/issues/7677).
  700         let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
  701         symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
  702 
  703         // Build cargo command.
  704         let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
  705         cargo.rustdocflag("--document-private-items");
  706         // Since we always pass --document-private-items, there's no need to warn about linking to private items.
  707         cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
  708         cargo.rustdocflag("--enable-index-page");
  709         cargo.rustdocflag("-Zunstable-options");
  710         cargo.rustdocflag("-Znormalize-docs");
  711         cargo.rustdocflag("--show-type-layout");
  712         cargo.rustdocflag("--generate-link-to-definition");
  713         compile::rustc_cargo(builder, &mut cargo, target, compiler.stage);
  714         cargo.arg("-Zunstable-options");
  715         cargo.arg("-Zskip-rustdoc-fingerprint");
  716 
  717         // Only include compiler crates, no dependencies of those, such as `libc`.
  718         // Do link to dependencies on `docs.rs` however using `rustdoc-map`.
  719         cargo.arg("--no-deps");
  720         cargo.arg("-Zrustdoc-map");
  721 
  722         // FIXME: `-Zrustdoc-map` does not yet correctly work for transitive dependencies,
  723         // once this is no longer an issue the special case for `ena` can be removed.
  724         cargo.rustdocflag("--extern-html-root-url");
  725         cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
  726 
  727         let mut to_open = None;
  728 
  729         if self.crates.is_empty() {
  730             self.crates = INTERNER.intern_list(vec!["rustc_driver".to_owned()]);
  731         };
  732 
  733         for krate in &*self.crates {
  734             // Create all crate output directories first to make sure rustdoc uses
  735             // relative links.
  736             // FIXME: Cargo should probably do this itself.
  737             t!(fs::create_dir_all(out_dir.join(krate)));
  738             cargo.arg("-p").arg(krate);
  739             if to_open.is_none() {
  740                 to_open = Some(krate);
  741             }
  742         }
  743 
  744         builder.run(&mut cargo.into());
  745         // Let's open the first crate documentation page:
  746         if let Some(krate) = to_open {
  747             let index = out.join(krate).join("index.html");
  748             builder.open_in_browser(index);
  749         }
  750     }
  751 }
  752 
  753 macro_rules! tool_doc {
  754     (
  755         $tool: ident,
  756         $should_run: literal,
  757         $path: literal,
  758         $(rustc_tool = $rustc_tool:literal, )?
  759         $(in_tree = $in_tree:literal, )?
  760         [$($extra_arg: literal),+ $(,)?]
  761         $(,)?
  762     ) => {
  763         #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  764         pub struct $tool {
  765             target: TargetSelection,
  766         }
  767 
  768         impl Step for $tool {
  769             type Output = ();
  770             const DEFAULT: bool = true;
  771             const ONLY_HOSTS: bool = true;
  772 
  773             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  774                 let builder = run.builder;
  775                 run.crate_or_deps($should_run).default_condition(builder.config.compiler_docs)
  776             }
  777 
  778             fn make_run(run: RunConfig<'_>) {
  779                 run.builder.ensure($tool { target: run.target });
  780             }
  781 
  782             /// Generates compiler documentation.
  783             ///
  784             /// This will generate all documentation for compiler and dependencies.
  785             /// Compiler documentation is distributed separately, so we make sure
  786             /// we do not merge it with the other documentation from std, test and
  787             /// proc_macros. This is largely just a wrapper around `cargo doc`.
  788             fn run(self, builder: &Builder<'_>) {
  789                 let stage = builder.top_stage;
  790                 let target = self.target;
  791 
  792                 // This is the intended out directory for compiler documentation.
  793                 let out = builder.compiler_doc_out(target);
  794                 t!(fs::create_dir_all(&out));
  795 
  796                 let compiler = builder.compiler(stage, builder.config.build);
  797                 builder.ensure(compile::Std::new(compiler, target));
  798 
  799                 if true $(&& $rustc_tool)? {
  800                     // Build rustc docs so that we generate relative links.
  801                     builder.ensure(Rustc::new(stage, target, builder));
  802 
  803                     // Rustdoc needs the rustc sysroot available to build.
  804                     // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed
  805                     // with strange errors, but only on a full bors test ...
  806                     builder.ensure(compile::Rustc::new(compiler, target));
  807                 }
  808 
  809                 let source_type = if true $(&& $in_tree)? {
  810                     SourceType::InTree
  811                 } else {
  812                     SourceType::Submodule
  813                 };
  814 
  815                 builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target);
  816 
  817                 // Symlink compiler docs to the output directory of rustdoc documentation.
  818                 let out_dirs = [
  819                     builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc"),
  820                     // Cargo uses a different directory for proc macros.
  821                     builder.stage_out(compiler, Mode::ToolRustc).join("doc"),
  822                 ];
  823                 for out_dir in out_dirs {
  824                     t!(fs::create_dir_all(&out_dir));
  825                     symlink_dir_force(&builder.config, &out, &out_dir);
  826                 }
  827 
  828                 // Build cargo command.
  829                 let mut cargo = prepare_tool_cargo(
  830                     builder,
  831                     compiler,
  832                     Mode::ToolRustc,
  833                     target,
  834                     "doc",
  835                     $path,
  836                     source_type,
  837                     &[],
  838                 );
  839 
  840                 cargo.arg("-Zskip-rustdoc-fingerprint");
  841                 // Only include compiler crates, no dependencies of those, such as `libc`.
  842                 cargo.arg("--no-deps");
  843 
  844                 $(
  845                     cargo.arg($extra_arg);
  846                 )+
  847 
  848                 cargo.rustdocflag("--document-private-items");
  849                 // Since we always pass --document-private-items, there's no need to warn about linking to private items.
  850                 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
  851                 cargo.rustdocflag("--enable-index-page");
  852                 cargo.rustdocflag("--show-type-layout");
  853                 cargo.rustdocflag("--generate-link-to-definition");
  854                 cargo.rustdocflag("-Zunstable-options");
  855                 builder.run(&mut cargo.into());
  856             }
  857         }
  858     }
  859 }
  860 
  861 tool_doc!(
  862     Rustdoc,
  863     "rustdoc-tool",
  864     "src/tools/rustdoc",
  865     ["-p", "rustdoc", "-p", "rustdoc-json-types"]
  866 );
  867 tool_doc!(
  868     Rustfmt,
  869     "rustfmt-nightly",
  870     "src/tools/rustfmt",
  871     ["-p", "rustfmt-nightly", "-p", "rustfmt-config_proc_macro"],
  872 );
  873 tool_doc!(Clippy, "clippy", "src/tools/clippy", ["-p", "clippy_utils"]);
  874 tool_doc!(Miri, "miri", "src/tools/miri", ["-p", "miri"]);
  875 tool_doc!(
  876     Cargo,
  877     "cargo",
  878     "src/tools/cargo",
  879     rustc_tool = false,
  880     in_tree = false,
  881     [
  882         "-p",
  883         "cargo",
  884         "-p",
  885         "cargo-platform",
  886         "-p",
  887         "cargo-util",
  888         "-p",
  889         "crates-io",
  890         "-p",
  891         "cargo-test-macro",
  892         "-p",
  893         "cargo-test-support",
  894         "-p",
  895         "cargo-credential",
  896         "-p",
  897         "cargo-credential-1password",
  898         "-p",
  899         "mdman",
  900         // FIXME: this trips a license check in tidy.
  901         // "-p",
  902         // "resolver-tests",
  903         // FIXME: we should probably document these, but they're different per-platform so we can't use `tool_doc`.
  904         // "-p",
  905         // "cargo-credential-gnome-secret",
  906         // "-p",
  907         // "cargo-credential-macos-keychain",
  908         // "-p",
  909         // "cargo-credential-wincred",
  910     ]
  911 );
  912 tool_doc!(Tidy, "tidy", "src/tools/tidy", rustc_tool = false, ["-p", "tidy"]);
  913 tool_doc!(
  914     Bootstrap,
  915     "bootstrap",
  916     "src/bootstrap",
  917     rustc_tool = false,
  918     ["--lib", "-p", "bootstrap"]
  919 );
  920 
  921 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
  922 pub struct ErrorIndex {
  923     pub target: TargetSelection,
  924 }
  925 
  926 impl Step for ErrorIndex {
  927     type Output = ();
  928     const DEFAULT: bool = true;
  929     const ONLY_HOSTS: bool = true;
  930 
  931     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  932         let builder = run.builder;
  933         run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
  934     }
  935 
  936     fn make_run(run: RunConfig<'_>) {
  937         let target = run.target;
  938         run.builder.ensure(ErrorIndex { target });
  939     }
  940 
  941     /// Generates the HTML rendered error-index by running the
  942     /// `error_index_generator` tool.
  943     fn run(self, builder: &Builder<'_>) {
  944         builder.info(&format!("Documenting error index ({})", self.target));
  945         let out = builder.doc_out(self.target);
  946         t!(fs::create_dir_all(&out));
  947         let mut index = tool::ErrorIndex::command(builder);
  948         index.arg("html");
  949         index.arg(out);
  950         index.arg(&builder.version);
  951 
  952         builder.run(&mut index);
  953     }
  954 }
  955 
  956 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
  957 pub struct UnstableBookGen {
  958     target: TargetSelection,
  959 }
  960 
  961 impl Step for UnstableBookGen {
  962     type Output = ();
  963     const DEFAULT: bool = true;
  964     const ONLY_HOSTS: bool = true;
  965 
  966     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
  967         let builder = run.builder;
  968         run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
  969     }
  970 
  971     fn make_run(run: RunConfig<'_>) {
  972         run.builder.ensure(UnstableBookGen { target: run.target });
  973     }
  974 
  975     fn run(self, builder: &Builder<'_>) {
  976         let target = self.target;
  977 
  978         builder.info(&format!("Generating unstable book md files ({})", target));
  979         let out = builder.md_doc_out(target).join("unstable-book");
  980         builder.create_dir(&out);
  981         builder.remove_dir(&out);
  982         let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
  983         cmd.arg(builder.src.join("library"));
  984         cmd.arg(builder.src.join("compiler"));
  985         cmd.arg(builder.src.join("src"));
  986         cmd.arg(out);
  987 
  988         builder.run(&mut cmd);
  989     }
  990 }
  991 
  992 fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
  993     if config.dry_run() {
  994         return;
  995     }
  996     if let Ok(m) = fs::symlink_metadata(link) {
  997         if m.file_type().is_dir() {
  998             t!(fs::remove_dir_all(link));
  999         } else {
 1000             // handle directory junctions on windows by falling back to
 1001             // `remove_dir`.
 1002             t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
 1003         }
 1004     }
 1005 
 1006     t!(
 1007         symlink_dir(config, original, link),
 1008         format!("failed to create link from {} -> {}", link.display(), original.display())
 1009     );
 1010 }
 1011 
 1012 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
 1013 pub struct RustcBook {
 1014     pub compiler: Compiler,
 1015     pub target: TargetSelection,
 1016     pub validate: bool,
 1017 }
 1018 
 1019 impl Step for RustcBook {
 1020     type Output = ();
 1021     const DEFAULT: bool = true;
 1022     const ONLY_HOSTS: bool = true;
 1023 
 1024     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
 1025         let builder = run.builder;
 1026         run.path("src/doc/rustc").default_condition(builder.config.docs)
 1027     }
 1028 
 1029     fn make_run(run: RunConfig<'_>) {
 1030         run.builder.ensure(RustcBook {
 1031             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
 1032             target: run.target,
 1033             validate: false,
 1034         });
 1035     }
 1036 
 1037     /// Builds the rustc book.
 1038     ///
 1039     /// The lints are auto-generated by a tool, and then merged into the book
 1040     /// in the "md-doc" directory in the build output directory. Then
 1041     /// "rustbook" is used to convert it to HTML.
 1042     fn run(self, builder: &Builder<'_>) {
 1043         let out_base = builder.md_doc_out(self.target).join("rustc");
 1044         t!(fs::create_dir_all(&out_base));
 1045         let out_listing = out_base.join("src/lints");
 1046         builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base);
 1047         builder.info(&format!("Generating lint docs ({})", self.target));
 1048 
 1049         let rustc = builder.rustc(self.compiler);
 1050         // The tool runs `rustc` for extracting output examples, so it needs a
 1051         // functional sysroot.
 1052         builder.ensure(compile::Std::new(self.compiler, self.target));
 1053         let mut cmd = builder.tool_cmd(Tool::LintDocs);
 1054         cmd.arg("--src");
 1055         cmd.arg(builder.src.join("compiler"));
 1056         cmd.arg("--out");
 1057         cmd.arg(&out_listing);
 1058         cmd.arg("--rustc");
 1059         cmd.arg(&rustc);
 1060         cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
 1061         if builder.is_verbose() {
 1062             cmd.arg("--verbose");
 1063         }
 1064         if self.validate {
 1065             cmd.arg("--validate");
 1066         }
 1067         // We need to validate nightly features, even on the stable channel.
 1068         // Set this unconditionally as the stage0 compiler may be being used to
 1069         // document.
 1070         cmd.env("RUSTC_BOOTSTRAP", "1");
 1071 
 1072         // If the lib directories are in an unusual location (changed in
 1073         // config.toml), then this needs to explicitly update the dylib search
 1074         // path.
 1075         builder.add_rustc_lib_path(self.compiler, &mut cmd);
 1076         builder.run(&mut cmd);
 1077         // Run rustbook/mdbook to generate the HTML pages.
 1078         builder.ensure(RustbookSrc {
 1079             target: self.target,
 1080             name: INTERNER.intern_str("rustc"),
 1081             src: INTERNER.intern_path(out_base),
 1082             parent: Some(self),
 1083         });
 1084     }
 1085 }