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