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::{
16 self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo,
17};
18use crate::core::builder::{
19 self, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
20};
21use crate::core::config::{Config, TargetSelection};
22use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date};
23use crate::{FileType, Mode};
24
25macro_rules! book {
26 ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => {
27 $(
28 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
29 pub struct $name {
30 target: TargetSelection,
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 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
50 builder.require_submodule(&submodule_path, None)
51 }
52
53 builder.ensure(RustbookSrc {
54 target: self.target,
55 name: $book_name.to_owned(),
56 src: builder.src.join($path),
57 parent: Some(self),
58 languages: $lang.into(),
59 build_compiler: None,
60 })
61 }
62 }
63 )+
64 }
65}
66
67book!(
71 CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
72 ClippyBook, "src/tools/clippy/book", "clippy", &[];
73 EditionGuide, "src/doc/edition-guide", "edition-guide", &[];
74 EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[];
75 Nomicon, "src/doc/nomicon", "nomicon", &[];
76 RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja", "zh"];
77 RustdocBook, "src/doc/rustdoc", "rustdoc", &[];
78 StyleGuide, "src/doc/style-guide", "style-guide", &[];
79);
80
81#[derive(Debug, Clone, Hash, PartialEq, Eq)]
82pub struct UnstableBook {
83 target: TargetSelection,
84}
85
86impl Step for UnstableBook {
87 type Output = ();
88 const DEFAULT: bool = true;
89
90 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
91 let builder = run.builder;
92 run.path("src/doc/unstable-book").default_condition(builder.config.docs)
93 }
94
95 fn make_run(run: RunConfig<'_>) {
96 run.builder.ensure(UnstableBook { target: run.target });
97 }
98
99 fn run(self, builder: &Builder<'_>) {
100 builder.ensure(UnstableBookGen { target: self.target });
101 builder.ensure(RustbookSrc {
102 target: self.target,
103 name: "unstable-book".to_owned(),
104 src: builder.md_doc_out(self.target).join("unstable-book"),
105 parent: Some(self),
106 languages: vec![],
107 build_compiler: None,
108 })
109 }
110}
111
112#[derive(Debug, Clone, Hash, PartialEq, Eq)]
113struct RustbookSrc<P: Step> {
114 target: TargetSelection,
115 name: String,
116 src: PathBuf,
117 parent: Option<P>,
118 languages: Vec<&'static str>,
119 build_compiler: Option<Compiler>,
121}
122
123impl<P: Step> Step for RustbookSrc<P> {
124 type Output = ();
125
126 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
127 run.never()
128 }
129
130 fn run(self, builder: &Builder<'_>) {
135 let target = self.target;
136 let name = self.name;
137 let src = self.src;
138 let out = builder.doc_out(target);
139 t!(fs::create_dir_all(&out));
140
141 let out = out.join(&name);
142 let index = out.join("index.html");
143 let rustbook = builder.tool_exe(Tool::Rustbook);
144
145 if !builder.config.dry_run()
146 && (!up_to_date(&src, &index) || !up_to_date(&rustbook, &index))
147 {
148 builder.info(&format!("Rustbook ({target}) - {name}"));
149 let _ = fs::remove_dir_all(&out);
150
151 let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
152
153 if let Some(compiler) = self.build_compiler {
154 let mut rustdoc = builder.rustdoc_for_compiler(compiler);
155 rustdoc.pop();
156 let old_path = env::var_os("PATH").unwrap_or_default();
157 let new_path =
158 env::join_paths(std::iter::once(rustdoc).chain(env::split_paths(&old_path)))
159 .expect("could not add rustdoc to PATH");
160
161 rustbook_cmd.env("PATH", new_path);
162 builder.add_rustc_lib_path(compiler, &mut rustbook_cmd);
163 }
164
165 rustbook_cmd
166 .arg("build")
167 .arg(&src)
168 .arg("-d")
169 .arg(&out)
170 .arg("--rust-root")
171 .arg(&builder.src)
172 .run(builder);
173
174 for lang in &self.languages {
175 let out = out.join(lang);
176
177 builder.info(&format!("Rustbook ({target}) - {name} - {lang}"));
178 let _ = fs::remove_dir_all(&out);
179
180 builder
181 .tool_cmd(Tool::Rustbook)
182 .arg("build")
183 .arg(&src)
184 .arg("-d")
185 .arg(&out)
186 .arg("-l")
187 .arg(lang)
188 .run(builder);
189 }
190 }
191
192 if self.parent.is_some() {
193 builder.maybe_open_in_browser::<P>(index)
194 }
195 }
196
197 fn metadata(&self) -> Option<StepMetadata> {
198 let mut metadata = StepMetadata::doc(&format!("{} (book)", self.name), self.target);
199 if let Some(compiler) = self.build_compiler {
200 metadata = metadata.built_by(compiler);
201 }
202
203 Some(metadata)
204 }
205}
206
207#[derive(Debug, Clone, Hash, PartialEq, Eq)]
208pub struct TheBook {
209 build_compiler: Compiler,
211 target: TargetSelection,
212}
213
214impl Step for TheBook {
215 type Output = ();
216 const DEFAULT: bool = true;
217
218 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
219 let builder = run.builder;
220 run.path("src/doc/book").default_condition(builder.config.docs)
221 }
222
223 fn make_run(run: RunConfig<'_>) {
224 run.builder.ensure(TheBook {
225 build_compiler: prepare_doc_compiler(run.builder, run.target, run.builder.top_stage),
226 target: run.target,
227 });
228 }
229
230 fn run(self, builder: &Builder<'_>) {
240 builder.require_submodule("src/doc/book", None);
241
242 let build_compiler = self.build_compiler;
243 let target = self.target;
244
245 let absolute_path = builder.src.join("src/doc/book");
246 let redirect_path = absolute_path.join("redirects");
247
248 builder.ensure(RustbookSrc {
250 target,
251 name: "book".to_owned(),
252 src: absolute_path.clone(),
253 parent: Some(self),
254 languages: vec![],
255 build_compiler: None,
256 });
257
258 for edition in &["first-edition", "second-edition", "2018-edition"] {
260 builder.ensure(RustbookSrc {
261 target,
262 name: format!("book/{edition}"),
263 src: absolute_path.join(edition),
264 parent: Option::<Self>::None,
267 languages: vec![],
268 build_compiler: None,
269 });
270 }
271
272 let shared_assets = builder.ensure(SharedAssets { target });
274
275 let _guard = builder.msg(Kind::Doc, "book redirect pages", None, build_compiler, target);
277 for file in t!(fs::read_dir(redirect_path)) {
278 let file = t!(file);
279 let path = file.path();
280 let path = path.to_str().unwrap();
281
282 invoke_rustdoc(builder, build_compiler, &shared_assets, target, path);
283 }
284 }
285}
286
287fn invoke_rustdoc(
288 builder: &Builder<'_>,
289 build_compiler: Compiler,
290 shared_assets: &SharedAssetsPaths,
291 target: TargetSelection,
292 markdown: &str,
293) {
294 let out = builder.doc_out(target);
295
296 let path = builder.src.join("src/doc").join(markdown);
297
298 let header = builder.src.join("src/doc/redirect.inc");
299 let footer = builder.src.join("src/doc/footer.inc");
300
301 let mut cmd = builder.rustdoc_cmd(build_compiler);
302
303 let out = out.join("book");
304
305 cmd.arg("--html-after-content")
306 .arg(&footer)
307 .arg("--html-before-content")
308 .arg(&shared_assets.version_info)
309 .arg("--html-in-header")
310 .arg(&header)
311 .arg("--markdown-no-toc")
312 .arg("--markdown-playground-url")
313 .arg("https://play.rust-lang.org/")
314 .arg("-o")
315 .arg(&out)
316 .arg(&path)
317 .arg("--markdown-css")
318 .arg("../rust.css")
319 .arg("-Zunstable-options");
320
321 if !builder.config.docs_minification {
322 cmd.arg("--disable-minification");
323 }
324
325 cmd.run(builder);
326}
327
328#[derive(Debug, Clone, Hash, PartialEq, Eq)]
329pub struct Standalone {
330 build_compiler: Compiler,
331 target: TargetSelection,
332}
333
334impl Step for Standalone {
335 type Output = ();
336 const DEFAULT: bool = true;
337
338 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
339 let builder = run.builder;
340 run.path("src/doc").alias("standalone").default_condition(builder.config.docs)
341 }
342
343 fn make_run(run: RunConfig<'_>) {
344 run.builder.ensure(Standalone {
345 build_compiler: prepare_doc_compiler(
346 run.builder,
347 run.builder.host_target,
348 run.builder.top_stage,
349 ),
350 target: run.target,
351 });
352 }
353
354 fn run(self, builder: &Builder<'_>) {
363 let target = self.target;
364 let build_compiler = self.build_compiler;
365 let _guard = builder.msg(Kind::Doc, "standalone", None, build_compiler, target);
366 let out = builder.doc_out(target);
367 t!(fs::create_dir_all(&out));
368
369 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
370
371 let favicon = builder.src.join("src/doc/favicon.inc");
372 let footer = builder.src.join("src/doc/footer.inc");
373 let full_toc = builder.src.join("src/doc/full-toc.inc");
374
375 for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
376 let file = t!(file);
377 let path = file.path();
378 let filename = path.file_name().unwrap().to_str().unwrap();
379 if !filename.ends_with(".md") || filename == "README.md" {
380 continue;
381 }
382
383 let html = out.join(filename).with_extension("html");
384 let rustdoc = builder.rustdoc_for_compiler(build_compiler);
385 if up_to_date(&path, &html)
386 && up_to_date(&footer, &html)
387 && up_to_date(&favicon, &html)
388 && up_to_date(&full_toc, &html)
389 && (builder.config.dry_run() || up_to_date(&version_info, &html))
390 && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
391 {
392 continue;
393 }
394
395 let mut cmd = builder.rustdoc_cmd(build_compiler);
396
397 cmd.arg("--html-after-content")
398 .arg(&footer)
399 .arg("--html-before-content")
400 .arg(&version_info)
401 .arg("--html-in-header")
402 .arg(&favicon)
403 .arg("--markdown-no-toc")
404 .arg("-Zunstable-options")
405 .arg("--index-page")
406 .arg(builder.src.join("src/doc/index.md"))
407 .arg("--markdown-playground-url")
408 .arg("https://play.rust-lang.org/")
409 .arg("-o")
410 .arg(&out)
411 .arg(&path);
412
413 if !builder.config.docs_minification {
414 cmd.arg("--disable-minification");
415 }
416
417 if filename == "not_found.md" {
418 cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
419 } else {
420 cmd.arg("--markdown-css").arg("rust.css");
421 }
422 cmd.run(builder);
423 }
424
425 if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
428 let index = out.join("index.html");
429 builder.open_in_browser(index);
430 }
431 }
432
433 fn metadata(&self) -> Option<StepMetadata> {
434 Some(StepMetadata::doc("standalone", self.target).built_by(self.build_compiler))
435 }
436}
437
438#[derive(Debug, Clone, Hash, PartialEq, Eq)]
439pub struct Releases {
440 build_compiler: Compiler,
441 target: TargetSelection,
442}
443
444impl Step for Releases {
445 type Output = ();
446 const DEFAULT: bool = true;
447
448 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
449 let builder = run.builder;
450 run.path("RELEASES.md").alias("releases").default_condition(builder.config.docs)
451 }
452
453 fn make_run(run: RunConfig<'_>) {
454 run.builder.ensure(Releases {
455 build_compiler: prepare_doc_compiler(
456 run.builder,
457 run.builder.host_target,
458 run.builder.top_stage,
459 ),
460 target: run.target,
461 });
462 }
463
464 fn run(self, builder: &Builder<'_>) {
470 let target = self.target;
471 let build_compiler = self.build_compiler;
472 let _guard = builder.msg(Kind::Doc, "releases", None, build_compiler, target);
473 let out = builder.doc_out(target);
474 t!(fs::create_dir_all(&out));
475
476 builder.ensure(Standalone { build_compiler, target });
477
478 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
479
480 let favicon = builder.src.join("src/doc/favicon.inc");
481 let footer = builder.src.join("src/doc/footer.inc");
482 let full_toc = builder.src.join("src/doc/full-toc.inc");
483
484 let html = out.join("releases.html");
485 let tmppath = out.join("releases.md");
486 let inpath = builder.src.join("RELEASES.md");
487 let rustdoc = builder.rustdoc_for_compiler(build_compiler);
488 if !up_to_date(&inpath, &html)
489 || !up_to_date(&footer, &html)
490 || !up_to_date(&favicon, &html)
491 || !up_to_date(&full_toc, &html)
492 || !(builder.config.dry_run()
493 || up_to_date(&version_info, &html)
494 || up_to_date(&rustdoc, &html))
495 {
496 let mut tmpfile = t!(fs::File::create(&tmppath));
497 t!(tmpfile.write_all(b"% Rust Release Notes\n\n"));
498 t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile));
499 mem::drop(tmpfile);
500 let mut cmd = builder.rustdoc_cmd(build_compiler);
501
502 cmd.arg("--html-after-content")
503 .arg(&footer)
504 .arg("--html-before-content")
505 .arg(&version_info)
506 .arg("--html-in-header")
507 .arg(&favicon)
508 .arg("--markdown-no-toc")
509 .arg("--markdown-css")
510 .arg("rust.css")
511 .arg("-Zunstable-options")
512 .arg("--index-page")
513 .arg(builder.src.join("src/doc/index.md"))
514 .arg("--markdown-playground-url")
515 .arg("https://play.rust-lang.org/")
516 .arg("-o")
517 .arg(&out)
518 .arg(&tmppath);
519
520 if !builder.config.docs_minification {
521 cmd.arg("--disable-minification");
522 }
523
524 cmd.run(builder);
525 }
526
527 if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
530 builder.open_in_browser(&html);
531 }
532 }
533
534 fn metadata(&self) -> Option<StepMetadata> {
535 Some(StepMetadata::doc("releases", self.target).built_by(self.build_compiler))
536 }
537}
538
539#[derive(Debug, Clone)]
540pub struct SharedAssetsPaths {
541 pub version_info: PathBuf,
542}
543
544#[derive(Debug, Clone, Hash, PartialEq, Eq)]
545pub struct SharedAssets {
546 target: TargetSelection,
547}
548
549impl Step for SharedAssets {
550 type Output = SharedAssetsPaths;
551 const DEFAULT: bool = false;
552
553 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
554 run.never()
556 }
557
558 fn run(self, builder: &Builder<'_>) -> Self::Output {
560 let out = builder.doc_out(self.target);
561
562 let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
563 let version_info = out.join("version_info.html");
564 if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
565 let info = t!(fs::read_to_string(&version_input))
566 .replace("VERSION", &builder.rust_release())
567 .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
568 .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
569 t!(fs::write(&version_info, info));
570 }
571
572 builder.copy_link(
573 &builder.src.join("src").join("doc").join("rust.css"),
574 &out.join("rust.css"),
575 FileType::Regular,
576 );
577
578 builder.copy_link(
579 &builder
580 .src
581 .join("src")
582 .join("librustdoc")
583 .join("html")
584 .join("static")
585 .join("images")
586 .join("favicon.svg"),
587 &out.join("favicon.svg"),
588 FileType::Regular,
589 );
590 builder.copy_link(
591 &builder
592 .src
593 .join("src")
594 .join("librustdoc")
595 .join("html")
596 .join("static")
597 .join("images")
598 .join("favicon-32x32.png"),
599 &out.join("favicon-32x32.png"),
600 FileType::Regular,
601 );
602
603 SharedAssetsPaths { version_info }
604 }
605}
606
607#[derive(Debug, Clone, Hash, PartialEq, Eq)]
609pub struct Std {
610 build_compiler: Compiler,
611 target: TargetSelection,
612 format: DocumentationFormat,
613 crates: Vec<String>,
614}
615
616impl Std {
617 pub(crate) fn from_build_compiler(
618 build_compiler: Compiler,
619 target: TargetSelection,
620 format: DocumentationFormat,
621 ) -> Self {
622 Std { build_compiler, target, format, crates: vec![] }
623 }
624}
625
626impl Step for Std {
627 type Output = PathBuf;
629
630 const DEFAULT: bool = true;
631
632 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
633 let builder = run.builder;
634 run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
635 }
636
637 fn make_run(run: RunConfig<'_>) {
638 let crates = compile::std_crates_for_run_make(&run);
639 let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
640 if crates.is_empty() && target_is_no_std {
641 return;
642 }
643 run.builder.ensure(Std {
644 build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
645 target: run.target,
646 format: if run.builder.config.cmd.json() {
647 DocumentationFormat::Json
648 } else {
649 DocumentationFormat::Html
650 },
651 crates,
652 });
653 }
654
655 fn run(self, builder: &Builder<'_>) -> Self::Output {
660 let target = self.target;
661 let crates = if self.crates.is_empty() {
662 builder
663 .in_tree_crates("sysroot", Some(target))
664 .iter()
665 .map(|c| c.name.to_string())
666 .collect()
667 } else {
668 self.crates
669 };
670
671 let out = match self.format {
672 DocumentationFormat::Html => builder.doc_out(target),
673 DocumentationFormat::Json => builder.json_doc_out(target),
674 };
675
676 t!(fs::create_dir_all(&out));
677
678 if self.format == DocumentationFormat::Html {
679 builder.ensure(SharedAssets { target: self.target });
680 }
681
682 let index_page = builder
683 .src
684 .join("src/doc/index.md")
685 .into_os_string()
686 .into_string()
687 .expect("non-utf8 paths are unsupported");
688 let mut extra_args = match self.format {
689 DocumentationFormat::Html => {
690 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
691 }
692 DocumentationFormat::Json => vec!["--output-format", "json"],
693 };
694
695 if !builder.config.docs_minification {
696 extra_args.push("--disable-minification");
697 }
698 extra_args.push("-Zunstable-options");
700
701 doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates);
702
703 if let DocumentationFormat::Html = self.format {
705 if builder.paths.iter().any(|path| path.ends_with("library")) {
706 let index = out.join("std").join("index.html");
708 builder.open_in_browser(index);
709 } else {
710 for requested_crate in crates {
711 if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
712 let index = out.join(requested_crate).join("index.html");
713 builder.open_in_browser(index);
714 break;
715 }
716 }
717 }
718 }
719
720 out
721 }
722
723 fn metadata(&self) -> Option<StepMetadata> {
724 Some(
725 StepMetadata::doc("std", self.target)
726 .built_by(self.build_compiler)
727 .with_metadata(format!("crates=[{}]", self.crates.join(","))),
728 )
729 }
730}
731
732const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
742
743#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
744pub enum DocumentationFormat {
745 Html,
746 Json,
747}
748
749impl DocumentationFormat {
750 fn as_str(&self) -> &str {
751 match self {
752 DocumentationFormat::Html => "HTML",
753 DocumentationFormat::Json => "JSON",
754 }
755 }
756}
757
758fn doc_std(
760 builder: &Builder<'_>,
761 format: DocumentationFormat,
762 build_compiler: Compiler,
763 target: TargetSelection,
764 out: &Path,
765 extra_args: &[&str],
766 requested_crates: &[String],
767) {
768 let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
769 let target_dir =
770 builder.stage_out(build_compiler, Mode::Std).join(target).join(target_doc_dir_name);
771
772 let out_dir = target_dir.join(target).join("doc");
776
777 let mut cargo = builder::Cargo::new(
778 builder,
779 build_compiler,
780 Mode::Std,
781 SourceType::InTree,
782 target,
783 Kind::Doc,
784 );
785
786 compile::std_cargo(builder, target, &mut cargo);
787 cargo
788 .arg("--no-deps")
789 .arg("--target-dir")
790 .arg(&*target_dir.to_string_lossy())
791 .arg("-Zskip-rustdoc-fingerprint")
792 .arg("-Zrustdoc-map")
793 .rustdocflag("--extern-html-root-url")
794 .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
795 .rustdocflag("--extern-html-root-takes-precedence")
796 .rustdocflag("--resource-suffix")
797 .rustdocflag(&builder.version);
798 for arg in extra_args {
799 cargo.rustdocflag(arg);
800 }
801
802 if builder.config.library_docs_private_items {
803 cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
804 }
805
806 for krate in requested_crates {
807 cargo.arg("-p").arg(krate);
808 }
809
810 let description =
811 format!("library{} in {} format", crate_description(requested_crates), format.as_str());
812 let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target);
813
814 cargo.into_cmd().run(builder);
815 builder.cp_link_r(&out_dir, out);
816}
817
818pub fn prepare_doc_compiler(
820 builder: &Builder<'_>,
821 target: TargetSelection,
822 stage: u32,
823) -> Compiler {
824 assert!(stage > 0, "Cannot document anything in stage 0");
825 let build_compiler = builder.compiler(stage - 1, builder.host_target);
826 builder.std(build_compiler, target);
827 build_compiler
828}
829
830#[derive(Debug, Clone, Hash, PartialEq, Eq)]
832pub struct Rustc {
833 build_compiler: Compiler,
834 target: TargetSelection,
835 crates: Vec<String>,
836}
837
838impl Rustc {
839 pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
841 let build_compiler = prepare_doc_compiler(builder, target, stage);
842 Self::from_build_compiler(builder, build_compiler, target)
843 }
844
845 fn from_build_compiler(
846 builder: &Builder<'_>,
847 build_compiler: Compiler,
848 target: TargetSelection,
849 ) -> Self {
850 let crates = builder
851 .in_tree_crates("rustc-main", Some(target))
852 .into_iter()
853 .map(|krate| krate.name.to_string())
854 .collect();
855 Self { build_compiler, target, crates }
856 }
857}
858
859impl Step for Rustc {
860 type Output = ();
861 const DEFAULT: bool = true;
862 const IS_HOST: bool = true;
863
864 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
865 let builder = run.builder;
866 run.crate_or_deps("rustc-main")
867 .path("compiler")
868 .default_condition(builder.config.compiler_docs)
869 }
870
871 fn make_run(run: RunConfig<'_>) {
872 run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target));
873 }
874
875 fn run(self, builder: &Builder<'_>) {
882 let target = self.target;
883
884 let out = builder.compiler_doc_out(target);
886 t!(fs::create_dir_all(&out));
887
888 let build_compiler = self.build_compiler;
891 builder.std(build_compiler, builder.config.host_target);
892
893 let _guard = builder.msg(
894 Kind::Doc,
895 format!("compiler{}", crate_description(&self.crates)),
896 Mode::Rustc,
897 build_compiler,
898 target,
899 );
900
901 let mut cargo = builder::Cargo::new(
903 builder,
904 build_compiler,
905 Mode::Rustc,
906 SourceType::InTree,
907 target,
908 Kind::Doc,
909 );
910
911 cargo.rustdocflag("--document-private-items");
912 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
914 cargo.rustdocflag("--enable-index-page");
915 cargo.rustdocflag("-Znormalize-docs");
916 cargo.rustdocflag("--show-type-layout");
917 cargo.rustdocflag("--generate-link-to-definition");
921
922 compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
923 cargo.arg("-Zskip-rustdoc-fingerprint");
924
925 cargo.arg("--no-deps");
928 cargo.arg("-Zrustdoc-map");
929
930 cargo.rustdocflag("--extern-html-root-url");
933 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
934
935 let mut to_open = None;
936
937 let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
938 for krate in &*self.crates {
939 let dir_name = krate.replace('-', "_");
943 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
944 cargo.arg("-p").arg(krate);
945 if to_open.is_none() {
946 to_open = Some(dir_name);
947 }
948 }
949
950 symlink_dir_force(&builder.config, &out, &out_dir);
957 let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
960 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
961
962 cargo.into_cmd().run(builder);
963
964 if !builder.config.dry_run() {
965 for krate in &*self.crates {
967 let dir_name = krate.replace('-', "_");
968 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
970 }
971 }
972
973 if builder.paths.iter().any(|path| path.ends_with("compiler")) {
974 let index = out.join("rustc_middle").join("index.html");
976 builder.open_in_browser(index);
977 } else if let Some(krate) = to_open {
978 let index = out.join(krate).join("index.html");
980 builder.open_in_browser(index);
981 }
982 }
983
984 fn metadata(&self) -> Option<StepMetadata> {
985 Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
986 }
987}
988
989macro_rules! tool_doc {
990 (
991 $tool: ident,
992 $path: literal,
993 mode = $mode:expr
994 $(, is_library = $is_library:expr )?
995 $(, crates = $crates:expr )?
996 $(, allow_features: $allow_features:expr )?
998 ) => {
999 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
1000 pub struct $tool {
1001 build_compiler: Compiler,
1002 mode: Mode,
1003 target: TargetSelection,
1004 }
1005
1006 impl Step for $tool {
1007 type Output = ();
1008 const DEFAULT: bool = true;
1009 const IS_HOST: bool = true;
1010
1011 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1012 let builder = run.builder;
1013 run.path($path).default_condition(builder.config.compiler_docs)
1014 }
1015
1016 fn make_run(run: RunConfig<'_>) {
1017 let target = run.target;
1018 let build_compiler = match $mode {
1019 Mode::ToolRustcPrivate => {
1020 let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
1022
1023 run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
1025 compilers.build_compiler()
1026 }
1027 Mode::ToolBootstrap => {
1028 prepare_doc_compiler(run.builder, run.builder.host_target, 1)
1030 }
1031 Mode::ToolTarget => {
1032 prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1034 }
1035 _ => {
1036 panic!("Unexpected tool mode for documenting: {:?}", $mode);
1037 }
1038 };
1039
1040 run.builder.ensure($tool { build_compiler, mode: $mode, target });
1041 }
1042
1043 fn run(self, builder: &Builder<'_>) {
1047 let mut source_type = SourceType::InTree;
1048
1049 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1050 source_type = SourceType::Submodule;
1051 builder.require_submodule(&submodule_path, None);
1052 }
1053
1054 let $tool { build_compiler, mode, target } = self;
1055
1056 let out = builder.compiler_doc_out(target);
1058 t!(fs::create_dir_all(&out));
1059
1060 let mut cargo = prepare_tool_cargo(
1062 builder,
1063 build_compiler,
1064 mode,
1065 target,
1066 Kind::Doc,
1067 $path,
1068 source_type,
1069 &[],
1070 );
1071 let allow_features = {
1072 let mut _value = "";
1073 $( _value = $allow_features; )?
1074 _value
1075 };
1076
1077 if !allow_features.is_empty() {
1078 cargo.allow_features(allow_features);
1079 }
1080
1081 cargo.arg("-Zskip-rustdoc-fingerprint");
1082 cargo.arg("--no-deps");
1084
1085 if false $(|| $is_library)? {
1086 cargo.arg("--lib");
1087 }
1088
1089 $(for krate in $crates {
1090 cargo.arg("-p").arg(krate);
1091 })?
1092
1093 cargo.rustdocflag("--document-private-items");
1094 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1096 cargo.rustdocflag("--enable-index-page");
1097 cargo.rustdocflag("--show-type-layout");
1098 cargo.rustdocflag("--generate-link-to-definition");
1099
1100 let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1101 $(for krate in $crates {
1102 let dir_name = krate.replace("-", "_");
1103 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1104 })?
1105
1106 symlink_dir_force(&builder.config, &out, &out_dir);
1108 let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1109 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1110
1111 let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1112 cargo.into_cmd().run(builder);
1113
1114 if !builder.config.dry_run() {
1115 $(for krate in $crates {
1117 let dir_name = krate.replace("-", "_");
1118 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1120 })?
1121 }
1122 }
1123
1124 fn metadata(&self) -> Option<StepMetadata> {
1125 Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1126 }
1127 }
1128 }
1129}
1130
1131tool_doc!(
1133 BuildHelper,
1134 "src/build_helper",
1135 mode = Mode::ToolBootstrap,
1136 is_library = true,
1137 crates = ["build_helper"]
1138);
1139tool_doc!(
1140 Rustdoc,
1141 "src/tools/rustdoc",
1142 mode = Mode::ToolRustcPrivate,
1143 crates = ["rustdoc", "rustdoc-json-types"]
1144);
1145tool_doc!(
1146 Rustfmt,
1147 "src/tools/rustfmt",
1148 mode = Mode::ToolRustcPrivate,
1149 crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1150);
1151tool_doc!(
1152 Clippy,
1153 "src/tools/clippy",
1154 mode = Mode::ToolRustcPrivate,
1155 crates = ["clippy_config", "clippy_utils"]
1156);
1157tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1158tool_doc!(
1159 Cargo,
1160 "src/tools/cargo",
1161 mode = Mode::ToolTarget,
1162 crates = [
1163 "cargo",
1164 "cargo-credential",
1165 "cargo-platform",
1166 "cargo-test-macro",
1167 "cargo-test-support",
1168 "cargo-util",
1169 "cargo-util-schemas",
1170 "crates-io",
1171 "mdman",
1172 "rustfix",
1173 ],
1174 allow_features: "specialization"
1177);
1178tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]);
1179tool_doc!(
1180 Bootstrap,
1181 "src/bootstrap",
1182 mode = Mode::ToolBootstrap,
1183 is_library = true,
1184 crates = ["bootstrap"]
1185);
1186tool_doc!(
1187 RunMakeSupport,
1188 "src/tools/run-make-support",
1189 mode = Mode::ToolBootstrap,
1190 is_library = true,
1191 crates = ["run_make_support"]
1192);
1193tool_doc!(
1194 Compiletest,
1195 "src/tools/compiletest",
1196 mode = Mode::ToolBootstrap,
1197 is_library = true,
1198 crates = ["compiletest"]
1199);
1200
1201#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1202pub struct ErrorIndex {
1203 compilers: RustcPrivateCompilers,
1204}
1205
1206impl Step for ErrorIndex {
1207 type Output = ();
1208 const DEFAULT: bool = true;
1209 const IS_HOST: bool = true;
1210
1211 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1212 let builder = run.builder;
1213 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1214 }
1215
1216 fn make_run(run: RunConfig<'_>) {
1217 run.builder.ensure(ErrorIndex {
1218 compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1219 });
1220 }
1221
1222 fn run(self, builder: &Builder<'_>) {
1225 builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1226 let out = builder.doc_out(self.compilers.target());
1227 t!(fs::create_dir_all(&out));
1228 tool::ErrorIndex::command(builder, self.compilers)
1229 .arg("html")
1230 .arg(out)
1231 .arg(&builder.version)
1232 .run(builder);
1233 }
1234
1235 fn metadata(&self) -> Option<StepMetadata> {
1236 Some(
1237 StepMetadata::doc("error-index", self.compilers.target())
1238 .built_by(self.compilers.build_compiler()),
1239 )
1240 }
1241}
1242
1243#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1244pub struct UnstableBookGen {
1245 target: TargetSelection,
1246}
1247
1248impl Step for UnstableBookGen {
1249 type Output = ();
1250 const DEFAULT: bool = true;
1251 const IS_HOST: bool = true;
1252
1253 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1254 let builder = run.builder;
1255 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
1256 }
1257
1258 fn make_run(run: RunConfig<'_>) {
1259 run.builder.ensure(UnstableBookGen { target: run.target });
1260 }
1261
1262 fn run(self, builder: &Builder<'_>) {
1263 let target = self.target;
1264
1265 builder.info(&format!("Generating unstable book md files ({target})"));
1266 let out = builder.md_doc_out(target).join("unstable-book");
1267 builder.create_dir(&out);
1268 builder.remove_dir(&out);
1269 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1270 cmd.arg(builder.src.join("library"));
1271 cmd.arg(builder.src.join("compiler"));
1272 cmd.arg(builder.src.join("src"));
1273 cmd.arg(out);
1274
1275 cmd.run(builder);
1276 }
1277}
1278
1279fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1280 if config.dry_run() {
1281 return;
1282 }
1283 if let Ok(m) = fs::symlink_metadata(link) {
1284 if m.file_type().is_dir() {
1285 t!(fs::remove_dir_all(link));
1286 } else {
1287 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1290 }
1291 }
1292
1293 t!(
1294 symlink_dir(config, original, link),
1295 format!("failed to create link from {} -> {}", link.display(), original.display())
1296 );
1297}
1298
1299#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1301pub struct RustcBook {
1302 build_compiler: Compiler,
1303 target: TargetSelection,
1304 validate: bool,
1307}
1308
1309impl RustcBook {
1310 pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1311 Self { build_compiler, target, validate: true }
1312 }
1313}
1314
1315impl Step for RustcBook {
1316 type Output = ();
1317 const DEFAULT: bool = true;
1318 const IS_HOST: bool = true;
1319
1320 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1321 let builder = run.builder;
1322 run.path("src/doc/rustc").default_condition(builder.config.docs)
1323 }
1324
1325 fn make_run(run: RunConfig<'_>) {
1326 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1330 run.builder.top_stage
1331 } else {
1332 2
1333 };
1334
1335 run.builder.ensure(RustcBook {
1336 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1337 target: run.target,
1338 validate: false,
1339 });
1340 }
1341
1342 fn run(self, builder: &Builder<'_>) {
1348 let out_base = builder.md_doc_out(self.target).join("rustc");
1349 t!(fs::create_dir_all(&out_base));
1350 let out_listing = out_base.join("src/lints");
1351 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1352 builder.info(&format!("Generating lint docs ({})", self.target));
1353
1354 let rustc = builder.rustc(self.build_compiler);
1355 builder.std(self.build_compiler, self.target);
1358 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1359 cmd.arg("--build-rustc-stage");
1360 cmd.arg(self.build_compiler.stage.to_string());
1361 cmd.arg("--src");
1362 cmd.arg(builder.src.join("compiler"));
1363 cmd.arg("--out");
1364 cmd.arg(&out_listing);
1365 cmd.arg("--rustc");
1366 cmd.arg(&rustc);
1367 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1368 if let Some(target_linker) = builder.linker(self.target) {
1369 cmd.arg("--rustc-linker").arg(target_linker);
1370 }
1371 if builder.is_verbose() {
1372 cmd.arg("--verbose");
1373 }
1374 if self.validate {
1375 cmd.arg("--validate");
1376 }
1377 cmd.env("RUSTC_BOOTSTRAP", "1");
1381
1382 builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1386 let doc_generator_guard =
1387 builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1388 cmd.run(builder);
1389 drop(doc_generator_guard);
1390
1391 builder.ensure(RustbookSrc {
1393 target: self.target,
1394 name: "rustc".to_owned(),
1395 src: out_base,
1396 parent: Some(self),
1397 languages: vec![],
1398 build_compiler: None,
1399 });
1400 }
1401}
1402
1403#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1407pub struct Reference {
1408 build_compiler: Compiler,
1409 target: TargetSelection,
1410}
1411
1412impl Step for Reference {
1413 type Output = ();
1414 const DEFAULT: bool = true;
1415
1416 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1417 let builder = run.builder;
1418 run.path("src/doc/reference").default_condition(builder.config.docs)
1419 }
1420
1421 fn make_run(run: RunConfig<'_>) {
1422 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1428 run.builder.top_stage
1429 } else {
1430 2
1431 };
1432
1433 run.builder.ensure(Reference {
1434 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1435 target: run.target,
1436 });
1437 }
1438
1439 fn run(self, builder: &Builder<'_>) {
1441 builder.require_submodule("src/doc/reference", None);
1442
1443 builder.std(self.build_compiler, builder.config.host_target);
1446
1447 builder.ensure(RustbookSrc {
1449 target: self.target,
1450 name: "reference".to_owned(),
1451 src: builder.src.join("src/doc/reference"),
1452 build_compiler: Some(self.build_compiler),
1453 parent: Some(self),
1454 languages: vec![],
1455 });
1456 }
1457}