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(run.builder.top_stage, run.builder.host_target),
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, None, build_compiler, target);
788
789 cargo.into_cmd().run(builder);
790 builder.cp_link_r(&out_dir, out);
791}
792
793fn prepare_doc_compiler(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Compiler {
795 assert!(stage > 0, "Cannot document anything in stage 0");
796 let build_compiler = builder.compiler(stage - 1, builder.host_target);
797 builder.std(build_compiler, target);
798 build_compiler
799}
800
801#[derive(Debug, Clone, Hash, PartialEq, Eq)]
803pub struct Rustc {
804 build_compiler: Compiler,
805 target: TargetSelection,
806 crates: Vec<String>,
807}
808
809impl Rustc {
810 pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
812 let build_compiler = prepare_doc_compiler(builder, target, stage);
813 Self::from_build_compiler(builder, build_compiler, target)
814 }
815
816 fn from_build_compiler(
817 builder: &Builder<'_>,
818 build_compiler: Compiler,
819 target: TargetSelection,
820 ) -> Self {
821 let crates = builder
822 .in_tree_crates("rustc-main", Some(target))
823 .into_iter()
824 .map(|krate| krate.name.to_string())
825 .collect();
826 Self { build_compiler, target, crates }
827 }
828}
829
830impl Step for Rustc {
831 type Output = ();
832 const DEFAULT: bool = true;
833 const IS_HOST: bool = true;
834
835 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
836 let builder = run.builder;
837 run.crate_or_deps("rustc-main")
838 .path("compiler")
839 .default_condition(builder.config.compiler_docs)
840 }
841
842 fn make_run(run: RunConfig<'_>) {
843 run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target));
844 }
845
846 fn run(self, builder: &Builder<'_>) {
853 let target = self.target;
854
855 let out = builder.compiler_doc_out(target);
857 t!(fs::create_dir_all(&out));
858
859 let build_compiler = self.build_compiler;
862 builder.std(build_compiler, builder.config.host_target);
863
864 let _guard = builder.msg(
865 Kind::Doc,
866 format!("compiler{}", crate_description(&self.crates)),
867 Mode::Rustc,
868 build_compiler,
869 target,
870 );
871
872 let mut cargo = builder::Cargo::new(
874 builder,
875 build_compiler,
876 Mode::Rustc,
877 SourceType::InTree,
878 target,
879 Kind::Doc,
880 );
881
882 cargo.rustdocflag("--document-private-items");
883 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
885 cargo.rustdocflag("--enable-index-page");
886 cargo.rustdocflag("-Znormalize-docs");
887 cargo.rustdocflag("--show-type-layout");
888 cargo.rustdocflag("--generate-link-to-definition");
892
893 compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
894 cargo.arg("-Zskip-rustdoc-fingerprint");
895
896 cargo.arg("--no-deps");
899 cargo.arg("-Zrustdoc-map");
900
901 cargo.rustdocflag("--extern-html-root-url");
904 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
905
906 let mut to_open = None;
907
908 let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
909 for krate in &*self.crates {
910 let dir_name = krate.replace('-', "_");
914 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
915 cargo.arg("-p").arg(krate);
916 if to_open.is_none() {
917 to_open = Some(dir_name);
918 }
919 }
920
921 symlink_dir_force(&builder.config, &out, &out_dir);
928 let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
931 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
932
933 cargo.into_cmd().run(builder);
934
935 if !builder.config.dry_run() {
936 for krate in &*self.crates {
938 let dir_name = krate.replace('-', "_");
939 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
941 }
942 }
943
944 if builder.paths.iter().any(|path| path.ends_with("compiler")) {
945 let index = out.join("rustc_middle").join("index.html");
947 builder.open_in_browser(index);
948 } else if let Some(krate) = to_open {
949 let index = out.join(krate).join("index.html");
951 builder.open_in_browser(index);
952 }
953 }
954
955 fn metadata(&self) -> Option<StepMetadata> {
956 Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
957 }
958}
959
960macro_rules! tool_doc {
961 (
962 $tool: ident,
963 $path: literal,
964 $(rustc_private_tool = $rustc_private_tool:literal, )?
965 $(is_library = $is_library:expr,)?
966 $(crates = $crates:expr)?
967 ) => {
968 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
969 pub struct $tool {
970 build_compiler: Compiler,
971 mode: Mode,
972 target: TargetSelection,
973 }
974
975 impl Step for $tool {
976 type Output = ();
977 const DEFAULT: bool = true;
978 const IS_HOST: bool = true;
979
980 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
981 let builder = run.builder;
982 run.path($path).default_condition(builder.config.compiler_docs)
983 }
984
985 fn make_run(run: RunConfig<'_>) {
986 let target = run.target;
987 let (build_compiler, mode) = if true $(&& $rustc_private_tool)? {
988 let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
990
991 run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
993
994 (compilers.build_compiler(), Mode::ToolRustc)
995 } else {
996 (prepare_doc_compiler(run.builder, target, 1), Mode::ToolBootstrap)
998 };
999
1000 run.builder.ensure($tool { build_compiler, mode, target });
1001 }
1002
1003 fn run(self, builder: &Builder<'_>) {
1007 let mut source_type = SourceType::InTree;
1008
1009 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1010 source_type = SourceType::Submodule;
1011 builder.require_submodule(&submodule_path, None);
1012 }
1013
1014 let $tool { build_compiler, mode, target } = self;
1015
1016 let out = builder.compiler_doc_out(target);
1018 t!(fs::create_dir_all(&out));
1019
1020 let mut cargo = prepare_tool_cargo(
1022 builder,
1023 build_compiler,
1024 mode,
1025 target,
1026 Kind::Doc,
1027 $path,
1028 source_type,
1029 &[],
1030 );
1031
1032 cargo.arg("-Zskip-rustdoc-fingerprint");
1033 cargo.arg("--no-deps");
1035
1036 if false $(|| $is_library)? {
1037 cargo.arg("--lib");
1038 }
1039
1040 $(for krate in $crates {
1041 cargo.arg("-p").arg(krate);
1042 })?
1043
1044 cargo.rustdocflag("--document-private-items");
1045 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1047 cargo.rustdocflag("--enable-index-page");
1048 cargo.rustdocflag("--show-type-layout");
1049 cargo.rustdocflag("--generate-link-to-definition");
1050
1051 let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1052 $(for krate in $crates {
1053 let dir_name = krate.replace("-", "_");
1054 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1055 })?
1056
1057 symlink_dir_force(&builder.config, &out, &out_dir);
1059 let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1060 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1061
1062 let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1063 cargo.into_cmd().run(builder);
1064
1065 if !builder.config.dry_run() {
1066 $(for krate in $crates {
1068 let dir_name = krate.replace("-", "_");
1069 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1071 })?
1072 }
1073 }
1074
1075 fn metadata(&self) -> Option<StepMetadata> {
1076 Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1077 }
1078 }
1079 }
1080}
1081
1082tool_doc!(
1084 BuildHelper,
1085 "src/build_helper",
1086 rustc_private_tool = false,
1087 is_library = true,
1088 crates = ["build_helper"]
1089);
1090tool_doc!(Rustdoc, "src/tools/rustdoc", crates = ["rustdoc", "rustdoc-json-types"]);
1091tool_doc!(Rustfmt, "src/tools/rustfmt", crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]);
1092tool_doc!(Clippy, "src/tools/clippy", crates = ["clippy_config", "clippy_utils"]);
1093tool_doc!(Miri, "src/tools/miri", crates = ["miri"]);
1094tool_doc!(
1095 Cargo,
1096 "src/tools/cargo",
1097 rustc_private_tool = false,
1098 crates = [
1099 "cargo",
1100 "cargo-credential",
1101 "cargo-platform",
1102 "cargo-test-macro",
1103 "cargo-test-support",
1104 "cargo-util",
1105 "cargo-util-schemas",
1106 "crates-io",
1107 "mdman",
1108 "rustfix",
1109 ]
1110);
1111tool_doc!(Tidy, "src/tools/tidy", rustc_private_tool = false, crates = ["tidy"]);
1112tool_doc!(
1113 Bootstrap,
1114 "src/bootstrap",
1115 rustc_private_tool = false,
1116 is_library = true,
1117 crates = ["bootstrap"]
1118);
1119tool_doc!(
1120 RunMakeSupport,
1121 "src/tools/run-make-support",
1122 rustc_private_tool = false,
1123 is_library = true,
1124 crates = ["run_make_support"]
1125);
1126tool_doc!(
1127 Compiletest,
1128 "src/tools/compiletest",
1129 rustc_private_tool = false,
1130 is_library = true,
1131 crates = ["compiletest"]
1132);
1133
1134#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1135pub struct ErrorIndex {
1136 compilers: RustcPrivateCompilers,
1137}
1138
1139impl Step for ErrorIndex {
1140 type Output = ();
1141 const DEFAULT: bool = true;
1142 const IS_HOST: bool = true;
1143
1144 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1145 let builder = run.builder;
1146 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1147 }
1148
1149 fn make_run(run: RunConfig<'_>) {
1150 run.builder.ensure(ErrorIndex {
1151 compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1152 });
1153 }
1154
1155 fn run(self, builder: &Builder<'_>) {
1158 builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1159 let out = builder.doc_out(self.compilers.target());
1160 t!(fs::create_dir_all(&out));
1161 tool::ErrorIndex::command(builder, self.compilers)
1162 .arg("html")
1163 .arg(out)
1164 .arg(&builder.version)
1165 .run(builder);
1166 }
1167
1168 fn metadata(&self) -> Option<StepMetadata> {
1169 Some(
1170 StepMetadata::doc("error-index", self.compilers.target())
1171 .built_by(self.compilers.build_compiler()),
1172 )
1173 }
1174}
1175
1176#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1177pub struct UnstableBookGen {
1178 target: TargetSelection,
1179}
1180
1181impl Step for UnstableBookGen {
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/unstable-book-gen").default_condition(builder.config.docs)
1189 }
1190
1191 fn make_run(run: RunConfig<'_>) {
1192 run.builder.ensure(UnstableBookGen { target: run.target });
1193 }
1194
1195 fn run(self, builder: &Builder<'_>) {
1196 let target = self.target;
1197
1198 builder.info(&format!("Generating unstable book md files ({target})"));
1199 let out = builder.md_doc_out(target).join("unstable-book");
1200 builder.create_dir(&out);
1201 builder.remove_dir(&out);
1202 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1203 cmd.arg(builder.src.join("library"));
1204 cmd.arg(builder.src.join("compiler"));
1205 cmd.arg(builder.src.join("src"));
1206 cmd.arg(out);
1207
1208 cmd.run(builder);
1209 }
1210}
1211
1212fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1213 if config.dry_run() {
1214 return;
1215 }
1216 if let Ok(m) = fs::symlink_metadata(link) {
1217 if m.file_type().is_dir() {
1218 t!(fs::remove_dir_all(link));
1219 } else {
1220 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1223 }
1224 }
1225
1226 t!(
1227 symlink_dir(config, original, link),
1228 format!("failed to create link from {} -> {}", link.display(), original.display())
1229 );
1230}
1231
1232#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1234pub struct RustcBook {
1235 build_compiler: Compiler,
1236 target: TargetSelection,
1237 validate: bool,
1240}
1241
1242impl RustcBook {
1243 pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1244 Self { build_compiler, target, validate: true }
1245 }
1246}
1247
1248impl Step for RustcBook {
1249 type Output = ();
1250 const DEFAULT: bool = true;
1251 const IS_HOST: bool = true;
1252
1253 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1254 let builder = run.builder;
1255 run.path("src/doc/rustc").default_condition(builder.config.docs)
1256 }
1257
1258 fn make_run(run: RunConfig<'_>) {
1259 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1263 run.builder.top_stage
1264 } else {
1265 2
1266 };
1267
1268 run.builder.ensure(RustcBook {
1269 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1270 target: run.target,
1271 validate: false,
1272 });
1273 }
1274
1275 fn run(self, builder: &Builder<'_>) {
1281 let out_base = builder.md_doc_out(self.target).join("rustc");
1282 t!(fs::create_dir_all(&out_base));
1283 let out_listing = out_base.join("src/lints");
1284 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1285 builder.info(&format!("Generating lint docs ({})", self.target));
1286
1287 let rustc = builder.rustc(self.build_compiler);
1288 builder.std(self.build_compiler, self.target);
1291 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1292 cmd.arg("--src");
1293 cmd.arg(builder.src.join("compiler"));
1294 cmd.arg("--out");
1295 cmd.arg(&out_listing);
1296 cmd.arg("--rustc");
1297 cmd.arg(&rustc);
1298 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1299 if let Some(target_linker) = builder.linker(self.target) {
1300 cmd.arg("--rustc-linker").arg(target_linker);
1301 }
1302 if builder.is_verbose() {
1303 cmd.arg("--verbose");
1304 }
1305 if self.validate {
1306 cmd.arg("--validate");
1307 }
1308 cmd.env("RUSTC_BOOTSTRAP", "1");
1312
1313 builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1317 let doc_generator_guard =
1318 builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1319 cmd.run(builder);
1320 drop(doc_generator_guard);
1321
1322 builder.ensure(RustbookSrc {
1324 target: self.target,
1325 name: "rustc".to_owned(),
1326 src: out_base,
1327 parent: Some(self),
1328 languages: vec![],
1329 build_compiler: None,
1330 });
1331 }
1332}
1333
1334#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1338pub struct Reference {
1339 build_compiler: Compiler,
1340 target: TargetSelection,
1341}
1342
1343impl Step for Reference {
1344 type Output = ();
1345 const DEFAULT: bool = true;
1346
1347 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1348 let builder = run.builder;
1349 run.path("src/doc/reference").default_condition(builder.config.docs)
1350 }
1351
1352 fn make_run(run: RunConfig<'_>) {
1353 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1359 run.builder.top_stage
1360 } else {
1361 2
1362 };
1363
1364 run.builder.ensure(Reference {
1365 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1366 target: run.target,
1367 });
1368 }
1369
1370 fn run(self, builder: &Builder<'_>) {
1372 builder.require_submodule("src/doc/reference", None);
1373
1374 builder.std(self.build_compiler, builder.config.host_target);
1377
1378 builder.ensure(RustbookSrc {
1380 target: self.target,
1381 name: "reference".to_owned(),
1382 src: builder.src.join("src/doc/reference"),
1383 build_compiler: Some(self.build_compiler),
1384 parent: Some(self),
1385 languages: vec![],
1386 });
1387 }
1388}