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 SharedAssetsPaths { version_info }
579 }
580}
581
582#[derive(Debug, Clone, Hash, PartialEq, Eq)]
584pub struct Std {
585 build_compiler: Compiler,
586 target: TargetSelection,
587 format: DocumentationFormat,
588 crates: Vec<String>,
589}
590
591impl Std {
592 pub(crate) fn from_build_compiler(
593 build_compiler: Compiler,
594 target: TargetSelection,
595 format: DocumentationFormat,
596 ) -> Self {
597 Std { build_compiler, target, format, crates: vec![] }
598 }
599}
600
601impl Step for Std {
602 type Output = PathBuf;
604
605 const DEFAULT: bool = true;
606
607 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
608 let builder = run.builder;
609 run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
610 }
611
612 fn make_run(run: RunConfig<'_>) {
613 let crates = compile::std_crates_for_run_make(&run);
614 let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
615 if crates.is_empty() && target_is_no_std {
616 return;
617 }
618 run.builder.ensure(Std {
619 build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
620 target: run.target,
621 format: if run.builder.config.cmd.json() {
622 DocumentationFormat::Json
623 } else {
624 DocumentationFormat::Html
625 },
626 crates,
627 });
628 }
629
630 fn run(self, builder: &Builder<'_>) -> Self::Output {
635 let target = self.target;
636 let crates = if self.crates.is_empty() {
637 builder
638 .in_tree_crates("sysroot", Some(target))
639 .iter()
640 .map(|c| c.name.to_string())
641 .collect()
642 } else {
643 self.crates
644 };
645
646 let out = match self.format {
647 DocumentationFormat::Html => builder.doc_out(target),
648 DocumentationFormat::Json => builder.json_doc_out(target),
649 };
650
651 t!(fs::create_dir_all(&out));
652
653 if self.format == DocumentationFormat::Html {
654 builder.ensure(SharedAssets { target: self.target });
655 }
656
657 let index_page = builder
658 .src
659 .join("src/doc/index.md")
660 .into_os_string()
661 .into_string()
662 .expect("non-utf8 paths are unsupported");
663 let mut extra_args = match self.format {
664 DocumentationFormat::Html => {
665 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
666 }
667 DocumentationFormat::Json => vec!["--output-format", "json"],
668 };
669
670 if !builder.config.docs_minification {
671 extra_args.push("--disable-minification");
672 }
673 extra_args.push("-Zunstable-options");
675
676 doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates);
677
678 if let DocumentationFormat::Html = self.format {
680 if builder.paths.iter().any(|path| path.ends_with("library")) {
681 let index = out.join("std").join("index.html");
683 builder.open_in_browser(index);
684 } else {
685 for requested_crate in crates {
686 if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
687 let index = out.join(requested_crate).join("index.html");
688 builder.open_in_browser(index);
689 break;
690 }
691 }
692 }
693 }
694
695 out
696 }
697
698 fn metadata(&self) -> Option<StepMetadata> {
699 Some(
700 StepMetadata::doc("std", self.target)
701 .built_by(self.build_compiler)
702 .with_metadata(format!("crates=[{}]", self.crates.join(","))),
703 )
704 }
705}
706
707const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
717
718#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
719pub enum DocumentationFormat {
720 Html,
721 Json,
722}
723
724impl DocumentationFormat {
725 fn as_str(&self) -> &str {
726 match self {
727 DocumentationFormat::Html => "HTML",
728 DocumentationFormat::Json => "JSON",
729 }
730 }
731}
732
733fn doc_std(
735 builder: &Builder<'_>,
736 format: DocumentationFormat,
737 build_compiler: Compiler,
738 target: TargetSelection,
739 out: &Path,
740 extra_args: &[&str],
741 requested_crates: &[String],
742) {
743 let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
744 let target_dir =
745 builder.stage_out(build_compiler, Mode::Std).join(target).join(target_doc_dir_name);
746
747 let out_dir = target_dir.join(target).join("doc");
751
752 let mut cargo = builder::Cargo::new(
753 builder,
754 build_compiler,
755 Mode::Std,
756 SourceType::InTree,
757 target,
758 Kind::Doc,
759 );
760
761 compile::std_cargo(builder, target, &mut cargo);
762 cargo
763 .arg("--no-deps")
764 .arg("--target-dir")
765 .arg(&*target_dir.to_string_lossy())
766 .arg("-Zskip-rustdoc-fingerprint")
767 .arg("-Zrustdoc-map")
768 .rustdocflag("--extern-html-root-url")
769 .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
770 .rustdocflag("--extern-html-root-takes-precedence")
771 .rustdocflag("--resource-suffix")
772 .rustdocflag(&builder.version);
773 for arg in extra_args {
774 cargo.rustdocflag(arg);
775 }
776
777 if builder.config.library_docs_private_items {
778 cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
779 }
780
781 for krate in requested_crates {
782 cargo.arg("-p").arg(krate);
783 }
784
785 let description =
786 format!("library{} in {} format", crate_description(requested_crates), format.as_str());
787 let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target);
788
789 cargo.into_cmd().run(builder);
790 builder.cp_link_r(&out_dir, out);
791}
792
793pub fn prepare_doc_compiler(
795 builder: &Builder<'_>,
796 target: TargetSelection,
797 stage: u32,
798) -> Compiler {
799 assert!(stage > 0, "Cannot document anything in stage 0");
800 let build_compiler = builder.compiler(stage - 1, builder.host_target);
801 builder.std(build_compiler, target);
802 build_compiler
803}
804
805#[derive(Debug, Clone, Hash, PartialEq, Eq)]
807pub struct Rustc {
808 build_compiler: Compiler,
809 target: TargetSelection,
810 crates: Vec<String>,
811}
812
813impl Rustc {
814 pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
816 let build_compiler = prepare_doc_compiler(builder, target, stage);
817 Self::from_build_compiler(builder, build_compiler, target)
818 }
819
820 fn from_build_compiler(
821 builder: &Builder<'_>,
822 build_compiler: Compiler,
823 target: TargetSelection,
824 ) -> Self {
825 let crates = builder
826 .in_tree_crates("rustc-main", Some(target))
827 .into_iter()
828 .map(|krate| krate.name.to_string())
829 .collect();
830 Self { build_compiler, target, crates }
831 }
832}
833
834impl Step for Rustc {
835 type Output = ();
836 const DEFAULT: bool = true;
837 const IS_HOST: bool = true;
838
839 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
840 let builder = run.builder;
841 run.crate_or_deps("rustc-main")
842 .path("compiler")
843 .default_condition(builder.config.compiler_docs)
844 }
845
846 fn make_run(run: RunConfig<'_>) {
847 run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target));
848 }
849
850 fn run(self, builder: &Builder<'_>) {
857 let target = self.target;
858
859 let out = builder.compiler_doc_out(target);
861 t!(fs::create_dir_all(&out));
862
863 let build_compiler = self.build_compiler;
866 builder.std(build_compiler, builder.config.host_target);
867
868 let _guard = builder.msg(
869 Kind::Doc,
870 format!("compiler{}", crate_description(&self.crates)),
871 Mode::Rustc,
872 build_compiler,
873 target,
874 );
875
876 let mut cargo = builder::Cargo::new(
878 builder,
879 build_compiler,
880 Mode::Rustc,
881 SourceType::InTree,
882 target,
883 Kind::Doc,
884 );
885
886 cargo.rustdocflag("--document-private-items");
887 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
889 cargo.rustdocflag("--enable-index-page");
890 cargo.rustdocflag("-Znormalize-docs");
891 cargo.rustdocflag("--show-type-layout");
892 cargo.rustdocflag("--generate-link-to-definition");
896
897 compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
898 cargo.arg("-Zskip-rustdoc-fingerprint");
899
900 cargo.arg("--no-deps");
903 cargo.arg("-Zrustdoc-map");
904
905 cargo.rustdocflag("--extern-html-root-url");
908 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
909
910 let mut to_open = None;
911
912 let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
913 for krate in &*self.crates {
914 let dir_name = krate.replace('-', "_");
918 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
919 cargo.arg("-p").arg(krate);
920 if to_open.is_none() {
921 to_open = Some(dir_name);
922 }
923 }
924
925 symlink_dir_force(&builder.config, &out, &out_dir);
932 let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
935 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
936
937 cargo.into_cmd().run(builder);
938
939 if !builder.config.dry_run() {
940 for krate in &*self.crates {
942 let dir_name = krate.replace('-', "_");
943 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
945 }
946 }
947
948 if builder.paths.iter().any(|path| path.ends_with("compiler")) {
949 let index = out.join("rustc_middle").join("index.html");
951 builder.open_in_browser(index);
952 } else if let Some(krate) = to_open {
953 let index = out.join(krate).join("index.html");
955 builder.open_in_browser(index);
956 }
957 }
958
959 fn metadata(&self) -> Option<StepMetadata> {
960 Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
961 }
962}
963
964macro_rules! tool_doc {
965 (
966 $tool: ident,
967 $path: literal,
968 mode = $mode:expr
969 $(, is_library = $is_library:expr )?
970 $(, crates = $crates:expr )?
971 $(, allow_features: $allow_features:expr )?
973 ) => {
974 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
975 pub struct $tool {
976 build_compiler: Compiler,
977 mode: Mode,
978 target: TargetSelection,
979 }
980
981 impl Step for $tool {
982 type Output = ();
983 const DEFAULT: bool = true;
984 const IS_HOST: bool = true;
985
986 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
987 let builder = run.builder;
988 run.path($path).default_condition(builder.config.compiler_docs)
989 }
990
991 fn make_run(run: RunConfig<'_>) {
992 let target = run.target;
993 let build_compiler = match $mode {
994 Mode::ToolRustcPrivate => {
995 let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
997
998 run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
1000 compilers.build_compiler()
1001 }
1002 Mode::ToolBootstrap => {
1003 prepare_doc_compiler(run.builder, run.builder.host_target, 1)
1005 }
1006 Mode::ToolTarget => {
1007 prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1009 }
1010 _ => {
1011 panic!("Unexpected tool mode for documenting: {:?}", $mode);
1012 }
1013 };
1014
1015 run.builder.ensure($tool { build_compiler, mode: $mode, target });
1016 }
1017
1018 fn run(self, builder: &Builder<'_>) {
1022 let mut source_type = SourceType::InTree;
1023
1024 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1025 source_type = SourceType::Submodule;
1026 builder.require_submodule(&submodule_path, None);
1027 }
1028
1029 let $tool { build_compiler, mode, target } = self;
1030
1031 let out = builder.compiler_doc_out(target);
1033 t!(fs::create_dir_all(&out));
1034
1035 let mut cargo = prepare_tool_cargo(
1037 builder,
1038 build_compiler,
1039 mode,
1040 target,
1041 Kind::Doc,
1042 $path,
1043 source_type,
1044 &[],
1045 );
1046 let allow_features = {
1047 let mut _value = "";
1048 $( _value = $allow_features; )?
1049 _value
1050 };
1051
1052 if !allow_features.is_empty() {
1053 cargo.allow_features(allow_features);
1054 }
1055
1056 cargo.arg("-Zskip-rustdoc-fingerprint");
1057 cargo.arg("--no-deps");
1059
1060 if false $(|| $is_library)? {
1061 cargo.arg("--lib");
1062 }
1063
1064 $(for krate in $crates {
1065 cargo.arg("-p").arg(krate);
1066 })?
1067
1068 cargo.rustdocflag("--document-private-items");
1069 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1071 cargo.rustdocflag("--enable-index-page");
1072 cargo.rustdocflag("--show-type-layout");
1073 cargo.rustdocflag("--generate-link-to-definition");
1074
1075 let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1076 $(for krate in $crates {
1077 let dir_name = krate.replace("-", "_");
1078 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1079 })?
1080
1081 symlink_dir_force(&builder.config, &out, &out_dir);
1083 let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1084 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1085
1086 let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1087 cargo.into_cmd().run(builder);
1088
1089 if !builder.config.dry_run() {
1090 $(for krate in $crates {
1092 let dir_name = krate.replace("-", "_");
1093 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1095 })?
1096 }
1097 }
1098
1099 fn metadata(&self) -> Option<StepMetadata> {
1100 Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1101 }
1102 }
1103 }
1104}
1105
1106tool_doc!(
1108 BuildHelper,
1109 "src/build_helper",
1110 mode = Mode::ToolBootstrap,
1111 is_library = true,
1112 crates = ["build_helper"]
1113);
1114tool_doc!(
1115 Rustdoc,
1116 "src/tools/rustdoc",
1117 mode = Mode::ToolRustcPrivate,
1118 crates = ["rustdoc", "rustdoc-json-types"]
1119);
1120tool_doc!(
1121 Rustfmt,
1122 "src/tools/rustfmt",
1123 mode = Mode::ToolRustcPrivate,
1124 crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1125);
1126tool_doc!(
1127 Clippy,
1128 "src/tools/clippy",
1129 mode = Mode::ToolRustcPrivate,
1130 crates = ["clippy_config", "clippy_utils"]
1131);
1132tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1133tool_doc!(
1134 Cargo,
1135 "src/tools/cargo",
1136 mode = Mode::ToolTarget,
1137 crates = [
1138 "cargo",
1139 "cargo-credential",
1140 "cargo-platform",
1141 "cargo-test-macro",
1142 "cargo-test-support",
1143 "cargo-util",
1144 "cargo-util-schemas",
1145 "crates-io",
1146 "mdman",
1147 "rustfix",
1148 ],
1149 allow_features: "specialization"
1152);
1153tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolBootstrap, crates = ["tidy"]);
1154tool_doc!(
1155 Bootstrap,
1156 "src/bootstrap",
1157 mode = Mode::ToolBootstrap,
1158 is_library = true,
1159 crates = ["bootstrap"]
1160);
1161tool_doc!(
1162 RunMakeSupport,
1163 "src/tools/run-make-support",
1164 mode = Mode::ToolBootstrap,
1165 is_library = true,
1166 crates = ["run_make_support"]
1167);
1168tool_doc!(
1169 Compiletest,
1170 "src/tools/compiletest",
1171 mode = Mode::ToolBootstrap,
1172 is_library = true,
1173 crates = ["compiletest"]
1174);
1175
1176#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1177pub struct ErrorIndex {
1178 compilers: RustcPrivateCompilers,
1179}
1180
1181impl Step for ErrorIndex {
1182 type Output = ();
1183 const DEFAULT: bool = true;
1184 const IS_HOST: bool = true;
1185
1186 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1187 let builder = run.builder;
1188 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1189 }
1190
1191 fn make_run(run: RunConfig<'_>) {
1192 run.builder.ensure(ErrorIndex {
1193 compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1194 });
1195 }
1196
1197 fn run(self, builder: &Builder<'_>) {
1200 builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1201 let out = builder.doc_out(self.compilers.target());
1202 t!(fs::create_dir_all(&out));
1203 tool::ErrorIndex::command(builder, self.compilers)
1204 .arg("html")
1205 .arg(out)
1206 .arg(&builder.version)
1207 .run(builder);
1208 }
1209
1210 fn metadata(&self) -> Option<StepMetadata> {
1211 Some(
1212 StepMetadata::doc("error-index", self.compilers.target())
1213 .built_by(self.compilers.build_compiler()),
1214 )
1215 }
1216}
1217
1218#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1219pub struct UnstableBookGen {
1220 target: TargetSelection,
1221}
1222
1223impl Step for UnstableBookGen {
1224 type Output = ();
1225 const DEFAULT: bool = true;
1226 const IS_HOST: bool = true;
1227
1228 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1229 let builder = run.builder;
1230 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
1231 }
1232
1233 fn make_run(run: RunConfig<'_>) {
1234 run.builder.ensure(UnstableBookGen { target: run.target });
1235 }
1236
1237 fn run(self, builder: &Builder<'_>) {
1238 let target = self.target;
1239
1240 builder.info(&format!("Generating unstable book md files ({target})"));
1241 let out = builder.md_doc_out(target).join("unstable-book");
1242 builder.create_dir(&out);
1243 builder.remove_dir(&out);
1244 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1245 cmd.arg(builder.src.join("library"));
1246 cmd.arg(builder.src.join("compiler"));
1247 cmd.arg(builder.src.join("src"));
1248 cmd.arg(out);
1249
1250 cmd.run(builder);
1251 }
1252}
1253
1254fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1255 if config.dry_run() {
1256 return;
1257 }
1258 if let Ok(m) = fs::symlink_metadata(link) {
1259 if m.file_type().is_dir() {
1260 t!(fs::remove_dir_all(link));
1261 } else {
1262 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1265 }
1266 }
1267
1268 t!(
1269 symlink_dir(config, original, link),
1270 format!("failed to create link from {} -> {}", link.display(), original.display())
1271 );
1272}
1273
1274#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1276pub struct RustcBook {
1277 build_compiler: Compiler,
1278 target: TargetSelection,
1279 validate: bool,
1282}
1283
1284impl RustcBook {
1285 pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1286 Self { build_compiler, target, validate: true }
1287 }
1288}
1289
1290impl Step for RustcBook {
1291 type Output = ();
1292 const DEFAULT: bool = true;
1293 const IS_HOST: bool = true;
1294
1295 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1296 let builder = run.builder;
1297 run.path("src/doc/rustc").default_condition(builder.config.docs)
1298 }
1299
1300 fn make_run(run: RunConfig<'_>) {
1301 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1305 run.builder.top_stage
1306 } else {
1307 2
1308 };
1309
1310 run.builder.ensure(RustcBook {
1311 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1312 target: run.target,
1313 validate: false,
1314 });
1315 }
1316
1317 fn run(self, builder: &Builder<'_>) {
1323 let out_base = builder.md_doc_out(self.target).join("rustc");
1324 t!(fs::create_dir_all(&out_base));
1325 let out_listing = out_base.join("src/lints");
1326 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1327 builder.info(&format!("Generating lint docs ({})", self.target));
1328
1329 let rustc = builder.rustc(self.build_compiler);
1330 builder.std(self.build_compiler, self.target);
1333 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1334 cmd.arg("--build-rustc-stage");
1335 cmd.arg(self.build_compiler.stage.to_string());
1336 cmd.arg("--src");
1337 cmd.arg(builder.src.join("compiler"));
1338 cmd.arg("--out");
1339 cmd.arg(&out_listing);
1340 cmd.arg("--rustc");
1341 cmd.arg(&rustc);
1342 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1343 if let Some(target_linker) = builder.linker(self.target) {
1344 cmd.arg("--rustc-linker").arg(target_linker);
1345 }
1346 if builder.is_verbose() {
1347 cmd.arg("--verbose");
1348 }
1349 if self.validate {
1350 cmd.arg("--validate");
1351 }
1352 cmd.env("RUSTC_BOOTSTRAP", "1");
1356
1357 builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1361 let doc_generator_guard =
1362 builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1363 cmd.run(builder);
1364 drop(doc_generator_guard);
1365
1366 builder.ensure(RustbookSrc {
1368 target: self.target,
1369 name: "rustc".to_owned(),
1370 src: out_base,
1371 parent: Some(self),
1372 languages: vec![],
1373 build_compiler: None,
1374 });
1375 }
1376}
1377
1378#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1382pub struct Reference {
1383 build_compiler: Compiler,
1384 target: TargetSelection,
1385}
1386
1387impl Step for Reference {
1388 type Output = ();
1389 const DEFAULT: bool = true;
1390
1391 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1392 let builder = run.builder;
1393 run.path("src/doc/reference").default_condition(builder.config.docs)
1394 }
1395
1396 fn make_run(run: RunConfig<'_>) {
1397 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1403 run.builder.top_stage
1404 } else {
1405 2
1406 };
1407
1408 run.builder.ensure(Reference {
1409 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1410 target: run.target,
1411 });
1412 }
1413
1414 fn run(self, builder: &Builder<'_>) {
1416 builder.require_submodule("src/doc/reference", None);
1417
1418 builder.std(self.build_compiler, builder.config.host_target);
1421
1422 builder.ensure(RustbookSrc {
1424 target: self.target,
1425 name: "reference".to_owned(),
1426 src: builder.src.join("src/doc/reference"),
1427 build_compiler: Some(self.build_compiler),
1428 parent: Some(self),
1429 languages: vec![],
1430 });
1431 }
1432}