]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/doc.rs
Rollup merge of #61389 - Zoxc:arena-cleanup, r=eddyb
[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::{PathBuf, Path};
14
15 use crate::Mode;
16 use build_helper::{t, up_to_date};
17
18 use crate::util::symlink_dir;
19 use crate::builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
20 use crate::tool::{self, prepare_tool_cargo, Tool, SourceType};
21 use crate::compile;
22 use crate::cache::{INTERNER, Interned};
23 use crate::config::Config;
24
25 macro_rules! book {
26     ($($name:ident, $path:expr, $book_name:expr, $book_ver:expr;)+) => {
27         $(
28             #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
29         pub struct $name {
30             target: Interned<String>,
31         }
32
33         impl Step for $name {
34             type Output = ();
35             const DEFAULT: bool = true;
36
37             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
38                 let builder = run.builder;
39                 run.path($path).default_condition(builder.config.docs)
40             }
41
42             fn make_run(run: RunConfig<'_>) {
43                 run.builder.ensure($name {
44                     target: run.target,
45                 });
46             }
47
48             fn run(self, builder: &Builder<'_>) {
49                 builder.ensure(Rustbook {
50                     target: self.target,
51                     name: INTERNER.intern_str($book_name),
52                     version: $book_ver,
53                 })
54             }
55         }
56         )+
57     }
58 }
59
60 // NOTE: When adding a book here, make sure to ALSO build the book by
61 // adding a build step in `src/bootstrap/builder.rs`!
62 book!(
63     EditionGuide, "src/doc/edition-guide", "edition-guide", RustbookVersion::MdBook2;
64     EmbeddedBook, "src/doc/embedded-book", "embedded-book", RustbookVersion::MdBook2;
65     Nomicon, "src/doc/nomicon", "nomicon", RustbookVersion::MdBook2;
66     Reference, "src/doc/reference", "reference", RustbookVersion::MdBook1;
67     RustByExample, "src/doc/rust-by-example", "rust-by-example", RustbookVersion::MdBook1;
68     RustcBook, "src/doc/rustc", "rustc", RustbookVersion::MdBook1;
69     RustdocBook, "src/doc/rustdoc", "rustdoc", RustbookVersion::MdBook2;
70 );
71
72 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
73 enum RustbookVersion {
74     MdBook1,
75     MdBook2,
76 }
77
78 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
79 struct Rustbook {
80     target: Interned<String>,
81     name: Interned<String>,
82     version: RustbookVersion,
83 }
84
85 impl Step for Rustbook {
86     type Output = ();
87
88     // rustbook is never directly called, and only serves as a shim for the nomicon and the
89     // reference.
90     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
91         run.never()
92     }
93
94     /// Invoke `rustbook` for `target` for the doc book `name`.
95     ///
96     /// This will not actually generate any documentation if the documentation has
97     /// already been generated.
98     fn run(self, builder: &Builder<'_>) {
99         let src = builder.src.join("src/doc");
100         builder.ensure(RustbookSrc {
101             target: self.target,
102             name: self.name,
103             src: INTERNER.intern_path(src),
104             version: self.version,
105         });
106     }
107 }
108
109 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
110 pub struct UnstableBook {
111     target: Interned<String>,
112 }
113
114 impl Step for UnstableBook {
115     type Output = ();
116     const DEFAULT: bool = true;
117
118     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
119         let builder = run.builder;
120         run.path("src/doc/unstable-book").default_condition(builder.config.docs)
121     }
122
123     fn make_run(run: RunConfig<'_>) {
124         run.builder.ensure(UnstableBook {
125             target: run.target,
126         });
127     }
128
129     fn run(self, builder: &Builder<'_>) {
130         builder.ensure(UnstableBookGen {
131             target: self.target,
132         });
133         builder.ensure(RustbookSrc {
134             target: self.target,
135             name: INTERNER.intern_str("unstable-book"),
136             src: builder.md_doc_out(self.target),
137             version: RustbookVersion::MdBook2,
138         })
139     }
140 }
141
142 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
143 pub struct CargoBook {
144     target: Interned<String>,
145     name: Interned<String>,
146 }
147
148 impl Step for CargoBook {
149     type Output = ();
150     const DEFAULT: bool = true;
151
152     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
153         let builder = run.builder;
154         run.path("src/tools/cargo/src/doc/book").default_condition(builder.config.docs)
155     }
156
157     fn make_run(run: RunConfig<'_>) {
158         run.builder.ensure(CargoBook {
159             target: run.target,
160             name: INTERNER.intern_str("cargo"),
161         });
162     }
163
164     fn run(self, builder: &Builder<'_>) {
165         let target = self.target;
166         let name = self.name;
167         let src = builder.src.join("src/tools/cargo/src/doc");
168
169         let out = builder.doc_out(target);
170         t!(fs::create_dir_all(&out));
171
172         let out = out.join(name);
173
174         builder.info(&format!("Cargo Book ({}) - {}", target, name));
175
176         let _ = fs::remove_dir_all(&out);
177
178         builder.run(builder.tool_cmd(Tool::Rustbook)
179                        .arg("build")
180                        .arg(&src)
181                        .arg("-d")
182                        .arg(out));
183     }
184 }
185
186 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
187 struct RustbookSrc {
188     target: Interned<String>,
189     name: Interned<String>,
190     src: Interned<PathBuf>,
191     version: RustbookVersion,
192 }
193
194 impl Step for RustbookSrc {
195     type Output = ();
196
197     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
198         run.never()
199     }
200
201     /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
202     ///
203     /// This will not actually generate any documentation if the documentation has
204     /// already been generated.
205     fn run(self, builder: &Builder<'_>) {
206         let target = self.target;
207         let name = self.name;
208         let src = self.src;
209         let out = builder.doc_out(target);
210         t!(fs::create_dir_all(&out));
211
212         let out = out.join(name);
213         let src = src.join(name);
214         let index = out.join("index.html");
215         let rustbook = builder.tool_exe(Tool::Rustbook);
216         let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
217         if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
218             return
219         }
220         builder.info(&format!("Rustbook ({}) - {}", target, name));
221         let _ = fs::remove_dir_all(&out);
222
223         let vers = match self.version {
224             RustbookVersion::MdBook1 => "1",
225             RustbookVersion::MdBook2 => "2",
226         };
227
228         builder.run(rustbook_cmd
229                        .arg("build")
230                        .arg(&src)
231                        .arg("-d")
232                        .arg(out)
233                        .arg("-m")
234                        .arg(vers));
235     }
236 }
237
238 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
239 pub struct TheBook {
240     compiler: Compiler,
241     target: Interned<String>,
242     name: &'static str,
243 }
244
245 impl Step for TheBook {
246     type Output = ();
247     const DEFAULT: bool = true;
248
249     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
250         let builder = run.builder;
251         run.path("src/doc/book").default_condition(builder.config.docs)
252     }
253
254     fn make_run(run: RunConfig<'_>) {
255         run.builder.ensure(TheBook {
256             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
257             target: run.target,
258             name: "book",
259         });
260     }
261
262     /// Builds the book and associated stuff.
263     ///
264     /// We need to build:
265     ///
266     /// * Book (first edition)
267     /// * Book (second edition)
268     /// * Version info and CSS
269     /// * Index page
270     /// * Redirect pages
271     fn run(self, builder: &Builder<'_>) {
272         let compiler = self.compiler;
273         let target = self.target;
274         let name = self.name;
275
276         // build book
277         builder.ensure(Rustbook {
278             target,
279             name: INTERNER.intern_string(name.to_string()),
280             version: RustbookVersion::MdBook2,
281         });
282
283         // building older edition redirects
284
285         let source_name = format!("{}/first-edition", name);
286         builder.ensure(Rustbook {
287             target,
288             name: INTERNER.intern_string(source_name),
289             version: RustbookVersion::MdBook2,
290         });
291
292         let source_name = format!("{}/second-edition", name);
293         builder.ensure(Rustbook {
294             target,
295             name: INTERNER.intern_string(source_name),
296             version: RustbookVersion::MdBook2,
297         });
298
299         let source_name = format!("{}/2018-edition", name);
300         builder.ensure(Rustbook {
301             target,
302             name: INTERNER.intern_string(source_name),
303             version: RustbookVersion::MdBook2,
304         });
305
306         // build the version info page and CSS
307         builder.ensure(Standalone {
308             compiler,
309             target,
310         });
311
312         // build the redirect pages
313         builder.info(&format!("Documenting book redirect pages ({})", target));
314         for file in t!(fs::read_dir(builder.src.join("src/doc/book/redirects"))) {
315             let file = t!(file);
316             let path = file.path();
317             let path = path.to_str().unwrap();
318
319             invoke_rustdoc(builder, compiler, target, path);
320         }
321     }
322 }
323
324 fn invoke_rustdoc(
325     builder: &Builder<'_>,
326     compiler: Compiler,
327     target: Interned<String>,
328     markdown: &str,
329 ) {
330     let out = builder.doc_out(target);
331
332     let path = builder.src.join("src/doc").join(markdown);
333
334     let header = builder.src.join("src/doc/redirect.inc");
335     let footer = builder.src.join("src/doc/footer.inc");
336     let version_info = out.join("version_info.html");
337
338     let mut cmd = builder.rustdoc_cmd(compiler);
339
340     let out = out.join("book");
341
342     cmd.arg("--html-after-content").arg(&footer)
343         .arg("--html-before-content").arg(&version_info)
344         .arg("--html-in-header").arg(&header)
345         .arg("--markdown-no-toc")
346         .arg("--markdown-playground-url").arg("https://play.rust-lang.org/")
347         .arg("-o").arg(&out).arg(&path)
348         .arg("--markdown-css").arg("../rust.css");
349
350     builder.run(&mut cmd);
351 }
352
353 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
354 pub struct Standalone {
355     compiler: Compiler,
356     target: Interned<String>,
357 }
358
359 impl Step for Standalone {
360     type Output = ();
361     const DEFAULT: bool = true;
362
363     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
364         let builder = run.builder;
365         run.path("src/doc").default_condition(builder.config.docs)
366     }
367
368     fn make_run(run: RunConfig<'_>) {
369         run.builder.ensure(Standalone {
370             compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
371             target: run.target,
372         });
373     }
374
375     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
376     /// for the `target` into `out`.
377     ///
378     /// This will list all of `src/doc` looking for markdown files and appropriately
379     /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
380     /// `STAMP` along with providing the various header/footer HTML we've customized.
381     ///
382     /// In the end, this is just a glorified wrapper around rustdoc!
383     fn run(self, builder: &Builder<'_>) {
384         let target = self.target;
385         let compiler = self.compiler;
386         builder.info(&format!("Documenting standalone ({})", target));
387         let out = builder.doc_out(target);
388         t!(fs::create_dir_all(&out));
389
390         let favicon = builder.src.join("src/doc/favicon.inc");
391         let footer = builder.src.join("src/doc/footer.inc");
392         let full_toc = builder.src.join("src/doc/full-toc.inc");
393         t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
394
395         let version_input = builder.src.join("src/doc/version_info.html.template");
396         let version_info = out.join("version_info.html");
397
398         if !builder.config.dry_run && !up_to_date(&version_input, &version_info) {
399             let info = t!(fs::read_to_string(&version_input))
400                 .replace("VERSION", &builder.rust_release())
401                 .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or(""))
402                 .replace("STAMP", builder.rust_info.sha().unwrap_or(""));
403             t!(fs::write(&version_info, &info));
404         }
405
406         for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
407             let file = t!(file);
408             let path = file.path();
409             let filename = path.file_name().unwrap().to_str().unwrap();
410             if !filename.ends_with(".md") || filename == "README.md" {
411                 continue
412             }
413
414             let html = out.join(filename).with_extension("html");
415             let rustdoc = builder.rustdoc(compiler);
416             if up_to_date(&path, &html) &&
417                up_to_date(&footer, &html) &&
418                up_to_date(&favicon, &html) &&
419                up_to_date(&full_toc, &html) &&
420                up_to_date(&version_info, &html) &&
421                (builder.config.dry_run || up_to_date(&rustdoc, &html)) {
422                 continue
423             }
424
425             let mut cmd = builder.rustdoc_cmd(compiler);
426             cmd.arg("--html-after-content").arg(&footer)
427                .arg("--html-before-content").arg(&version_info)
428                .arg("--html-in-header").arg(&favicon)
429                .arg("--markdown-no-toc")
430                .arg("--index-page").arg(&builder.src.join("src/doc/index.md"))
431                .arg("--markdown-playground-url").arg("https://play.rust-lang.org/")
432                .arg("-o").arg(&out)
433                .arg(&path);
434
435             if filename == "not_found.md" {
436                 cmd.arg("--markdown-css")
437                    .arg("https://doc.rust-lang.org/rust.css");
438             } else {
439                 cmd.arg("--markdown-css").arg("rust.css");
440             }
441             builder.run(&mut cmd);
442         }
443     }
444 }
445
446 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
447 pub struct Std {
448     pub stage: u32,
449     pub target: Interned<String>,
450 }
451
452 impl Step for Std {
453     type Output = ();
454     const DEFAULT: bool = true;
455
456     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
457         let builder = run.builder;
458         run.all_krates("std").default_condition(builder.config.docs)
459     }
460
461     fn make_run(run: RunConfig<'_>) {
462         run.builder.ensure(Std {
463             stage: run.builder.top_stage,
464             target: run.target
465         });
466     }
467
468     /// Compile all standard library documentation.
469     ///
470     /// This will generate all documentation for the standard library and its
471     /// dependencies. This is largely just a wrapper around `cargo doc`.
472     fn run(self, builder: &Builder<'_>) {
473         let stage = self.stage;
474         let target = self.target;
475         builder.info(&format!("Documenting stage{} std ({})", stage, target));
476         let out = builder.doc_out(target);
477         t!(fs::create_dir_all(&out));
478         let compiler = builder.compiler_for(stage, builder.config.build, target);
479
480         builder.ensure(compile::Std { compiler, target });
481         let out_dir = builder.stage_out(compiler, Mode::Std)
482                            .join(target).join("doc");
483
484         // Here what we're doing is creating a *symlink* (directory junction on
485         // Windows) to the final output location. This is not done as an
486         // optimization but rather for correctness. We've got three trees of
487         // documentation, one for std, one for test, and one for rustc. It's then
488         // our job to merge them all together.
489         //
490         // Unfortunately rustbuild doesn't know nearly as well how to merge doc
491         // trees as rustdoc does itself, so instead of actually having three
492         // separate trees we just have rustdoc output to the same location across
493         // all of them.
494         //
495         // This way rustdoc generates output directly into the output, and rustdoc
496         // will also directly handle merging.
497         let my_out = builder.crate_doc_out(target);
498         t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
499         t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
500
501         let run_cargo_rustdoc_for = |package: &str| {
502             let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc");
503             compile::std_cargo(builder, &compiler, target, &mut cargo);
504
505             // Keep a whitelist so we do not build internal stdlib crates, these will be
506             // build by the rustc step later if enabled.
507             cargo.arg("-Z").arg("unstable-options")
508                  .arg("-p").arg(package);
509             // Create all crate output directories first to make sure rustdoc uses
510             // relative links.
511             // FIXME: Cargo should probably do this itself.
512             t!(fs::create_dir_all(out_dir.join(package)));
513             cargo.arg("--")
514                  .arg("--markdown-css").arg("rust.css")
515                  .arg("--markdown-no-toc")
516                  .arg("--generate-redirect-pages")
517                  .arg("--resource-suffix").arg(crate::channel::CFG_RELEASE_NUM)
518                  .arg("--index-page").arg(&builder.src.join("src/doc/index.md"));
519
520             builder.run(&mut cargo);
521             builder.cp_r(&my_out, &out);
522         };
523         for krate in &["alloc", "core", "std"] {
524             run_cargo_rustdoc_for(krate);
525         }
526     }
527 }
528
529 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
530 pub struct Test {
531     stage: u32,
532     target: Interned<String>,
533 }
534
535 impl Step for Test {
536     type Output = ();
537     const DEFAULT: bool = true;
538
539     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
540         let builder = run.builder;
541         run.krate("test").default_condition(builder.config.docs)
542     }
543
544     fn make_run(run: RunConfig<'_>) {
545         run.builder.ensure(Test {
546             stage: run.builder.top_stage,
547             target: run.target,
548         });
549     }
550
551     /// Compile all libtest documentation.
552     ///
553     /// This will generate all documentation for libtest and its dependencies. This
554     /// is largely just a wrapper around `cargo doc`.
555     fn run(self, builder: &Builder<'_>) {
556         let stage = self.stage;
557         let target = self.target;
558         builder.info(&format!("Documenting stage{} test ({})", stage, target));
559         let out = builder.doc_out(target);
560         t!(fs::create_dir_all(&out));
561         let compiler = builder.compiler_for(stage, builder.config.build, target);
562
563         // Build libstd docs so that we generate relative links
564         builder.ensure(Std { stage, target });
565
566         builder.ensure(compile::Test { compiler, target });
567         let out_dir = builder.stage_out(compiler, Mode::Test)
568                            .join(target).join("doc");
569
570         // See docs in std above for why we symlink
571         let my_out = builder.crate_doc_out(target);
572         t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
573
574         let mut cargo = builder.cargo(compiler, Mode::Test, target, "doc");
575         compile::test_cargo(builder, &compiler, target, &mut cargo);
576
577         cargo.arg("--no-deps")
578              .arg("-p").arg("test")
579              .env("RUSTDOC_RESOURCE_SUFFIX", crate::channel::CFG_RELEASE_NUM)
580              .env("RUSTDOC_GENERATE_REDIRECT_PAGES", "1");
581
582         builder.run(&mut cargo);
583         builder.cp_r(&my_out, &out);
584     }
585 }
586
587 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
588 pub struct WhitelistedRustc {
589     stage: u32,
590     target: Interned<String>,
591 }
592
593 impl Step for WhitelistedRustc {
594     type Output = ();
595     const DEFAULT: bool = true;
596     const ONLY_HOSTS: bool = true;
597
598     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
599         let builder = run.builder;
600         run.krate("rustc-main").default_condition(builder.config.docs)
601     }
602
603     fn make_run(run: RunConfig<'_>) {
604         run.builder.ensure(WhitelistedRustc {
605             stage: run.builder.top_stage,
606             target: run.target,
607         });
608     }
609
610     /// Generates whitelisted compiler crate documentation.
611     ///
612     /// This will generate all documentation for crates that are whitelisted
613     /// to be included in the standard documentation. This documentation is
614     /// included in the standard Rust documentation, so we should always
615     /// document it and symlink to merge with the rest of the std and test
616     /// documentation. We don't build other compiler documentation
617     /// here as we want to be able to keep it separate from the standard
618     /// documentation. This is largely just a wrapper around `cargo doc`.
619     fn run(self, builder: &Builder<'_>) {
620         let stage = self.stage;
621         let target = self.target;
622         builder.info(&format!("Documenting stage{} whitelisted compiler ({})", stage, target));
623         let out = builder.doc_out(target);
624         t!(fs::create_dir_all(&out));
625         let compiler = builder.compiler_for(stage, builder.config.build, target);
626
627         // Build libstd docs so that we generate relative links
628         builder.ensure(Std { stage, target });
629
630         builder.ensure(compile::Rustc { compiler, target });
631         let out_dir = builder.stage_out(compiler, Mode::Rustc)
632                            .join(target).join("doc");
633
634         // See docs in std above for why we symlink
635         let my_out = builder.crate_doc_out(target);
636         t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
637
638         let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
639         compile::rustc_cargo(builder, &mut cargo);
640
641         // We don't want to build docs for internal compiler dependencies in this
642         // step (there is another step for that). Therefore, we whitelist the crates
643         // for which docs must be built.
644         for krate in &["proc_macro"] {
645             cargo.arg("-p").arg(krate)
646                  .env("RUSTDOC_RESOURCE_SUFFIX", crate::channel::CFG_RELEASE_NUM)
647                  .env("RUSTDOC_GENERATE_REDIRECT_PAGES", "1");
648         }
649
650         builder.run(&mut cargo);
651         builder.cp_r(&my_out, &out);
652     }
653 }
654
655 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
656 pub struct Rustc {
657     stage: u32,
658     target: Interned<String>,
659 }
660
661 impl Step for Rustc {
662     type Output = ();
663     const DEFAULT: bool = true;
664     const ONLY_HOSTS: bool = true;
665
666     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
667         let builder = run.builder;
668         run.krate("rustc-main").default_condition(builder.config.docs)
669     }
670
671     fn make_run(run: RunConfig<'_>) {
672         run.builder.ensure(Rustc {
673             stage: run.builder.top_stage,
674             target: run.target,
675         });
676     }
677
678     /// Generates compiler documentation.
679     ///
680     /// This will generate all documentation for compiler and dependencies.
681     /// Compiler documentation is distributed separately, so we make sure
682     /// we do not merge it with the other documentation from std, test and
683     /// proc_macros. This is largely just a wrapper around `cargo doc`.
684     fn run(self, builder: &Builder<'_>) {
685         let stage = self.stage;
686         let target = self.target;
687         builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
688
689         // This is the intended out directory for compiler documentation.
690         let out = builder.compiler_doc_out(target);
691         t!(fs::create_dir_all(&out));
692
693         // Get the correct compiler for this stage.
694         let compiler = builder.compiler_for(stage, builder.config.build, target);
695
696         if !builder.config.compiler_docs {
697             builder.info("\tskipping - compiler/librustdoc docs disabled");
698             return;
699         }
700
701         // Build rustc.
702         builder.ensure(compile::Rustc { compiler, target });
703
704         // We do not symlink to the same shared folder that already contains std library
705         // documentation from previous steps as we do not want to include that.
706         let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target).join("doc");
707         t!(symlink_dir_force(&builder.config, &out, &out_dir));
708
709         // Build cargo command.
710         let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
711         cargo.env("RUSTDOCFLAGS", "--document-private-items --passes strip-hidden");
712         compile::rustc_cargo(builder, &mut cargo);
713
714         // Only include compiler crates, no dependencies of those, such as `libc`.
715         cargo.arg("--no-deps");
716
717         // Find dependencies for top level crates.
718         let mut compiler_crates = HashSet::new();
719         for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] {
720             let interned_root_crate = INTERNER.intern_str(root_crate);
721             find_compiler_crates(builder, &interned_root_crate, &mut compiler_crates);
722         }
723
724         for krate in &compiler_crates {
725             // Create all crate output directories first to make sure rustdoc uses
726             // relative links.
727             // FIXME: Cargo should probably do this itself.
728             t!(fs::create_dir_all(out_dir.join(krate)));
729             cargo.arg("-p").arg(krate);
730         }
731
732         builder.run(&mut cargo);
733     }
734 }
735
736 fn find_compiler_crates(
737     builder: &Builder<'_>,
738     name: &Interned<String>,
739     crates: &mut HashSet<Interned<String>>
740 ) {
741     // Add current crate.
742     crates.insert(*name);
743
744     // Look for dependencies.
745     for dep in builder.crates.get(name).unwrap().deps.iter() {
746         if builder.crates.get(dep).unwrap().is_local(builder) {
747             find_compiler_crates(builder, dep, crates);
748         }
749     }
750 }
751
752 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
753 pub struct Rustdoc {
754     stage: u32,
755     target: Interned<String>,
756 }
757
758 impl Step for Rustdoc {
759     type Output = ();
760     const DEFAULT: bool = true;
761     const ONLY_HOSTS: bool = true;
762
763     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
764         run.krate("rustdoc-tool")
765     }
766
767     fn make_run(run: RunConfig<'_>) {
768         run.builder.ensure(Rustdoc {
769             stage: run.builder.top_stage,
770             target: run.target,
771         });
772     }
773
774     /// Generates compiler documentation.
775     ///
776     /// This will generate all documentation for compiler and dependencies.
777     /// Compiler documentation is distributed separately, so we make sure
778     /// we do not merge it with the other documentation from std, test and
779     /// proc_macros. This is largely just a wrapper around `cargo doc`.
780     fn run(self, builder: &Builder<'_>) {
781         let stage = self.stage;
782         let target = self.target;
783         builder.info(&format!("Documenting stage{} rustdoc ({})", stage, target));
784
785         // This is the intended out directory for compiler documentation.
786         let out = builder.compiler_doc_out(target);
787         t!(fs::create_dir_all(&out));
788
789         // Get the correct compiler for this stage.
790         let compiler = builder.compiler_for(stage, builder.config.build, target);
791
792         if !builder.config.compiler_docs {
793             builder.info("\tskipping - compiler/librustdoc docs disabled");
794             return;
795         }
796
797         // Build rustc docs so that we generate relative links.
798         builder.ensure(Rustc { stage, target });
799
800         // Build rustdoc.
801         builder.ensure(tool::Rustdoc { compiler: compiler });
802
803         // Symlink compiler docs to the output directory of rustdoc documentation.
804         let out_dir = builder.stage_out(compiler, Mode::ToolRustc)
805             .join(target)
806             .join("doc");
807         t!(fs::create_dir_all(&out_dir));
808         t!(symlink_dir_force(&builder.config, &out, &out_dir));
809
810         // Build cargo command.
811         let mut cargo = prepare_tool_cargo(
812             builder,
813             compiler,
814             Mode::ToolRustc,
815             target,
816             "doc",
817             "src/tools/rustdoc",
818             SourceType::InTree,
819             &[]
820         );
821
822         // Only include compiler crates, no dependencies of those, such as `libc`.
823         cargo.arg("--no-deps");
824         cargo.arg("-p").arg("rustdoc");
825
826         cargo.env("RUSTDOCFLAGS", "--document-private-items");
827         builder.run(&mut cargo);
828     }
829 }
830
831 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
832 pub struct ErrorIndex {
833     target: Interned<String>,
834 }
835
836 impl Step for ErrorIndex {
837     type Output = ();
838     const DEFAULT: bool = true;
839     const ONLY_HOSTS: bool = true;
840
841     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
842         let builder = run.builder;
843         run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
844     }
845
846     fn make_run(run: RunConfig<'_>) {
847         run.builder.ensure(ErrorIndex {
848             target: run.target,
849         });
850     }
851
852     /// Generates the HTML rendered error-index by running the
853     /// `error_index_generator` tool.
854     fn run(self, builder: &Builder<'_>) {
855         let target = self.target;
856
857         builder.info(&format!("Documenting error index ({})", target));
858         let out = builder.doc_out(target);
859         t!(fs::create_dir_all(&out));
860         let compiler = builder.compiler(2, builder.config.build);
861         let mut index = tool::ErrorIndex::command(
862             builder,
863             compiler,
864         );
865         index.arg("html");
866         index.arg(out.join("error-index.html"));
867         index.arg(crate::channel::CFG_RELEASE_NUM);
868
869         // FIXME: shouldn't have to pass this env var
870         index.env("CFG_BUILD", &builder.config.build)
871              .env("RUSTC_ERROR_METADATA_DST", builder.extended_error_dir());
872
873         builder.run(&mut index);
874     }
875 }
876
877 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
878 pub struct UnstableBookGen {
879     target: Interned<String>,
880 }
881
882 impl Step for UnstableBookGen {
883     type Output = ();
884     const DEFAULT: bool = true;
885     const ONLY_HOSTS: bool = true;
886
887     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
888         let builder = run.builder;
889         run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
890     }
891
892     fn make_run(run: RunConfig<'_>) {
893         run.builder.ensure(UnstableBookGen {
894             target: run.target,
895         });
896     }
897
898     fn run(self, builder: &Builder<'_>) {
899         let target = self.target;
900
901         builder.ensure(compile::Std {
902             compiler: builder.compiler(builder.top_stage, builder.config.build),
903             target,
904         });
905
906         builder.info(&format!("Generating unstable book md files ({})", target));
907         let out = builder.md_doc_out(target).join("unstable-book");
908         builder.create_dir(&out);
909         builder.remove_dir(&out);
910         let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
911         cmd.arg(builder.src.join("src"));
912         cmd.arg(out);
913
914         builder.run(&mut cmd);
915     }
916 }
917
918 fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
919     if config.dry_run {
920         return Ok(());
921     }
922     if let Ok(m) = fs::symlink_metadata(dst) {
923         if m.file_type().is_dir() {
924             fs::remove_dir_all(dst)?;
925         } else {
926             // handle directory junctions on windows by falling back to
927             // `remove_dir`.
928             fs::remove_file(dst).or_else(|_| {
929                 fs::remove_dir(dst)
930             })?;
931         }
932     }
933
934     symlink_dir(config, src, dst)
935 }