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").path("compiler").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 let mut is_explicit_request = false;
557 builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
562 .map(components_simplified)
564 if path.get(0) == Some(&"compiler") {
565 is_explicit_request = true;
566 path.get(1).map(|p| p.to_owned())
571 .collect::<Vec<_>>();
573 if !builder.config.compiler_docs && !is_explicit_request {
574 builder.info("\tskipping - compiler/librustdoc docs disabled");
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));
583 let compiler = builder.compiler(stage, builder.config.build);
584 builder.ensure(compile::Rustc { compiler, target });
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));
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");
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");
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/");
622 let mut compiler_crates = HashSet::new();
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(
629 .in_tree_crates(root_crate, Some(target))
631 .map(|krate| krate.name),
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)",
642 compiler_crates.extend(
644 .in_tree_crates(root_crate, Some(target))
646 .map(|krate| krate.name),
652 let mut to_open = None;
653 for krate in &compiler_crates {
654 // Create all crate output directories first to make sure rustdoc uses
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);
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);
673 macro_rules! tool_doc {
674 ($tool: ident, $should_run: literal, $path: literal, [$($krate: literal),+ $(,)?] $(,)?) => {
675 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
678 target: TargetSelection,
681 impl Step for $tool {
683 const DEFAULT: bool = true;
684 const ONLY_HOSTS: bool = true;
686 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
687 run.krate($should_run)
690 fn make_run(run: RunConfig<'_>) {
691 run.builder.ensure($tool { stage: run.builder.top_stage, target: run.target });
694 /// Generates compiler documentation.
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));
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));
709 let compiler = builder.compiler(stage, builder.config.build);
711 if !builder.config.compiler_docs {
712 builder.info("\tskipping - compiler/tool docs disabled");
716 // Build rustc docs so that we generate relative links.
717 builder.ensure(Rustc { stage, target });
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));
724 // Build cargo command.
725 let mut cargo = prepare_tool_cargo(
736 cargo.arg("-Zskip-rustdoc-fingerprint");
737 // Only include compiler crates, no dependencies of those, such as `libc`.
738 cargo.arg("--no-deps");
740 cargo.arg("-p").arg($krate);
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());
754 tool_doc!(Rustdoc, "rustdoc-tool", "src/tools/rustdoc", ["rustdoc", "rustdoc-json-types"]);
759 ["rustfmt-nightly", "rustfmt-config_proc_macro"],
762 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
763 pub struct ErrorIndex {
764 pub target: TargetSelection,
767 impl Step for ErrorIndex {
769 const DEFAULT: bool = true;
770 const ONLY_HOSTS: bool = true;
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)
777 fn make_run(run: RunConfig<'_>) {
778 let target = run.target;
779 run.builder.ensure(ErrorIndex { target });
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);
790 index.arg(out.join("error-index.html"));
791 index.arg(&builder.version);
793 builder.run(&mut index);
797 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
798 pub struct UnstableBookGen {
799 target: TargetSelection,
802 impl Step for UnstableBookGen {
804 const DEFAULT: bool = true;
805 const ONLY_HOSTS: bool = true;
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)
812 fn make_run(run: RunConfig<'_>) {
813 run.builder.ensure(UnstableBookGen { target: run.target });
816 fn run(self, builder: &Builder<'_>) {
817 let target = self.target;
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"));
829 builder.run(&mut cmd);
833 fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
837 if let Ok(m) = fs::symlink_metadata(dst) {
838 if m.file_type().is_dir() {
839 fs::remove_dir_all(dst)?;
841 // handle directory junctions on windows by falling back to
843 fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?;
847 symlink_dir(config, src, dst)
850 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
851 pub struct RustcBook {
852 pub compiler: Compiler,
853 pub target: TargetSelection,
857 impl Step for RustcBook {
859 const DEFAULT: bool = true;
860 const ONLY_HOSTS: bool = true;
862 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
863 let builder = run.builder;
864 run.path("src/doc/rustc").default_condition(builder.config.docs)
867 fn make_run(run: RunConfig<'_>) {
868 run.builder.ensure(RustcBook {
869 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
875 /// Builds the rustc book.
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));
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);
893 cmd.arg(builder.src.join("compiler"));
895 cmd.arg(&out_listing);
898 cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
899 if builder.config.verbose() {
900 cmd.arg("--verbose");
903 cmd.arg("--validate");
905 // If the lib directories are in an unusual location (changed in
906 // config.toml), then this needs to explicitly update the dylib search
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 {
913 name: INTERNER.intern_str("rustc"),
914 src: INTERNER.intern_path(out_base),
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);