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);
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 for krate in requested_crates {
811 cargo.arg("-p").arg(krate);
812 }
813
814 let description =
815 format!("library{} in {} format", crate_description(requested_crates), format.as_str());
816 let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target);
817
818 cargo.into_cmd().run(builder);
819 builder.cp_link_r(&out_dir, out);
820}
821
822pub fn prepare_doc_compiler(
824 builder: &Builder<'_>,
825 target: TargetSelection,
826 stage: u32,
827) -> Compiler {
828 assert!(stage > 0, "Cannot document anything in stage 0");
829 let build_compiler = builder.compiler(stage - 1, builder.host_target);
830 builder.std(build_compiler, target);
831 build_compiler
832}
833
834#[derive(Debug, Clone, Hash, PartialEq, Eq)]
836pub struct Rustc {
837 build_compiler: Compiler,
838 target: TargetSelection,
839 crates: Vec<String>,
840}
841
842impl Rustc {
843 pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
845 let build_compiler = prepare_doc_compiler(builder, target, stage);
846 Self::from_build_compiler(builder, build_compiler, target)
847 }
848
849 fn from_build_compiler(
850 builder: &Builder<'_>,
851 build_compiler: Compiler,
852 target: TargetSelection,
853 ) -> Self {
854 let crates = builder
855 .in_tree_crates("rustc-main", Some(target))
856 .into_iter()
857 .map(|krate| krate.name.to_string())
858 .collect();
859 Self { build_compiler, target, crates }
860 }
861}
862
863impl Step for Rustc {
864 type Output = ();
865 const DEFAULT: bool = true;
866 const IS_HOST: bool = true;
867
868 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
869 let builder = run.builder;
870 run.crate_or_deps("rustc-main")
871 .path("compiler")
872 .default_condition(builder.config.compiler_docs)
873 }
874
875 fn make_run(run: RunConfig<'_>) {
876 run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target));
877 }
878
879 fn run(self, builder: &Builder<'_>) {
886 let target = self.target;
887
888 let out = builder.compiler_doc_out(target);
890 t!(fs::create_dir_all(&out));
891
892 let build_compiler = self.build_compiler;
895 builder.std(build_compiler, builder.config.host_target);
896
897 let _guard = builder.msg(
898 Kind::Doc,
899 format!("compiler{}", crate_description(&self.crates)),
900 Mode::Rustc,
901 build_compiler,
902 target,
903 );
904
905 let mut cargo = builder::Cargo::new(
907 builder,
908 build_compiler,
909 Mode::Rustc,
910 SourceType::InTree,
911 target,
912 Kind::Doc,
913 );
914
915 cargo.rustdocflag("--document-private-items");
916 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
918 cargo.rustdocflag("--enable-index-page");
919 cargo.rustdocflag("-Znormalize-docs");
920 cargo.rustdocflag("--show-type-layout");
921 cargo.rustdocflag("--generate-link-to-definition");
925
926 compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
927 cargo.arg("-Zskip-rustdoc-fingerprint");
928
929 cargo.arg("--no-deps");
932 cargo.arg("-Zrustdoc-map");
933
934 cargo.rustdocflag("--extern-html-root-url");
937 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
938
939 let mut to_open = None;
940
941 let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
942 for krate in &*self.crates {
943 let dir_name = krate.replace('-', "_");
947 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
948 cargo.arg("-p").arg(krate);
949 if to_open.is_none() {
950 to_open = Some(dir_name);
951 }
952 }
953
954 symlink_dir_force(&builder.config, &out, &out_dir);
961 let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
964 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
965
966 cargo.into_cmd().run(builder);
967
968 if !builder.config.dry_run() {
969 for krate in &*self.crates {
971 let dir_name = krate.replace('-', "_");
972 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
974 }
975 }
976
977 if builder.paths.iter().any(|path| path.ends_with("compiler")) {
978 let index = out.join("rustc_middle").join("index.html");
980 builder.open_in_browser(index);
981 } else if let Some(krate) = to_open {
982 let index = out.join(krate).join("index.html");
984 builder.open_in_browser(index);
985 }
986 }
987
988 fn metadata(&self) -> Option<StepMetadata> {
989 Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
990 }
991}
992
993macro_rules! tool_doc {
994 (
995 $tool: ident,
996 $path: literal,
997 mode = $mode:expr
998 $(, is_library = $is_library:expr )?
999 $(, crates = $crates:expr )?
1000 $(, allow_features: $allow_features:expr )?
1002 ) => {
1003 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
1004 pub struct $tool {
1005 build_compiler: Compiler,
1006 mode: Mode,
1007 target: TargetSelection,
1008 }
1009
1010 impl Step for $tool {
1011 type Output = ();
1012 const DEFAULT: bool = true;
1013 const IS_HOST: bool = true;
1014
1015 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1016 let builder = run.builder;
1017 run.path($path).default_condition(builder.config.compiler_docs)
1018 }
1019
1020 fn make_run(run: RunConfig<'_>) {
1021 let target = run.target;
1022 let build_compiler = match $mode {
1023 Mode::ToolRustcPrivate => {
1024 let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
1026
1027 run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
1029 compilers.build_compiler()
1030 }
1031 Mode::ToolTarget => {
1032 prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1035 }
1036 _ => {
1037 panic!("Unexpected tool mode for documenting: {:?}", $mode);
1038 }
1039 };
1040
1041 run.builder.ensure($tool { build_compiler, mode: $mode, target });
1042 }
1043
1044 fn run(self, builder: &Builder<'_>) {
1048 let mut source_type = SourceType::InTree;
1049
1050 if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1051 source_type = SourceType::Submodule;
1052 builder.require_submodule(&submodule_path, None);
1053 }
1054
1055 let $tool { build_compiler, mode, target } = self;
1056
1057 let out = builder.compiler_doc_out(target);
1059 t!(fs::create_dir_all(&out));
1060
1061 let mut cargo = prepare_tool_cargo(
1063 builder,
1064 build_compiler,
1065 mode,
1066 target,
1067 Kind::Doc,
1068 $path,
1069 source_type,
1070 &[],
1071 );
1072 let allow_features = {
1073 let mut _value = "";
1074 $( _value = $allow_features; )?
1075 _value
1076 };
1077
1078 if !allow_features.is_empty() {
1079 cargo.allow_features(allow_features);
1080 }
1081
1082 cargo.arg("-Zskip-rustdoc-fingerprint");
1083 cargo.arg("--no-deps");
1085
1086 if false $(|| $is_library)? {
1087 cargo.arg("--lib");
1088 }
1089
1090 $(for krate in $crates {
1091 cargo.arg("-p").arg(krate);
1092 })?
1093
1094 cargo.rustdocflag("--document-private-items");
1095 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1097 cargo.rustdocflag("--enable-index-page");
1098 cargo.rustdocflag("--show-type-layout");
1099 cargo.rustdocflag("--generate-link-to-definition");
1100
1101 let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1102 $(for krate in $crates {
1103 let dir_name = krate.replace("-", "_");
1104 t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1105 })?
1106
1107 symlink_dir_force(&builder.config, &out, &out_dir);
1109 let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1110 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1111
1112 let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1113 cargo.into_cmd().run(builder);
1114
1115 if !builder.config.dry_run() {
1116 $(for krate in $crates {
1118 let dir_name = krate.replace("-", "_");
1119 assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1121 })?
1122 }
1123 }
1124
1125 fn metadata(&self) -> Option<StepMetadata> {
1126 Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1127 }
1128 }
1129 }
1130}
1131
1132tool_doc!(
1134 BuildHelper,
1135 "src/build_helper",
1136 mode = Mode::ToolTarget,
1141 is_library = true,
1142 crates = ["build_helper"]
1143);
1144tool_doc!(
1145 Rustdoc,
1146 "src/tools/rustdoc",
1147 mode = Mode::ToolRustcPrivate,
1148 crates = ["rustdoc", "rustdoc-json-types"]
1149);
1150tool_doc!(
1151 Rustfmt,
1152 "src/tools/rustfmt",
1153 mode = Mode::ToolRustcPrivate,
1154 crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1155);
1156tool_doc!(
1157 Clippy,
1158 "src/tools/clippy",
1159 mode = Mode::ToolRustcPrivate,
1160 crates = ["clippy_config", "clippy_utils"]
1161);
1162tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1163tool_doc!(
1164 Cargo,
1165 "src/tools/cargo",
1166 mode = Mode::ToolTarget,
1167 crates = [
1168 "cargo",
1169 "cargo-credential",
1170 "cargo-platform",
1171 "cargo-test-macro",
1172 "cargo-test-support",
1173 "cargo-util",
1174 "cargo-util-schemas",
1175 "crates-io",
1176 "mdman",
1177 "rustfix",
1178 ],
1179 allow_features: "specialization"
1182);
1183tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
1184tool_doc!(
1185 Bootstrap,
1186 "src/bootstrap",
1187 mode = Mode::ToolTarget,
1188 is_library = true,
1189 crates = ["bootstrap"]
1190);
1191tool_doc!(
1192 RunMakeSupport,
1193 "src/tools/run-make-support",
1194 mode = Mode::ToolTarget,
1195 is_library = true,
1196 crates = ["run_make_support"]
1197);
1198tool_doc!(
1199 Compiletest,
1200 "src/tools/compiletest",
1201 mode = Mode::ToolTarget,
1202 is_library = true,
1203 crates = ["compiletest"]
1204);
1205
1206#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1207pub struct ErrorIndex {
1208 compilers: RustcPrivateCompilers,
1209}
1210
1211impl Step for ErrorIndex {
1212 type Output = ();
1213 const DEFAULT: bool = true;
1214 const IS_HOST: bool = true;
1215
1216 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1217 let builder = run.builder;
1218 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
1219 }
1220
1221 fn make_run(run: RunConfig<'_>) {
1222 run.builder.ensure(ErrorIndex {
1223 compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1224 });
1225 }
1226
1227 fn run(self, builder: &Builder<'_>) {
1230 builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1231 let out = builder.doc_out(self.compilers.target());
1232 t!(fs::create_dir_all(&out));
1233 tool::ErrorIndex::command(builder, self.compilers)
1234 .arg("html")
1235 .arg(out)
1236 .arg(&builder.version)
1237 .run(builder);
1238 }
1239
1240 fn metadata(&self) -> Option<StepMetadata> {
1241 Some(
1242 StepMetadata::doc("error-index", self.compilers.target())
1243 .built_by(self.compilers.build_compiler()),
1244 )
1245 }
1246}
1247
1248#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1249pub struct UnstableBookGen {
1250 target: TargetSelection,
1251}
1252
1253impl Step for UnstableBookGen {
1254 type Output = ();
1255 const DEFAULT: bool = true;
1256 const IS_HOST: bool = true;
1257
1258 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1259 let builder = run.builder;
1260 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
1261 }
1262
1263 fn make_run(run: RunConfig<'_>) {
1264 run.builder.ensure(UnstableBookGen { target: run.target });
1265 }
1266
1267 fn run(self, builder: &Builder<'_>) {
1268 let target = self.target;
1269
1270 builder.info(&format!("Generating unstable book md files ({target})"));
1271 let out = builder.md_doc_out(target).join("unstable-book");
1272 builder.create_dir(&out);
1273 builder.remove_dir(&out);
1274 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1275 cmd.arg(builder.src.join("library"));
1276 cmd.arg(builder.src.join("compiler"));
1277 cmd.arg(builder.src.join("src"));
1278 cmd.arg(out);
1279
1280 cmd.run(builder);
1281 }
1282}
1283
1284fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1285 if config.dry_run() {
1286 return;
1287 }
1288 if let Ok(m) = fs::symlink_metadata(link) {
1289 if m.file_type().is_dir() {
1290 t!(fs::remove_dir_all(link));
1291 } else {
1292 t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1295 }
1296 }
1297
1298 t!(
1299 symlink_dir(config, original, link),
1300 format!("failed to create link from {} -> {}", link.display(), original.display())
1301 );
1302}
1303
1304#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1306pub struct RustcBook {
1307 build_compiler: Compiler,
1308 target: TargetSelection,
1309 validate: bool,
1312}
1313
1314impl RustcBook {
1315 pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1316 Self { build_compiler, target, validate: true }
1317 }
1318}
1319
1320impl Step for RustcBook {
1321 type Output = ();
1322 const DEFAULT: bool = true;
1323 const IS_HOST: bool = true;
1324
1325 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1326 let builder = run.builder;
1327 run.path("src/doc/rustc").default_condition(builder.config.docs)
1328 }
1329
1330 fn make_run(run: RunConfig<'_>) {
1331 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1335 run.builder.top_stage
1336 } else {
1337 2
1338 };
1339
1340 run.builder.ensure(RustcBook {
1341 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1342 target: run.target,
1343 validate: false,
1344 });
1345 }
1346
1347 fn run(self, builder: &Builder<'_>) {
1353 let out_base = builder.md_doc_out(self.target).join("rustc");
1354 t!(fs::create_dir_all(&out_base));
1355 let out_listing = out_base.join("src/lints");
1356 builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1357 builder.info(&format!("Generating lint docs ({})", self.target));
1358
1359 let rustc = builder.rustc(self.build_compiler);
1360 builder.std(self.build_compiler, self.target);
1363 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1364 cmd.arg("--build-rustc-stage");
1365 cmd.arg(self.build_compiler.stage.to_string());
1366 cmd.arg("--src");
1367 cmd.arg(builder.src.join("compiler"));
1368 cmd.arg("--out");
1369 cmd.arg(&out_listing);
1370 cmd.arg("--rustc");
1371 cmd.arg(&rustc);
1372 cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1373 if let Some(target_linker) = builder.linker(self.target) {
1374 cmd.arg("--rustc-linker").arg(target_linker);
1375 }
1376 if builder.is_verbose() {
1377 cmd.arg("--verbose");
1378 }
1379 if self.validate {
1380 cmd.arg("--validate");
1381 }
1382 cmd.env("RUSTC_BOOTSTRAP", "1");
1386
1387 builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1391 let doc_generator_guard =
1392 builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1393 cmd.run(builder);
1394 drop(doc_generator_guard);
1395
1396 builder.ensure(RustbookSrc {
1398 target: self.target,
1399 name: "rustc".to_owned(),
1400 src: out_base,
1401 parent: Some(self),
1402 languages: vec![],
1403 build_compiler: None,
1404 });
1405 }
1406}
1407
1408#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1412pub struct Reference {
1413 build_compiler: Compiler,
1414 target: TargetSelection,
1415}
1416
1417impl Step for Reference {
1418 type Output = ();
1419 const DEFAULT: bool = true;
1420
1421 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1422 let builder = run.builder;
1423 run.path("src/doc/reference").default_condition(builder.config.docs)
1424 }
1425
1426 fn make_run(run: RunConfig<'_>) {
1427 let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1433 run.builder.top_stage
1434 } else {
1435 2
1436 };
1437
1438 run.builder.ensure(Reference {
1439 build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1440 target: run.target,
1441 });
1442 }
1443
1444 fn run(self, builder: &Builder<'_>) {
1446 builder.require_submodule("src/doc/reference", None);
1447
1448 builder.std(self.build_compiler, builder.config.host_target);
1451
1452 builder.ensure(RustbookSrc {
1454 target: self.target,
1455 name: "reference".to_owned(),
1456 src: builder.src.join("src/doc/reference"),
1457 build_compiler: Some(self.build_compiler),
1458 parent: Some(self),
1459 languages: vec![],
1460 });
1461 }
1462}