1use std::io::{self, Write};
11use std::path::{Path, PathBuf};
12use std::{env, fs, mem};
13
14use crate::core::build_steps::compile;
15use crate::core::build_steps::tool::{self, SourceType, Tool, prepare_tool_cargo};
16use crate::core::builder::{
17 self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata,
18 crate_description,
19};
20use crate::core::config::{Config, TargetSelection};
21use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date};
22use crate::{FileType, Mode};
23
24macro_rules! book {
25 ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => {
26 $(
27 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
28 pub struct $name {
29 target: TargetSelection,
30 }
31
32 impl Step for $name {
33 type Output = ();
34 const DEFAULT: bool = true;
35
36 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
37 let builder = run.builder;
38 run.path($path).default_condition(builder.config.docs)
39 }
40
41 fn make_run(run: RunConfig<'_>) {
42 run.builder.ensure($name {
43 target: run.target,
44 });
45 }
46
47 fn run(self, builder: &Builder<'_>) {
48 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
49 builder.require_submodule(&submodule_path, None)
50 }
51
52 builder.ensure(RustbookSrc {
53 target: self.target,
54 name: $book_name.to_owned(),
55 src: builder.src.join($path),
56 parent: Some(self),
57 languages: $lang.into(),
58 rustdoc_compiler: None,
59 })
60 }
61 }
62 )+
63 }
64}
65
66book!(
70 CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
71 ClippyBook, "src/tools/clippy/book", "clippy", &[];
72 EditionGuide, "src/doc/edition-guide", "edition-guide", &[];
73 EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[];
74 Nomicon, "src/doc/nomicon", "nomicon", &[];
75 RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja", "zh"];
76 RustdocBook, "src/doc/rustdoc", "rustdoc", &[];
77 StyleGuide, "src/doc/style-guide", "style-guide", &[];
78);
79
80#[derive(Debug, Clone, Hash, PartialEq, Eq)]
81pub struct UnstableBook {
82 target: TargetSelection,
83}
84
85impl Step for UnstableBook {
86 type Output = ();
87 const DEFAULT: bool = true;
88
89 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
90 let builder = run.builder;
91 run.path("src/doc/unstable-book").default_condition(builder.config.docs)
92 }
93
94 fn make_run(run: RunConfig<'_>) {
95 run.builder.ensure(UnstableBook { target: run.target });
96 }
97
98 fn run(self, builder: &Builder<'_>) {
99 builder.ensure(UnstableBookGen { target: self.target });
100 builder.ensure(RustbookSrc {
101 target: self.target,
102 name: "unstable-book".to_owned(),
103 src: builder.md_doc_out(self.target).join("unstable-book"),
104 parent: Some(self),
105 languages: vec![],
106 rustdoc_compiler: None,
107 })
108 }
109}
110
111#[derive(Debug, Clone, Hash, PartialEq, Eq)]
112struct RustbookSrc<P: Step> {
113 target: TargetSelection,
114 name: String,
115 src: PathBuf,
116 parent: Option<P>,
117 languages: Vec<&'static str>,
118 rustdoc_compiler: Option<Compiler>,
119}
120
121impl<P: Step> Step for RustbookSrc<P> {
122 type Output = ();
123
124 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
125 run.never()
126 }
127
128 fn run(self, builder: &Builder<'_>) {
133 let target = self.target;
134 let name = self.name;
135 let src = self.src;
136 let out = builder.doc_out(target);
137 t!(fs::create_dir_all(&out));
138
139 let out = out.join(&name);
140 let index = out.join("index.html");
141 let rustbook = builder.tool_exe(Tool::Rustbook);
142
143 if !builder.config.dry_run()
144 && (!up_to_date(&src, &index) || !up_to_date(&rustbook, &index))
145 {
146 builder.info(&format!("Rustbook ({target}) - {name}"));
147 let _ = fs::remove_dir_all(&out);
148
149 let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
150
151 if let Some(compiler) = self.rustdoc_compiler {
152 let mut rustdoc = builder.rustdoc(compiler);
153 rustdoc.pop();
154 let old_path = env::var_os("PATH").unwrap_or_default();
155 let new_path =
156 env::join_paths(std::iter::once(rustdoc).chain(env::split_paths(&old_path)))
157 .expect("could not add rustdoc to PATH");
158
159 rustbook_cmd.env("PATH", new_path);
160 builder.add_rustc_lib_path(compiler, &mut rustbook_cmd);
161 }
162
163 rustbook_cmd
164 .arg("build")
165 .arg(&src)
166 .arg("-d")
167 .arg(&out)
168 .arg("--rust-root")
169 .arg(&builder.src)
170 .run(builder);
171
172 for lang in &self.languages {
173 let out = out.join(lang);
174
175 builder.info(&format!("Rustbook ({target}) - {name} - {lang}"));
176 let _ = fs::remove_dir_all(&out);
177
178 builder
179 .tool_cmd(Tool::Rustbook)
180 .arg("build")
181 .arg(&src)
182 .arg("-d")
183 .arg(&out)
184 .arg("-l")
185 .arg(lang)
186 .run(builder);
187 }
188 }
189
190 if self.parent.is_some() {
191 builder.maybe_open_in_browser::<P>(index)
192 }
193 }
194}
195
196#[derive(Debug, Clone, Hash, PartialEq, Eq)]
197pub struct TheBook {
198 compiler: Compiler,
199 target: TargetSelection,
200}
201
202impl Step for TheBook {
203 type Output = ();
204 const DEFAULT: bool = true;
205
206 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
207 let builder = run.builder;
208 run.path("src/doc/book").default_condition(builder.config.docs)
209 }
210
211 fn make_run(run: RunConfig<'_>) {
212 run.builder.ensure(TheBook {
213 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
214 target: run.target,
215 });
216 }
217
218 fn run(self, builder: &Builder<'_>) {
228 builder.require_submodule("src/doc/book", None);
229
230 let compiler = self.compiler;
231 let target = self.target;
232
233 let absolute_path = builder.src.join("src/doc/book");
234 let redirect_path = absolute_path.join("redirects");
235
236 builder.ensure(RustbookSrc {
238 target,
239 name: "book".to_owned(),
240 src: absolute_path.clone(),
241 parent: Some(self),
242 languages: vec![],
243 rustdoc_compiler: None,
244 });
245
246 for edition in &["first-edition", "second-edition", "2018-edition"] {
248 builder.ensure(RustbookSrc {
249 target,
250 name: format!("book/{edition}"),
251 src: absolute_path.join(edition),
252 parent: Option::<Self>::None,
255 languages: vec![],
256 rustdoc_compiler: None,
257 });
258 }
259
260 let shared_assets = builder.ensure(SharedAssets { target });
262
263 let _guard = builder.msg_doc(compiler, "book redirect pages", target);
265 for file in t!(fs::read_dir(redirect_path)) {
266 let file = t!(file);
267 let path = file.path();
268 let path = path.to_str().unwrap();
269
270 invoke_rustdoc(builder, compiler, &shared_assets, target, path);
271 }
272 }
273}
274
275fn invoke_rustdoc(
276 builder: &Builder<'_>,
277 compiler: Compiler,
278 shared_assets: &SharedAssetsPaths,
279 target: TargetSelection,
280 markdown: &str,
281) {
282 let out = builder.doc_out(target);
283
284 let path = builder.src.join("src/doc").join(markdown);
285
286 let header = builder.src.join("src/doc/redirect.inc");
287 let footer = builder.src.join("src/doc/footer.inc");
288
289 let mut cmd = builder.rustdoc_cmd(compiler);
290
291 let out = out.join("book");
292
293 cmd.arg("--html-after-content")
294 .arg(&footer)
295 .arg("--html-before-content")
296 .arg(&shared_assets.version_info)
297 .arg("--html-in-header")
298 .arg(&header)
299 .arg("--markdown-no-toc")
300 .arg("--markdown-playground-url")
301 .arg("https://play.rust-lang.org/")
302 .arg("-o")
303 .arg(&out)
304 .arg(&path)
305 .arg("--markdown-css")
306 .arg("../rust.css")
307 .arg("-Zunstable-options");
308
309 if !builder.config.docs_minification {
310 cmd.arg("--disable-minification");
311 }
312
313 cmd.run(builder);
314}
315
316#[derive(Debug, Clone, Hash, PartialEq, Eq)]
317pub struct Standalone {
318 compiler: Compiler,
319 target: TargetSelection,
320}
321
322impl Step for Standalone {
323 type Output = ();
324 const DEFAULT: bool = true;
325
326 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
327 let builder = run.builder;
328 run.path("src/doc").alias("standalone").default_condition(builder.config.docs)
329 }
330
331 fn make_run(run: RunConfig<'_>) {
332 run.builder.ensure(Standalone {
333 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
334 target: run.target,
335 });
336 }
337
338 fn run(self, builder: &Builder<'_>) {
347 let target = self.target;
348 let compiler = self.compiler;
349 let _guard = builder.msg_doc(compiler, "standalone", target);
350 let out = builder.doc_out(target);
351 t!(fs::create_dir_all(&out));
352
353 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
354
355 let favicon = builder.src.join("src/doc/favicon.inc");
356 let footer = builder.src.join("src/doc/footer.inc");
357 let full_toc = builder.src.join("src/doc/full-toc.inc");
358
359 for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
360 let file = t!(file);
361 let path = file.path();
362 let filename = path.file_name().unwrap().to_str().unwrap();
363 if !filename.ends_with(".md") || filename == "README.md" {
364 continue;
365 }
366
367 let html = out.join(filename).with_extension("html");
368 let rustdoc = builder.rustdoc(compiler);
369 if up_to_date(&path, &html)
370 && up_to_date(&footer, &html)
371 && up_to_date(&favicon, &html)
372 && up_to_date(&full_toc, &html)
373 && (builder.config.dry_run() || up_to_date(&version_info, &html))
374 && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
375 {
376 continue;
377 }
378
379 let mut cmd = builder.rustdoc_cmd(compiler);
380
381 cmd.arg("--html-after-content")
382 .arg(&footer)
383 .arg("--html-before-content")
384 .arg(&version_info)
385 .arg("--html-in-header")
386 .arg(&favicon)
387 .arg("--markdown-no-toc")
388 .arg("-Zunstable-options")
389 .arg("--index-page")
390 .arg(builder.src.join("src/doc/index.md"))
391 .arg("--markdown-playground-url")
392 .arg("https://play.rust-lang.org/")
393 .arg("-o")
394 .arg(&out)
395 .arg(&path);
396
397 if !builder.config.docs_minification {
398 cmd.arg("--disable-minification");
399 }
400
401 if filename == "not_found.md" {
402 cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
403 } else {
404 cmd.arg("--markdown-css").arg("rust.css");
405 }
406 cmd.run(builder);
407 }
408
409 if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
412 let index = out.join("index.html");
413 builder.open_in_browser(index);
414 }
415 }
416}
417
418#[derive(Debug, Clone, Hash, PartialEq, Eq)]
419pub struct Releases {
420 compiler: Compiler,
421 target: TargetSelection,
422}
423
424impl Step for Releases {
425 type Output = ();
426 const DEFAULT: bool = true;
427
428 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
429 let builder = run.builder;
430 run.path("RELEASES.md").alias("releases").default_condition(builder.config.docs)
431 }
432
433 fn make_run(run: RunConfig<'_>) {
434 run.builder.ensure(Releases {
435 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
436 target: run.target,
437 });
438 }
439
440 fn run(self, builder: &Builder<'_>) {
446 let target = self.target;
447 let compiler = self.compiler;
448 let _guard = builder.msg_doc(compiler, "releases", target);
449 let out = builder.doc_out(target);
450 t!(fs::create_dir_all(&out));
451
452 builder.ensure(Standalone {
453 compiler: builder.compiler(builder.top_stage, builder.config.host_target),
454 target,
455 });
456
457 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
458
459 let favicon = builder.src.join("src/doc/favicon.inc");
460 let footer = builder.src.join("src/doc/footer.inc");
461 let full_toc = builder.src.join("src/doc/full-toc.inc");
462
463 let html = out.join("releases.html");
464 let tmppath = out.join("releases.md");
465 let inpath = builder.src.join("RELEASES.md");
466 let rustdoc = builder.rustdoc(compiler);
467 if !up_to_date(&inpath, &html)
468 || !up_to_date(&footer, &html)
469 || !up_to_date(&favicon, &html)
470 || !up_to_date(&full_toc, &html)
471 || !(builder.config.dry_run()
472 || up_to_date(&version_info, &html)
473 || up_to_date(&rustdoc, &html))
474 {
475 let mut tmpfile = t!(fs::File::create(&tmppath));
476 t!(tmpfile.write_all(b"% Rust Release Notes\n\n"));
477 t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile));
478 mem::drop(tmpfile);
479 let mut cmd = builder.rustdoc_cmd(compiler);
480
481 cmd.arg("--html-after-content")
482 .arg(&footer)
483 .arg("--html-before-content")
484 .arg(&version_info)
485 .arg("--html-in-header")
486 .arg(&favicon)
487 .arg("--markdown-no-toc")
488 .arg("--markdown-css")
489 .arg("rust.css")
490 .arg("-Zunstable-options")
491 .arg("--index-page")
492 .arg(builder.src.join("src/doc/index.md"))
493 .arg("--markdown-playground-url")
494 .arg("https://play.rust-lang.org/")
495 .arg("-o")
496 .arg(&out)
497 .arg(&tmppath);
498
499 if !builder.config.docs_minification {
500 cmd.arg("--disable-minification");
501 }
502
503 cmd.run(builder);
504 }
505
506 if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
509 builder.open_in_browser(&html);
510 }
511 }
512}
513
514#[derive(Debug, Clone)]
515pub struct SharedAssetsPaths {
516 pub version_info: PathBuf,
517}
518
519#[derive(Debug, Clone, Hash, PartialEq, Eq)]
520pub struct SharedAssets {
521 target: TargetSelection,
522}
523
524impl Step for SharedAssets {
525 type Output = SharedAssetsPaths;
526 const DEFAULT: bool = false;
527
528 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
529 run.never()
531 }
532
533 fn run(self, builder: &Builder<'_>) -> Self::Output {
535 let out = builder.doc_out(self.target);
536
537 let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
538 let version_info = out.join("version_info.html");
539 if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
540 let info = t!(fs::read_to_string(&version_input))
541 .replace("VERSION", &builder.rust_release())
542 .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
543 .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
544 t!(fs::write(&version_info, info));
545 }
546
547 builder.copy_link(
548 &builder.src.join("src").join("doc").join("rust.css"),
549 &out.join("rust.css"),
550 FileType::Regular,
551 );
552
553 SharedAssetsPaths { version_info }
554 }
555}
556
557#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
558pub struct Std {
559 pub stage: u32,
560 pub target: TargetSelection,
561 pub format: DocumentationFormat,
562 crates: Vec<String>,
563}
564
565impl Std {
566 pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self {
567 Std { stage, target, format, crates: vec![] }
568 }
569}
570
571impl Step for Std {
572 type Output = ();
573 const DEFAULT: bool = true;
574
575 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
576 let builder = run.builder;
577 run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
578 }
579
580 fn make_run(run: RunConfig<'_>) {
581 let crates = compile::std_crates_for_run_make(&run);
582 let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
583 if crates.is_empty() && target_is_no_std {
584 return;
585 }
586 run.builder.ensure(Std {
587 stage: run.builder.top_stage,
588 target: run.target,
589 format: if run.builder.config.cmd.json() {
590 DocumentationFormat::Json
591 } else {
592 DocumentationFormat::Html
593 },
594 crates,
595 });
596 }
597
598 fn run(self, builder: &Builder<'_>) {
603 let stage = self.stage;
604 let target = self.target;
605 let crates = if self.crates.is_empty() {
606 builder
607 .in_tree_crates("sysroot", Some(target))
608 .iter()
609 .map(|c| c.name.to_string())
610 .collect()
611 } else {
612 self.crates
613 };
614
615 let out = match self.format {
616 DocumentationFormat::Html => builder.doc_out(target),
617 DocumentationFormat::Json => builder.json_doc_out(target),
618 };
619
620 t!(fs::create_dir_all(&out));
621
622 if self.format == DocumentationFormat::Html {
623 builder.ensure(SharedAssets { target: self.target });
624 }
625
626 let index_page = builder
627 .src
628 .join("src/doc/index.md")
629 .into_os_string()
630 .into_string()
631 .expect("non-utf8 paths are unsupported");
632 let mut extra_args = match self.format {
633 DocumentationFormat::Html => {
634 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
635 }
636 DocumentationFormat::Json => vec!["--output-format", "json"],
637 };
638
639 if !builder.config.docs_minification {
640 extra_args.push("--disable-minification");
641 }
642 extra_args.push("-Zunstable-options");
644
645 doc_std(builder, self.format, stage, target, &out, &extra_args, &crates);
646
647 if let DocumentationFormat::Json = self.format {
649 return;
650 }
651
652 if builder.paths.iter().any(|path| path.ends_with("library")) {
653 let index = out.join("std").join("index.html");
655 builder.open_in_browser(index);
656 } else {
657 for requested_crate in crates {
658 if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
659 let index = out.join(requested_crate).join("index.html");
660 builder.open_in_browser(index);
661 break;
662 }
663 }
664 }
665 }
666
667 fn metadata(&self) -> Option<StepMetadata> {
668 Some(StepMetadata::doc("std", self.target).stage(self.stage))
669 }
670}
671
672const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
682
683#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
684pub enum DocumentationFormat {
685 Html,
686 Json,
687}
688
689impl DocumentationFormat {
690 fn as_str(&self) -> &str {
691 match self {
692 DocumentationFormat::Html => "HTML",
693 DocumentationFormat::Json => "JSON",
694 }
695 }
696}
697
698fn doc_std(
700 builder: &Builder<'_>,
701 format: DocumentationFormat,
702 stage: u32,
703 target: TargetSelection,
704 out: &Path,
705 extra_args: &[&str],
706 requested_crates: &[String],
707) {
708 let compiler = builder.compiler(stage, builder.config.host_target);
709
710 let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
711 let target_dir = builder.stage_out(compiler, Mode::Std).join(target).join(target_doc_dir_name);
712
713 let out_dir = target_dir.join(target).join("doc");
717
718 let mut cargo =
719 builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, Kind::Doc);
720
721 compile::std_cargo(builder, target, compiler.stage, &mut cargo);
722 cargo
723 .arg("--no-deps")
724 .arg("--target-dir")
725 .arg(&*target_dir.to_string_lossy())
726 .arg("-Zskip-rustdoc-fingerprint")
727 .arg("-Zrustdoc-map")
728 .rustdocflag("--extern-html-root-url")
729 .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
730 .rustdocflag("--extern-html-root-takes-precedence")
731 .rustdocflag("--resource-suffix")
732 .rustdocflag(&builder.version);
733 for arg in extra_args {
734 cargo.rustdocflag(arg);
735 }
736
737 if builder.config.library_docs_private_items {
738 cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
739 }
740
741 for krate in requested_crates {
742 if krate == "sysroot" {
743 continue;
745 }
746 cargo.arg("-p").arg(krate);
747 }
748
749 let description =
750 format!("library{} in {} format", crate_description(requested_crates), format.as_str());
751 let _guard = builder.msg_doc(compiler, description, target);
752
753 cargo.into_cmd().run(builder);
754 builder.cp_link_r(&out_dir, out);
755}
756
757#[derive(Debug, Clone, Hash, PartialEq, Eq)]
758pub struct Rustc {
759 pub stage: u32,
760 pub target: TargetSelection,
761 crates: Vec<String>,
762}
763
764impl Rustc {
765 pub(crate) fn new(stage: u32, target: TargetSelection, builder: &Builder<'_>) -> Self {
766 let crates = builder
767 .in_tree_crates("rustc-main", Some(target))
768 .into_iter()
769 .map(|krate| krate.name.to_string())
770 .collect();
771 Self { stage, target, crates }
772 }
773}
774
775impl Step for Rustc {
776 type Output = ();
777 const DEFAULT: bool = true;
778 const ONLY_HOSTS: bool = true;
779
780 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
781 let builder = run.builder;
782 run.crate_or_deps("rustc-main")
783 .path("compiler")
784 .default_condition(builder.config.compiler_docs)
785 }
786
787 fn make_run(run: RunConfig<'_>) {
788 run.builder.ensure(Rustc {
789 stage: run.builder.top_stage,
790 target: run.target,
791 crates: run.make_run_crates(Alias::Compiler),
792 });
793 }
794
795 fn run(self, builder: &Builder<'_>) {
802 let stage = self.stage;
803 let target = self.target;
804
805 let out = builder.compiler_doc_out(target);
807 t!(fs::create_dir_all(&out));
808
809 let compiler = builder.compiler(stage, builder.config.host_target);
812 builder.std(compiler, builder.config.host_target);
813
814 let _guard = builder.msg_sysroot_tool(
815 Kind::Doc,
816 stage,
817 format!("compiler{}", crate_description(&self.crates)),
818 compiler.host,
819 target,
820 );
821
822 let mut cargo = builder::Cargo::new(
824 builder,
825 compiler,
826 Mode::Rustc,
827 SourceType::InTree,
828 target,
829 Kind::Doc,
830 );
831
832 cargo.rustdocflag("--document-private-items");
833 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
835 cargo.rustdocflag("--enable-index-page");
836 cargo.rustdocflag("-Znormalize-docs");
837 cargo.rustdocflag("--show-type-layout");
838 cargo.rustdocflag("--generate-link-to-definition");
842
843 compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
844 cargo.arg("-Zskip-rustdoc-fingerprint");
845
846 cargo.arg("--no-deps");
849 cargo.arg("-Zrustdoc-map");
850
851 cargo.rustdocflag("--extern-html-root-url");
854 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
855
856 let mut to_open = None;
857
858 let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target).join("doc");
859 for krate in &*self.crates {
860 let dir_name = krate.replace('-', "_");
864 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
865 cargo.arg("-p").arg(krate);
866 if to_open.is_none() {
867 to_open = Some(dir_name);
868 }
869 }
870
871 symlink_dir_force(&builder.config, &out, &out_dir);
878 let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
881 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
882
883 cargo.into_cmd().run(builder);
884
885 if !builder.config.dry_run() {
886 for krate in &*self.crates {
888 let dir_name = krate.replace('-', "_");
889 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
891 }
892 }
893
894 if builder.paths.iter().any(|path| path.ends_with("compiler")) {
895 let index = out.join("rustc_middle").join("index.html");
897 builder.open_in_browser(index);
898 } else if let Some(krate) = to_open {
899 let index = out.join(krate).join("index.html");
901 builder.open_in_browser(index);
902 }
903 }
904}
905
906macro_rules! tool_doc {
907 (
908 $tool: ident,
909 $path: literal,
910 $(rustc_tool = $rustc_tool:literal, )?
911 $(is_library = $is_library:expr,)?
912 $(crates = $crates:expr)?
913 ) => {
914 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
915 pub struct $tool {
916 target: TargetSelection,
917 }
918
919 impl Step for $tool {
920 type Output = ();
921 const DEFAULT: bool = true;
922 const ONLY_HOSTS: bool = true;
923
924 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
925 let builder = run.builder;
926 run.path($path).default_condition(builder.config.compiler_docs)
927 }
928
929 fn make_run(run: RunConfig<'_>) {
930 run.builder.ensure($tool { target: run.target });
931 }
932
933 fn run(self, builder: &Builder<'_>) {
940 let mut source_type = SourceType::InTree;
941
942 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
943 source_type = SourceType::Submodule;
944 builder.require_submodule(&submodule_path, None);
945 }
946
947 let stage = builder.top_stage;
948 let target = self.target;
949
950 let out = builder.compiler_doc_out(target);
952 t!(fs::create_dir_all(&out));
953
954 let compiler = builder.compiler(stage, builder.config.host_target);
955 builder.std(compiler, target);
956
957 if true $(&& $rustc_tool)? {
958 builder.ensure(Rustc::new(stage, target, builder));
960
961 builder.ensure(compile::Rustc::new(compiler, target));
965 }
966
967 let mut cargo = prepare_tool_cargo(
969 builder,
970 compiler,
971 Mode::ToolRustc,
972 target,
973 Kind::Doc,
974 $path,
975 source_type,
976 &[],
977 );
978
979 cargo.arg("-Zskip-rustdoc-fingerprint");
980 cargo.arg("--no-deps");
982
983 if false $(|| $is_library)? {
984 cargo.arg("--lib");
985 }
986
987 $(for krate in $crates {
988 cargo.arg("-p").arg(krate);
989 })?
990
991 cargo.rustdocflag("--document-private-items");
992 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
994 cargo.rustdocflag("--enable-index-page");
995 cargo.rustdocflag("--show-type-layout");
996 cargo.rustdocflag("--generate-link-to-definition");
997
998 let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target).join("doc");
999 $(for krate in $crates {
1000 let dir_name = krate.replace("-", "_");
1001 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1002 })?
1003
1004 symlink_dir_force(&builder.config, &out, &out_dir);
1006 let proc_macro_out_dir = builder.stage_out(compiler, Mode::ToolRustc).join("doc");
1007 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1008
1009 let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target);
1010 cargo.into_cmd().run(builder);
1011
1012 if !builder.config.dry_run() {
1013 $(for krate in $crates {
1015 let dir_name = krate.replace("-", "_");
1016 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1018 })?
1019 }
1020 }
1021 }
1022 }
1023}
1024
1025tool_doc!(
1027 BuildHelper,
1028 "src/build_helper",
1029 rustc_tool = false,
1030 is_library = true,
1031 crates = ["build_helper"]
1032);
1033tool_doc!(Rustdoc, "src/tools/rustdoc", crates = ["rustdoc", "rustdoc-json-types"]);
1034tool_doc!(Rustfmt, "src/tools/rustfmt", crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]);
1035tool_doc!(Clippy, "src/tools/clippy", crates = ["clippy_config", "clippy_utils"]);
1036tool_doc!(Miri, "src/tools/miri", crates = ["miri"]);
1037tool_doc!(
1038 Cargo,
1039 "src/tools/cargo",
1040 rustc_tool = false,
1041 crates = [
1042 "cargo",
1043 "cargo-credential",
1044 "cargo-platform",
1045 "cargo-test-macro",
1046 "cargo-test-support",
1047 "cargo-util",
1048 "cargo-util-schemas",
1049 "crates-io",
1050 "mdman",
1051 "rustfix",
1052 ]
1053);
1054tool_doc!(Tidy, "src/tools/tidy", rustc_tool = false, crates = ["tidy"]);
1055tool_doc!(
1056 Bootstrap,
1057 "src/bootstrap",
1058 rustc_tool = false,
1059 is_library = true,
1060 crates = ["bootstrap"]
1061);
1062tool_doc!(
1063 RunMakeSupport,
1064 "src/tools/run-make-support",
1065 rustc_tool = false,
1066 is_library = true,
1067 crates = ["run_make_support"]
1068);
1069tool_doc!(
1070 Compiletest,
1071 "src/tools/compiletest",
1072 rustc_tool = false,
1073 is_library = true,
1074 crates = ["compiletest"]
1075);
1076
1077#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
1078pub struct ErrorIndex {
1079 pub target: TargetSelection,
1080}
1081
1082impl Step for ErrorIndex {
1083 type Output = ();
1084 const DEFAULT: bool = true;
1085 const ONLY_HOSTS: bool = true;
1086
1087 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1088 let builder = run.builder;
1089 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1090 }
1091
1092 fn make_run(run: RunConfig<'_>) {
1093 let target = run.target;
1094 run.builder.ensure(ErrorIndex { target });
1095 }
1096
1097 fn run(self, builder: &Builder<'_>) {
1100 builder.info(&format!("Documenting error index ({})", self.target));
1101 let out = builder.doc_out(self.target);
1102 t!(fs::create_dir_all(&out));
1103 tool::ErrorIndex::command(builder).arg("html").arg(out).arg(&builder.version).run(builder);
1104 }
1105}
1106
1107#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1108pub struct UnstableBookGen {
1109 target: TargetSelection,
1110}
1111
1112impl Step for UnstableBookGen {
1113 type Output = ();
1114 const DEFAULT: bool = true;
1115 const ONLY_HOSTS: bool = true;
1116
1117 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1118 let builder = run.builder;
1119 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
1120 }
1121
1122 fn make_run(run: RunConfig<'_>) {
1123 run.builder.ensure(UnstableBookGen { target: run.target });
1124 }
1125
1126 fn run(self, builder: &Builder<'_>) {
1127 let target = self.target;
1128
1129 builder.info(&format!("Generating unstable book md files ({target})"));
1130 let out = builder.md_doc_out(target).join("unstable-book");
1131 builder.create_dir(&out);
1132 builder.remove_dir(&out);
1133 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1134 cmd.arg(builder.src.join("library"));
1135 cmd.arg(builder.src.join("compiler"));
1136 cmd.arg(builder.src.join("src"));
1137 cmd.arg(out);
1138
1139 cmd.run(builder);
1140 }
1141}
1142
1143fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1144 if config.dry_run() {
1145 return;
1146 }
1147 if let Ok(m) = fs::symlink_metadata(link) {
1148 if m.file_type().is_dir() {
1149 t!(fs::remove_dir_all(link));
1150 } else {
1151 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1154 }
1155 }
1156
1157 t!(
1158 symlink_dir(config, original, link),
1159 format!("failed to create link from {} -> {}", link.display(), original.display())
1160 );
1161}
1162
1163#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
1164pub struct RustcBook {
1165 pub compiler: Compiler,
1166 pub target: TargetSelection,
1167 pub validate: bool,
1168}
1169
1170impl Step for RustcBook {
1171 type Output = ();
1172 const DEFAULT: bool = true;
1173 const ONLY_HOSTS: bool = true;
1174
1175 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1176 let builder = run.builder;
1177 run.path("src/doc/rustc").default_condition(builder.config.docs)
1178 }
1179
1180 fn make_run(run: RunConfig<'_>) {
1181 run.builder.ensure(RustcBook {
1182 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
1183 target: run.target,
1184 validate: false,
1185 });
1186 }
1187
1188 fn run(self, builder: &Builder<'_>) {
1194 let out_base = builder.md_doc_out(self.target).join("rustc");
1195 t!(fs::create_dir_all(&out_base));
1196 let out_listing = out_base.join("src/lints");
1197 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1198 builder.info(&format!("Generating lint docs ({})", self.target));
1199
1200 let rustc = builder.rustc(self.compiler);
1201 builder.std(self.compiler, self.target);
1204 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1205 cmd.arg("--src");
1206 cmd.arg(builder.src.join("compiler"));
1207 cmd.arg("--out");
1208 cmd.arg(&out_listing);
1209 cmd.arg("--rustc");
1210 cmd.arg(&rustc);
1211 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1212 if let Some(target_linker) = builder.linker(self.target) {
1213 cmd.arg("--rustc-linker").arg(target_linker);
1214 }
1215 if builder.is_verbose() {
1216 cmd.arg("--verbose");
1217 }
1218 if self.validate {
1219 cmd.arg("--validate");
1220 }
1221 cmd.env("RUSTC_BOOTSTRAP", "1");
1225
1226 builder.add_rustc_lib_path(self.compiler, &mut cmd);
1230 let doc_generator_guard = builder.msg(
1231 Kind::Run,
1232 self.compiler.stage,
1233 "lint-docs",
1234 self.compiler.host,
1235 self.target,
1236 );
1237 cmd.run(builder);
1238 drop(doc_generator_guard);
1239
1240 builder.ensure(RustbookSrc {
1242 target: self.target,
1243 name: "rustc".to_owned(),
1244 src: out_base,
1245 parent: Some(self),
1246 languages: vec![],
1247 rustdoc_compiler: None,
1248 });
1249 }
1250}
1251
1252#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
1253pub struct Reference {
1254 pub compiler: Compiler,
1255 pub target: TargetSelection,
1256}
1257
1258impl Step for Reference {
1259 type Output = ();
1260 const DEFAULT: bool = true;
1261
1262 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1263 let builder = run.builder;
1264 run.path("src/doc/reference").default_condition(builder.config.docs)
1265 }
1266
1267 fn make_run(run: RunConfig<'_>) {
1268 run.builder.ensure(Reference {
1269 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
1270 target: run.target,
1271 });
1272 }
1273
1274 fn run(self, builder: &Builder<'_>) {
1276 builder.require_submodule("src/doc/reference", None);
1277
1278 builder.std(self.compiler, builder.config.host_target);
1281
1282 builder.ensure(RustbookSrc {
1284 target: self.target,
1285 name: "reference".to_owned(),
1286 src: builder.src.join("src/doc/reference"),
1287 rustdoc_compiler: Some(self.compiler),
1288 parent: Some(self),
1289 languages: vec![],
1290 });
1291 }
1292}