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