1 //! Documentation generation for rustbuilder.
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.
7 //! Everything here is basically just a shim around calling either `rustbook` or
10 use std::collections::HashSet;
13 use std::path::{Path, PathBuf};
16 use build_helper::{t, up_to_date};
18 use crate::builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
19 use crate::cache::{Interned, INTERNER};
21 use crate::config::{Config, TargetSelection};
22 use crate::tool::{self, prepare_tool_cargo, SourceType, Tool};
23 use crate::util::symlink_dir;
25 macro_rules! submodule_helper {
26 ($path:expr, submodule) => {
29 ($path:expr, submodule = $submodule:literal) => {
35 ($($name:ident, $path:expr, $book_name:expr $(, submodule $(= $submodule:literal)? )? ;)+) => {
37 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
39 target: TargetSelection,
44 const DEFAULT: bool = true;
46 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
47 let builder = run.builder;
48 run.path($path).default_condition(builder.config.docs)
51 fn make_run(run: RunConfig<'_>) {
52 run.builder.ensure($name {
57 fn run(self, builder: &Builder<'_>) {
59 let path = Path::new(submodule_helper!( $path, submodule $( = $submodule )? ));
60 builder.update_submodule(&path);
62 builder.ensure(RustbookSrc {
64 name: INTERNER.intern_str($book_name),
65 src: INTERNER.intern_path(builder.src.join($path)),
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?).
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";
88 fn open(builder: &Builder<'_>, path: impl AsRef<Path>) {
89 if builder.config.dry_run || !builder.config.cmd.open() {
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));
100 // "library/std" -> ["library", "std"]
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
105 fn components_simplified(path: &PathBuf) -> Vec<&str> {
106 path.iter().map(|component| component.to_str().unwrap_or("???")).collect()
109 fn is_explicit_request(builder: &Builder<'_>, path: &str) -> bool {
113 .map(components_simplified)
114 .any(|requested| requested.iter().copied().eq(path.split('/')))
117 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
118 pub struct UnstableBook {
119 target: TargetSelection,
122 impl Step for UnstableBook {
124 const DEFAULT: bool = true;
126 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
127 let builder = run.builder;
128 run.path("src/doc/unstable-book").default_condition(builder.config.docs)
131 fn make_run(run: RunConfig<'_>) {
132 run.builder.ensure(UnstableBook { target: run.target });
135 fn run(self, builder: &Builder<'_>) {
136 builder.ensure(UnstableBookGen { target: self.target });
137 builder.ensure(RustbookSrc {
139 name: INTERNER.intern_str("unstable-book"),
140 src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
145 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
147 target: TargetSelection,
148 name: Interned<String>,
149 src: Interned<PathBuf>,
152 impl Step for RustbookSrc {
155 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
159 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
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;
167 let out = builder.doc_out(target);
168 t!(fs::create_dir_all(&out));
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) {
177 builder.info(&format!("Rustbook ({}) - {}", target, name));
178 let _ = fs::remove_dir_all(&out);
180 builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
184 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
187 target: TargetSelection,
190 impl Step for TheBook {
192 const DEFAULT: bool = true;
194 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
195 let builder = run.builder;
196 run.path("src/doc/book").default_condition(builder.config.docs)
199 fn make_run(run: RunConfig<'_>) {
200 run.builder.ensure(TheBook {
201 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
206 /// Builds the book and associated stuff.
208 /// We need to build:
211 /// * Older edition redirects
212 /// * Version info and CSS
215 fn run(self, builder: &Builder<'_>) {
216 let relative_path = Path::new("src").join("doc").join("book");
217 builder.update_submodule(&relative_path);
219 let compiler = self.compiler;
220 let target = self.target;
223 builder.ensure(RustbookSrc {
225 name: INTERNER.intern_str("book"),
226 src: INTERNER.intern_path(builder.src.join(&relative_path)),
229 // building older edition redirects
230 for edition in &["first-edition", "second-edition", "2018-edition"] {
231 builder.ensure(RustbookSrc {
233 name: INTERNER.intern_string(format!("book/{}", edition)),
234 src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)),
238 // build the version info page and CSS
239 builder.ensure(Standalone { compiler, target });
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"))) {
245 let path = file.path();
246 let path = path.to_str().unwrap();
248 invoke_rustdoc(builder, compiler, target, path);
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);
260 builder: &Builder<'_>,
262 target: TargetSelection,
265 let out = builder.doc_out(target);
267 let path = builder.src.join("src/doc").join(markdown);
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");
273 let mut cmd = builder.rustdoc_cmd(compiler);
275 let out = out.join("book");
277 cmd.arg("--html-after-content")
279 .arg("--html-before-content")
281 .arg("--html-in-header")
283 .arg("--markdown-no-toc")
284 .arg("--markdown-playground-url")
285 .arg("https://play.rust-lang.org/")
289 .arg("--markdown-css")
292 if !builder.config.docs_minification {
293 cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
296 builder.run(&mut cmd);
299 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
300 pub struct Standalone {
302 target: TargetSelection,
305 impl Step for Standalone {
307 const DEFAULT: bool = true;
309 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
310 let builder = run.builder;
311 run.path("src/doc").default_condition(builder.config.docs)
314 fn make_run(run: RunConfig<'_>) {
315 run.builder.ensure(Standalone {
316 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
321 /// Generates all standalone documentation as compiled by the rustdoc in `stage`
322 /// for the `target` into `out`.
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.
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));
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")));
341 let version_input = builder.src.join("src/doc/version_info.html.template");
342 let version_info = out.join("version_info.html");
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));
352 for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
354 let path = file.path();
355 let filename = path.file_name().unwrap().to_str().unwrap();
356 if !filename.ends_with(".md") || filename == "README.md" {
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))
372 let mut cmd = builder.rustdoc_cmd(compiler);
373 // Needed for --index-page flag
374 cmd.arg("-Z").arg("unstable-options");
376 cmd.arg("--html-after-content")
378 .arg("--html-before-content")
380 .arg("--html-in-header")
382 .arg("--markdown-no-toc")
384 .arg(&builder.src.join("src/doc/index.md"))
385 .arg("--markdown-playground-url")
386 .arg("https://play.rust-lang.org/")
391 if !builder.config.docs_minification {
392 cmd.arg("--disable-minification");
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");
401 cmd.arg("--markdown-css")
402 .arg(format!("rustdoc{}.css", &builder.version))
403 .arg("--markdown-css")
406 builder.run(&mut cmd);
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);
418 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
421 pub target: TargetSelection,
426 const DEFAULT: bool = true;
428 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
429 let builder = run.builder;
430 run.all_krates("test").default_condition(builder.config.docs)
433 fn make_run(run: RunConfig<'_>) {
434 run.builder.ensure(Std { stage: run.builder.top_stage, target: run.target });
437 /// Compile all standard library documentation.
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);
449 builder.ensure(compile::Std { compiler, target });
450 let out_dir = builder.stage_out(compiler, Mode::Std).join(target.triple).join("doc");
452 t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
454 let run_cargo_rustdoc_for = |package: &str| {
456 builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustdoc");
457 compile::std_cargo(builder, target, compiler.stage, &mut cargo);
462 .arg("-Zskip-rustdoc-fingerprint")
464 .arg("--markdown-css")
466 .arg("--markdown-no-toc")
468 .arg("unstable-options")
469 .arg("--resource-suffix")
470 .arg(&builder.version)
472 .arg(&builder.src.join("src/doc/index.md"));
474 if !builder.config.docs_minification {
475 cargo.arg("--disable-minification");
478 builder.run(&mut cargo.into());
484 .map(components_simplified)
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())
494 .collect::<Vec<_>>();
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.
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
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.
514 builder.cp_r(&out_dir, &out);
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);
527 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
530 pub target: TargetSelection,
533 impl Step for Rustc {
535 const DEFAULT: bool = true;
536 const ONLY_HOSTS: bool = true;
538 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
539 let builder = run.builder;
540 run.krate("rustc-main").default_condition(builder.config.docs)
543 fn make_run(run: RunConfig<'_>) {
544 run.builder.ensure(Rustc { stage: run.builder.top_stage, target: run.target });
547 /// Generates compiler documentation.
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 builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
558 if !builder.config.compiler_docs {
559 builder.info("\tskipping - compiler/librustdoc docs disabled");
563 // This is the intended out directory for compiler documentation.
564 let out = builder.compiler_doc_out(target);
565 t!(fs::create_dir_all(&out));
568 let compiler = builder.compiler(stage, builder.config.build);
569 builder.ensure(compile::Rustc { compiler, target });
571 // This uses a shared directory so that librustdoc documentation gets
572 // correctly built and merged with the rustc documentation. This is
573 // needed because rustdoc is built in a different directory from
574 // rustc. rustdoc needs to be able to see everything, for example when
575 // merging the search index, or generating local (relative) links.
576 let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
577 t!(symlink_dir_force(&builder.config, &out, &out_dir));
578 // Cargo puts proc macros in `target/doc` even if you pass `--target`
579 // explicitly (https://github.com/rust-lang/cargo/issues/7677).
580 let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
581 t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir));
583 // Build cargo command.
584 let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
585 cargo.rustdocflag("--document-private-items");
586 // Since we always pass --document-private-items, there's no need to warn about linking to private items.
587 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
588 cargo.rustdocflag("--enable-index-page");
589 cargo.rustdocflag("-Zunstable-options");
590 cargo.rustdocflag("-Znormalize-docs");
591 cargo.rustdocflag("--show-type-layout");
592 compile::rustc_cargo(builder, &mut cargo, target);
593 cargo.arg("-Zskip-rustdoc-fingerprint");
595 // Only include compiler crates, no dependencies of those, such as `libc`.
596 cargo.arg("--no-deps");
598 // Find dependencies for top level crates.
599 let mut compiler_crates = HashSet::new();
600 for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] {
601 compiler_crates.extend(
603 .in_tree_crates(root_crate, Some(target))
605 .map(|krate| krate.name),
609 for krate in &compiler_crates {
610 // Create all crate output directories first to make sure rustdoc uses
612 // FIXME: Cargo should probably do this itself.
613 t!(fs::create_dir_all(out_dir.join(krate)));
614 cargo.arg("-p").arg(krate);
617 builder.run(&mut cargo.into());
621 macro_rules! tool_doc {
622 ($tool: ident, $should_run: literal, $path: literal, [$($krate: literal),+ $(,)?], binary=$bin:expr) => {
623 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
626 target: TargetSelection,
629 impl Step for $tool {
631 const DEFAULT: bool = true;
632 const ONLY_HOSTS: bool = true;
634 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
635 run.krate($should_run)
638 fn make_run(run: RunConfig<'_>) {
639 run.builder.ensure($tool { stage: run.builder.top_stage, target: run.target });
642 /// Generates compiler documentation.
644 /// This will generate all documentation for compiler and dependencies.
645 /// Compiler documentation is distributed separately, so we make sure
646 /// we do not merge it with the other documentation from std, test and
647 /// proc_macros. This is largely just a wrapper around `cargo doc`.
648 fn run(self, builder: &Builder<'_>) {
649 let stage = self.stage;
650 let target = self.target;
651 builder.info(&format!("Documenting stage{} {} ({})", stage, stringify!($tool).to_lowercase(), target));
653 // This is the intended out directory for compiler documentation.
654 let out = builder.compiler_doc_out(target);
655 t!(fs::create_dir_all(&out));
657 let compiler = builder.compiler(stage, builder.config.build);
659 if !builder.config.compiler_docs {
660 builder.info("\tskipping - compiler/tool docs disabled");
664 // Build rustc docs so that we generate relative links.
665 builder.ensure(Rustc { stage, target });
667 // Symlink compiler docs to the output directory of rustdoc documentation.
668 let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
669 t!(fs::create_dir_all(&out_dir));
670 t!(symlink_dir_force(&builder.config, &out, &out_dir));
672 // Build cargo command.
673 let mut cargo = prepare_tool_cargo(
684 cargo.arg("-Zskip-rustdoc-fingerprint");
685 // Only include compiler crates, no dependencies of those, such as `libc`.
686 cargo.arg("--no-deps");
688 cargo.arg("-p").arg($krate);
692 cargo.rustdocflag("--document-private-items");
694 cargo.rustdocflag("--enable-index-page");
695 cargo.rustdocflag("--show-type-layout");
696 cargo.rustdocflag("-Zunstable-options");
697 builder.run(&mut cargo.into());
707 ["rustdoc", "rustdoc-json-types"],
714 ["rustfmt-nightly", "rustfmt-config_proc_macro"],
718 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
719 pub struct ErrorIndex {
720 pub target: TargetSelection,
723 impl Step for ErrorIndex {
725 const DEFAULT: bool = true;
726 const ONLY_HOSTS: bool = true;
728 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
729 let builder = run.builder;
730 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
733 fn make_run(run: RunConfig<'_>) {
734 let target = run.target;
735 run.builder.ensure(ErrorIndex { target });
738 /// Generates the HTML rendered error-index by running the
739 /// `error_index_generator` tool.
740 fn run(self, builder: &Builder<'_>) {
741 builder.info(&format!("Documenting error index ({})", self.target));
742 let out = builder.doc_out(self.target);
743 t!(fs::create_dir_all(&out));
744 let mut index = tool::ErrorIndex::command(builder);
746 index.arg(out.join("error-index.html"));
747 index.arg(&builder.version);
749 builder.run(&mut index);
753 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
754 pub struct UnstableBookGen {
755 target: TargetSelection,
758 impl Step for UnstableBookGen {
760 const DEFAULT: bool = true;
761 const ONLY_HOSTS: bool = true;
763 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
764 let builder = run.builder;
765 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
768 fn make_run(run: RunConfig<'_>) {
769 run.builder.ensure(UnstableBookGen { target: run.target });
772 fn run(self, builder: &Builder<'_>) {
773 let target = self.target;
775 builder.info(&format!("Generating unstable book md files ({})", target));
776 let out = builder.md_doc_out(target).join("unstable-book");
777 builder.create_dir(&out);
778 builder.remove_dir(&out);
779 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
780 cmd.arg(builder.src.join("library"));
781 cmd.arg(builder.src.join("compiler"));
782 cmd.arg(builder.src.join("src"));
785 builder.run(&mut cmd);
789 fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
793 if let Ok(m) = fs::symlink_metadata(dst) {
794 if m.file_type().is_dir() {
795 fs::remove_dir_all(dst)?;
797 // handle directory junctions on windows by falling back to
799 fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?;
803 symlink_dir(config, src, dst)
806 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
807 pub struct RustcBook {
808 pub compiler: Compiler,
809 pub target: TargetSelection,
813 impl Step for RustcBook {
815 const DEFAULT: bool = true;
816 const ONLY_HOSTS: bool = true;
818 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
819 let builder = run.builder;
820 run.path("src/doc/rustc").default_condition(builder.config.docs)
823 fn make_run(run: RunConfig<'_>) {
824 run.builder.ensure(RustcBook {
825 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
831 /// Builds the rustc book.
833 /// The lints are auto-generated by a tool, and then merged into the book
834 /// in the "md-doc" directory in the build output directory. Then
835 /// "rustbook" is used to convert it to HTML.
836 fn run(self, builder: &Builder<'_>) {
837 let out_base = builder.md_doc_out(self.target).join("rustc");
838 t!(fs::create_dir_all(&out_base));
839 let out_listing = out_base.join("src/lints");
840 builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base);
841 builder.info(&format!("Generating lint docs ({})", self.target));
843 let rustc = builder.rustc(self.compiler);
844 // The tool runs `rustc` for extracting output examples, so it needs a
845 // functional sysroot.
846 builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
847 let mut cmd = builder.tool_cmd(Tool::LintDocs);
849 cmd.arg(builder.src.join("compiler"));
851 cmd.arg(&out_listing);
854 cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
855 if builder.config.verbose() {
856 cmd.arg("--verbose");
859 cmd.arg("--validate");
861 // If the lib directories are in an unusual location (changed in
862 // config.toml), then this needs to explicitly update the dylib search
864 builder.add_rustc_lib_path(self.compiler, &mut cmd);
865 builder.run(&mut cmd);
866 // Run rustbook/mdbook to generate the HTML pages.
867 builder.ensure(RustbookSrc {
869 name: INTERNER.intern_str("rustc"),
870 src: INTERNER.intern_path(out_base),
872 if is_explicit_request(builder, "src/doc/rustc") {
873 let out = builder.doc_out(self.target);
874 let index = out.join("rustc").join("index.html");
875 open(builder, &index);