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 if builder.config.dry_run() {
278 return;
279 }
280
281 for file in t!(fs::read_dir(redirect_path)) {
282 let file = t!(file);
283 let path = file.path();
284 let path = path.to_str().unwrap();
285
286 invoke_rustdoc(builder, build_compiler, &shared_assets, target, path);
287 }
288 }
289}
290
291fn invoke_rustdoc(
292 builder: &Builder<'_>,
293 build_compiler: Compiler,
294 shared_assets: &SharedAssetsPaths,
295 target: TargetSelection,
296 markdown: &str,
297) {
298 let out = builder.doc_out(target);
299
300 let path = builder.src.join("src/doc").join(markdown);
301
302 let header = builder.src.join("src/doc/redirect.inc");
303 let footer = builder.src.join("src/doc/footer.inc");
304
305 let mut cmd = builder.rustdoc_cmd(build_compiler);
306
307 let out = out.join("book");
308
309 cmd.arg("--html-after-content")
310 .arg(&footer)
311 .arg("--html-before-content")
312 .arg(&shared_assets.version_info)
313 .arg("--html-in-header")
314 .arg(&header)
315 .arg("--markdown-no-toc")
316 .arg("--markdown-playground-url")
317 .arg("https://play.rust-lang.org/")
318 .arg("-o")
319 .arg(&out)
320 .arg(&path)
321 .arg("--markdown-css")
322 .arg("../rust.css")
323 .arg("-Zunstable-options");
324
325 if !builder.config.docs_minification {
326 cmd.arg("--disable-minification");
327 }
328
329 cmd.run(builder);
330}
331
332#[derive(Debug, Clone, Hash, PartialEq, Eq)]
333pub struct Standalone {
334 build_compiler: Compiler,
335 target: TargetSelection,
336}
337
338impl Step for Standalone {
339 type Output = ();
340 const DEFAULT: bool = true;
341
342 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
343 let builder = run.builder;
344 run.path("src/doc").alias("standalone").default_condition(builder.config.docs)
345 }
346
347 fn make_run(run: RunConfig<'_>) {
348 run.builder.ensure(Standalone {
349 build_compiler: prepare_doc_compiler(
350 run.builder,
351 run.builder.host_target,
352 run.builder.top_stage,
353 ),
354 target: run.target,
355 });
356 }
357
358 fn run(self, builder: &Builder<'_>) {
367 let target = self.target;
368 let build_compiler = self.build_compiler;
369 let _guard = builder.msg(Kind::Doc, "standalone", None, build_compiler, target);
370 let out = builder.doc_out(target);
371 t!(fs::create_dir_all(&out));
372
373 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
374
375 let favicon = builder.src.join("src/doc/favicon.inc");
376 let footer = builder.src.join("src/doc/footer.inc");
377 let full_toc = builder.src.join("src/doc/full-toc.inc");
378
379 for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
380 let file = t!(file);
381 let path = file.path();
382 let filename = path.file_name().unwrap().to_str().unwrap();
383 if !filename.ends_with(".md") || filename == "README.md" {
384 continue;
385 }
386
387 let html = out.join(filename).with_extension("html");
388 let rustdoc = builder.rustdoc_for_compiler(build_compiler);
389 if up_to_date(&path, &html)
390 && up_to_date(&footer, &html)
391 && up_to_date(&favicon, &html)
392 && up_to_date(&full_toc, &html)
393 && (builder.config.dry_run() || up_to_date(&version_info, &html))
394 && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
395 {
396 continue;
397 }
398
399 let mut cmd = builder.rustdoc_cmd(build_compiler);
400
401 cmd.arg("--html-after-content")
402 .arg(&footer)
403 .arg("--html-before-content")
404 .arg(&version_info)
405 .arg("--html-in-header")
406 .arg(&favicon)
407 .arg("--markdown-no-toc")
408 .arg("-Zunstable-options")
409 .arg("--index-page")
410 .arg(builder.src.join("src/doc/index.md"))
411 .arg("--markdown-playground-url")
412 .arg("https://play.rust-lang.org/")
413 .arg("-o")
414 .arg(&out)
415 .arg(&path);
416
417 if !builder.config.docs_minification {
418 cmd.arg("--disable-minification");
419 }
420
421 if filename == "not_found.md" {
422 cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
423 } else {
424 cmd.arg("--markdown-css").arg("rust.css");
425 }
426 cmd.run(builder);
427 }
428
429 if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
432 let index = out.join("index.html");
433 builder.open_in_browser(index);
434 }
435 }
436
437 fn metadata(&self) -> Option<StepMetadata> {
438 Some(StepMetadata::doc("standalone", self.target).built_by(self.build_compiler))
439 }
440}
441
442#[derive(Debug, Clone, Hash, PartialEq, Eq)]
443pub struct Releases {
444 build_compiler: Compiler,
445 target: TargetSelection,
446}
447
448impl Step for Releases {
449 type Output = ();
450 const DEFAULT: bool = true;
451
452 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
453 let builder = run.builder;
454 run.path("RELEASES.md").alias("releases").default_condition(builder.config.docs)
455 }
456
457 fn make_run(run: RunConfig<'_>) {
458 run.builder.ensure(Releases {
459 build_compiler: prepare_doc_compiler(
460 run.builder,
461 run.builder.host_target,
462 run.builder.top_stage,
463 ),
464 target: run.target,
465 });
466 }
467
468 fn run(self, builder: &Builder<'_>) {
474 let target = self.target;
475 let build_compiler = self.build_compiler;
476 let _guard = builder.msg(Kind::Doc, "releases", None, build_compiler, target);
477 let out = builder.doc_out(target);
478 t!(fs::create_dir_all(&out));
479
480 builder.ensure(Standalone { build_compiler, target });
481
482 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
483
484 let favicon = builder.src.join("src/doc/favicon.inc");
485 let footer = builder.src.join("src/doc/footer.inc");
486 let full_toc = builder.src.join("src/doc/full-toc.inc");
487
488 let html = out.join("releases.html");
489 let tmppath = out.join("releases.md");
490 let inpath = builder.src.join("RELEASES.md");
491 let rustdoc = builder.rustdoc_for_compiler(build_compiler);
492 if !up_to_date(&inpath, &html)
493 || !up_to_date(&footer, &html)
494 || !up_to_date(&favicon, &html)
495 || !up_to_date(&full_toc, &html)
496 || !(builder.config.dry_run()
497 || up_to_date(&version_info, &html)
498 || up_to_date(&rustdoc, &html))
499 {
500 let mut tmpfile = t!(fs::File::create(&tmppath));
501 t!(tmpfile.write_all(b"% Rust Release Notes\n\n"));
502 t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile));
503 mem::drop(tmpfile);
504 let mut cmd = builder.rustdoc_cmd(build_compiler);
505
506 cmd.arg("--html-after-content")
507 .arg(&footer)
508 .arg("--html-before-content")
509 .arg(&version_info)
510 .arg("--html-in-header")
511 .arg(&favicon)
512 .arg("--markdown-no-toc")
513 .arg("--markdown-css")
514 .arg("rust.css")
515 .arg("-Zunstable-options")
516 .arg("--index-page")
517 .arg(builder.src.join("src/doc/index.md"))
518 .arg("--markdown-playground-url")
519 .arg("https://play.rust-lang.org/")
520 .arg("-o")
521 .arg(&out)
522 .arg(&tmppath);
523
524 if !builder.config.docs_minification {
525 cmd.arg("--disable-minification");
526 }
527
528 cmd.run(builder);
529 }
530
531 if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
534 builder.open_in_browser(&html);
535 }
536 }
537
538 fn metadata(&self) -> Option<StepMetadata> {
539 Some(StepMetadata::doc("releases", self.target).built_by(self.build_compiler))
540 }
541}
542
543#[derive(Debug, Clone)]
544pub struct SharedAssetsPaths {
545 pub version_info: PathBuf,
546}
547
548#[derive(Debug, Clone, Hash, PartialEq, Eq)]
549pub struct SharedAssets {
550 target: TargetSelection,
551}
552
553impl Step for SharedAssets {
554 type Output = SharedAssetsPaths;
555 const DEFAULT: bool = false;
556
557 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
558 run.never()
560 }
561
562 fn run(self, builder: &Builder<'_>) -> Self::Output {
564 let out = builder.doc_out(self.target);
565
566 let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
567 let version_info = out.join("version_info.html");
568 if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
569 let info = t!(fs::read_to_string(&version_input))
570 .replace("VERSION", &builder.rust_release())
571 .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
572 .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
573 t!(fs::write(&version_info, info));
574 }
575
576 builder.copy_link(
577 &builder.src.join("src").join("doc").join("rust.css"),
578 &out.join("rust.css"),
579 FileType::Regular,
580 );
581
582 builder.copy_link(
583 &builder
584 .src
585 .join("src")
586 .join("librustdoc")
587 .join("html")
588 .join("static")
589 .join("images")
590 .join("favicon.svg"),
591 &out.join("favicon.svg"),
592 FileType::Regular,
593 );
594 builder.copy_link(
595 &builder
596 .src
597 .join("src")
598 .join("librustdoc")
599 .join("html")
600 .join("static")
601 .join("images")
602 .join("favicon-32x32.png"),
603 &out.join("favicon-32x32.png"),
604 FileType::Regular,
605 );
606
607 SharedAssetsPaths { version_info }
608 }
609}
610
611#[derive(Debug, Clone, Hash, PartialEq, Eq)]
613pub struct Std {
614 build_compiler: Compiler,
615 target: TargetSelection,
616 format: DocumentationFormat,
617 crates: Vec<String>,
618}
619
620impl Std {
621 pub(crate) fn from_build_compiler(
622 build_compiler: Compiler,
623 target: TargetSelection,
624 format: DocumentationFormat,
625 ) -> Self {
626 Std { build_compiler, target, format, crates: vec![] }
627 }
628}
629
630impl Step for Std {
631 type Output = PathBuf;
633
634 const DEFAULT: bool = true;
635
636 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
637 let builder = run.builder;
638 run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
639 }
640
641 fn make_run(run: RunConfig<'_>) {
642 let crates = compile::std_crates_for_run_make(&run);
643 let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
644 if crates.is_empty() && target_is_no_std {
645 return;
646 }
647 run.builder.ensure(Std {
648 build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
649 target: run.target,
650 format: if run.builder.config.cmd.json() {
651 DocumentationFormat::Json
652 } else {
653 DocumentationFormat::Html
654 },
655 crates,
656 });
657 }
658
659 fn run(self, builder: &Builder<'_>) -> Self::Output {
664 let target = self.target;
665 let crates = if self.crates.is_empty() {
666 builder
667 .in_tree_crates("sysroot", Some(target))
668 .iter()
669 .map(|c| c.name.to_string())
670 .collect()
671 } else {
672 self.crates
673 };
674
675 let out = match self.format {
676 DocumentationFormat::Html => builder.doc_out(target),
677 DocumentationFormat::Json => builder.json_doc_out(target),
678 };
679
680 t!(fs::create_dir_all(&out));
681
682 if self.format == DocumentationFormat::Html {
683 builder.ensure(SharedAssets { target: self.target });
684 }
685
686 let index_page = builder
687 .src
688 .join("src/doc/index.md")
689 .into_os_string()
690 .into_string()
691 .expect("non-utf8 paths are unsupported");
692 let mut extra_args = match self.format {
693 DocumentationFormat::Html => {
694 vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
695 }
696 DocumentationFormat::Json => vec!["--output-format", "json"],
697 };
698
699 if !builder.config.docs_minification {
700 extra_args.push("--disable-minification");
701 }
702 extra_args.push("-Zunstable-options");
704
705 doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates);
706
707 if let DocumentationFormat::Html = self.format {
709 if builder.paths.iter().any(|path| path.ends_with("library")) {
710 let index = out.join("std").join("index.html");
712 builder.open_in_browser(index);
713 } else {
714 for requested_crate in crates {
715 if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
716 let index = out.join(requested_crate).join("index.html");
717 builder.open_in_browser(index);
718 break;
719 }
720 }
721 }
722 }
723
724 out
725 }
726
727 fn metadata(&self) -> Option<StepMetadata> {
728 Some(
729 StepMetadata::doc("std", self.target)
730 .built_by(self.build_compiler)
731 .with_metadata(format!("crates=[{}]", self.crates.join(","))),
732 )
733 }
734}
735
736const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
746
747#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
748pub enum DocumentationFormat {
749 Html,
750 Json,
751}
752
753impl DocumentationFormat {
754 fn as_str(&self) -> &str {
755 match self {
756 DocumentationFormat::Html => "HTML",
757 DocumentationFormat::Json => "JSON",
758 }
759 }
760}
761
762fn doc_std(
764 builder: &Builder<'_>,
765 format: DocumentationFormat,
766 build_compiler: Compiler,
767 target: TargetSelection,
768 out: &Path,
769 extra_args: &[&str],
770 requested_crates: &[String],
771) {
772 let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
773 let target_dir =
774 builder.stage_out(build_compiler, Mode::Std).join(target).join(target_doc_dir_name);
775
776 let out_dir = target_dir.join(target).join("doc");
780
781 let mut cargo = builder::Cargo::new(
782 builder,
783 build_compiler,
784 Mode::Std,
785 SourceType::InTree,
786 target,
787 Kind::Doc,
788 );
789
790 compile::std_cargo(builder, target, &mut cargo, requested_crates);
791 cargo
792 .arg("--no-deps")
793 .arg("--target-dir")
794 .arg(&*target_dir.to_string_lossy())
795 .arg("-Zskip-rustdoc-fingerprint")
796 .arg("-Zrustdoc-map")
797 .rustdocflag("--extern-html-root-url")
798 .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
799 .rustdocflag("--extern-html-root-takes-precedence")
800 .rustdocflag("--resource-suffix")
801 .rustdocflag(&builder.version);
802 for arg in extra_args {
803 cargo.rustdocflag(arg);
804 }
805
806 if builder.config.library_docs_private_items {
807 cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
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::ToolTarget => {
1028 prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1031 }
1032 _ => {
1033 panic!("Unexpected tool mode for documenting: {:?}", $mode);
1034 }
1035 };
1036
1037 run.builder.ensure($tool { build_compiler, mode: $mode, target });
1038 }
1039
1040 fn run(self, builder: &Builder<'_>) {
1044 let mut source_type = SourceType::InTree;
1045
1046 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1047 source_type = SourceType::Submodule;
1048 builder.require_submodule(&submodule_path, None);
1049 }
1050
1051 let $tool { build_compiler, mode, target } = self;
1052
1053 let out = builder.compiler_doc_out(target);
1055 t!(fs::create_dir_all(&out));
1056
1057 let mut cargo = prepare_tool_cargo(
1059 builder,
1060 build_compiler,
1061 mode,
1062 target,
1063 Kind::Doc,
1064 $path,
1065 source_type,
1066 &[],
1067 );
1068 let allow_features = {
1069 let mut _value = "";
1070 $( _value = $allow_features; )?
1071 _value
1072 };
1073
1074 if !allow_features.is_empty() {
1075 cargo.allow_features(allow_features);
1076 }
1077
1078 cargo.arg("-Zskip-rustdoc-fingerprint");
1079 cargo.arg("--no-deps");
1081
1082 if false $(|| $is_library)? {
1083 cargo.arg("--lib");
1084 }
1085
1086 $(for krate in $crates {
1087 cargo.arg("-p").arg(krate);
1088 })?
1089
1090 cargo.rustdocflag("--document-private-items");
1091 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1093 cargo.rustdocflag("--enable-index-page");
1094 cargo.rustdocflag("--show-type-layout");
1095 cargo.rustdocflag("--generate-link-to-definition");
1096
1097 let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1098 $(for krate in $crates {
1099 let dir_name = krate.replace("-", "_");
1100 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1101 })?
1102
1103 symlink_dir_force(&builder.config, &out, &out_dir);
1105 let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1106 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1107
1108 let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1109 cargo.into_cmd().run(builder);
1110
1111 if !builder.config.dry_run() {
1112 $(for krate in $crates {
1114 let dir_name = krate.replace("-", "_");
1115 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1117 })?
1118 }
1119 }
1120
1121 fn metadata(&self) -> Option<StepMetadata> {
1122 Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1123 }
1124 }
1125 }
1126}
1127
1128tool_doc!(
1130 BuildHelper,
1131 "src/build_helper",
1132 mode = Mode::ToolTarget,
1137 is_library = true,
1138 crates = ["build_helper"]
1139);
1140tool_doc!(
1141 Rustdoc,
1142 "src/tools/rustdoc",
1143 mode = Mode::ToolRustcPrivate,
1144 crates = ["rustdoc", "rustdoc-json-types"]
1145);
1146tool_doc!(
1147 Rustfmt,
1148 "src/tools/rustfmt",
1149 mode = Mode::ToolRustcPrivate,
1150 crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1151);
1152tool_doc!(
1153 Clippy,
1154 "src/tools/clippy",
1155 mode = Mode::ToolRustcPrivate,
1156 crates = ["clippy_config", "clippy_utils"]
1157);
1158tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1159tool_doc!(
1160 Cargo,
1161 "src/tools/cargo",
1162 mode = Mode::ToolTarget,
1163 crates = [
1164 "cargo",
1165 "cargo-credential",
1166 "cargo-platform",
1167 "cargo-test-macro",
1168 "cargo-test-support",
1169 "cargo-util",
1170 "cargo-util-schemas",
1171 "crates-io",
1172 "mdman",
1173 "rustfix",
1174 ],
1175 allow_features: "specialization"
1178);
1179tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
1180tool_doc!(
1181 Bootstrap,
1182 "src/bootstrap",
1183 mode = Mode::ToolTarget,
1184 is_library = true,
1185 crates = ["bootstrap"]
1186);
1187tool_doc!(
1188 RunMakeSupport,
1189 "src/tools/run-make-support",
1190 mode = Mode::ToolTarget,
1191 is_library = true,
1192 crates = ["run_make_support"]
1193);
1194tool_doc!(
1195 Compiletest,
1196 "src/tools/compiletest",
1197 mode = Mode::ToolTarget,
1198 is_library = true,
1199 crates = ["compiletest"]
1200);
1201
1202#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1203pub struct ErrorIndex {
1204 compilers: RustcPrivateCompilers,
1205}
1206
1207impl Step for ErrorIndex {
1208 type Output = ();
1209 const DEFAULT: bool = true;
1210 const IS_HOST: bool = true;
1211
1212 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1213 let builder = run.builder;
1214 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1215 }
1216
1217 fn make_run(run: RunConfig<'_>) {
1218 run.builder.ensure(ErrorIndex {
1219 compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1220 });
1221 }
1222
1223 fn run(self, builder: &Builder<'_>) {
1226 builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1227 let out = builder.doc_out(self.compilers.target());
1228 t!(fs::create_dir_all(&out));
1229 tool::ErrorIndex::command(builder, self.compilers)
1230 .arg("html")
1231 .arg(out)
1232 .arg(&builder.version)
1233 .run(builder);
1234 }
1235
1236 fn metadata(&self) -> Option<StepMetadata> {
1237 Some(
1238 StepMetadata::doc("error-index", self.compilers.target())
1239 .built_by(self.compilers.build_compiler()),
1240 )
1241 }
1242}
1243
1244#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1245pub struct UnstableBookGen {
1246 target: TargetSelection,
1247}
1248
1249impl Step for UnstableBookGen {
1250 type Output = ();
1251 const DEFAULT: bool = true;
1252 const IS_HOST: bool = true;
1253
1254 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1255 let builder = run.builder;
1256 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
1257 }
1258
1259 fn make_run(run: RunConfig<'_>) {
1260 run.builder.ensure(UnstableBookGen { target: run.target });
1261 }
1262
1263 fn run(self, builder: &Builder<'_>) {
1264 let target = self.target;
1265
1266 builder.info(&format!("Generating unstable book md files ({target})"));
1267 let out = builder.md_doc_out(target).join("unstable-book");
1268 builder.create_dir(&out);
1269 builder.remove_dir(&out);
1270 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1271 cmd.arg(builder.src.join("library"));
1272 cmd.arg(builder.src.join("compiler"));
1273 cmd.arg(builder.src.join("src"));
1274 cmd.arg(out);
1275
1276 cmd.run(builder);
1277 }
1278}
1279
1280fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1281 if config.dry_run() {
1282 return;
1283 }
1284 if let Ok(m) = fs::symlink_metadata(link) {
1285 if m.file_type().is_dir() {
1286 t!(fs::remove_dir_all(link));
1287 } else {
1288 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1291 }
1292 }
1293
1294 t!(
1295 symlink_dir(config, original, link),
1296 format!("failed to create link from {} -> {}", link.display(), original.display())
1297 );
1298}
1299
1300#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1302pub struct RustcBook {
1303 build_compiler: Compiler,
1304 target: TargetSelection,
1305 validate: bool,
1308}
1309
1310impl RustcBook {
1311 pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1312 Self { build_compiler, target, validate: true }
1313 }
1314}
1315
1316impl Step for RustcBook {
1317 type Output = ();
1318 const DEFAULT: bool = true;
1319 const IS_HOST: bool = true;
1320
1321 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1322 let builder = run.builder;
1323 run.path("src/doc/rustc").default_condition(builder.config.docs)
1324 }
1325
1326 fn make_run(run: RunConfig<'_>) {
1327 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1331 run.builder.top_stage
1332 } else {
1333 2
1334 };
1335
1336 run.builder.ensure(RustcBook {
1337 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1338 target: run.target,
1339 validate: false,
1340 });
1341 }
1342
1343 fn run(self, builder: &Builder<'_>) {
1349 let out_base = builder.md_doc_out(self.target).join("rustc");
1350 t!(fs::create_dir_all(&out_base));
1351 let out_listing = out_base.join("src/lints");
1352 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1353 builder.info(&format!("Generating lint docs ({})", self.target));
1354
1355 let rustc = builder.rustc(self.build_compiler);
1356 builder.std(self.build_compiler, self.target);
1359 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1360 cmd.arg("--build-rustc-stage");
1361 cmd.arg(self.build_compiler.stage.to_string());
1362 cmd.arg("--src");
1363 cmd.arg(builder.src.join("compiler"));
1364 cmd.arg("--out");
1365 cmd.arg(&out_listing);
1366 cmd.arg("--rustc");
1367 cmd.arg(&rustc);
1368 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1369 if let Some(target_linker) = builder.linker(self.target) {
1370 cmd.arg("--rustc-linker").arg(target_linker);
1371 }
1372 if builder.is_verbose() {
1373 cmd.arg("--verbose");
1374 }
1375 if self.validate {
1376 cmd.arg("--validate");
1377 }
1378 cmd.env("RUSTC_BOOTSTRAP", "1");
1382
1383 builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1387 let doc_generator_guard =
1388 builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1389 cmd.run(builder);
1390 drop(doc_generator_guard);
1391
1392 builder.ensure(RustbookSrc {
1394 target: self.target,
1395 name: "rustc".to_owned(),
1396 src: out_base,
1397 parent: Some(self),
1398 languages: vec![],
1399 build_compiler: None,
1400 });
1401 }
1402}
1403
1404#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1408pub struct Reference {
1409 build_compiler: Compiler,
1410 target: TargetSelection,
1411}
1412
1413impl Step for Reference {
1414 type Output = ();
1415 const DEFAULT: bool = true;
1416
1417 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1418 let builder = run.builder;
1419 run.path("src/doc/reference").default_condition(builder.config.docs)
1420 }
1421
1422 fn make_run(run: RunConfig<'_>) {
1423 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1429 run.builder.top_stage
1430 } else {
1431 2
1432 };
1433
1434 run.builder.ensure(Reference {
1435 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1436 target: run.target,
1437 });
1438 }
1439
1440 fn run(self, builder: &Builder<'_>) {
1442 builder.require_submodule("src/doc/reference", None);
1443
1444 builder.std(self.build_compiler, builder.config.host_target);
1447
1448 builder.ensure(RustbookSrc {
1450 target: self.target,
1451 name: "reference".to_owned(),
1452 src: builder.src.join("src/doc/reference"),
1453 build_compiler: Some(self.build_compiler),
1454 parent: Some(self),
1455 languages: vec![],
1456 });
1457 }
1458}