Skip to main content

bootstrap/core/build_steps/
doc.rs

1//! Documentation generation for bootstrap.
2//!
3//! This module implements generation for all bits and pieces of documentation
4//! for the Rust project. This notably includes suites like the rust book, the
5//! nomicon, rust by example, standalone documentation, etc.
6//!
7//! Everything here is basically just a shim around calling either `rustbook` or
8//! `rustdoc`.
9
10use 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
36            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
37                run.path($path)
38            }
39
40            fn is_default_step(builder: &Builder<'_>) -> bool {
41                builder.config.docs
42            }
43
44            fn make_run(run: RunConfig<'_>) {
45                run.builder.ensure($name {
46                    target: run.target,
47                });
48            }
49
50            fn run(self, builder: &Builder<'_>) {
51                if let Some(submodule_path) = submodule_path_of(&builder, $path) {
52                    builder.require_submodule(&submodule_path, None)
53                }
54
55                builder.ensure(RustbookSrc {
56                    target: self.target,
57                    name: $book_name.to_owned(),
58                    src: builder.src.join($path),
59                    parent: Some(self),
60                    languages: $lang.into(),
61                    build_compiler: None,
62                })
63            }
64        }
65        )+
66    }
67}
68
69// NOTE: When adding a book here, make sure to ALSO build the book by
70// adding a build step in `src/bootstrap/code/builder/mod.rs`!
71// NOTE: Make sure to add the corresponding submodule when adding a new book.
72book!(
73    CargoBook, "src/tools/cargo/src/doc", "cargo", &[];
74    ClippyBook, "src/tools/clippy/book", "clippy", &[];
75    EditionGuide, "src/doc/edition-guide", "edition-guide", &[];
76    EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[];
77    Nomicon, "src/doc/nomicon", "nomicon", &[];
78    RustByExample, "src/doc/rust-by-example", "rust-by-example", &["es", "ja", "zh", "ko"];
79    RustdocBook, "src/doc/rustdoc", "rustdoc", &[];
80    StyleGuide, "src/doc/style-guide", "style-guide", &[];
81);
82
83#[derive(Debug, Clone, Hash, PartialEq, Eq)]
84pub struct UnstableBook {
85    target: TargetSelection,
86}
87
88impl Step for UnstableBook {
89    type Output = ();
90
91    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
92        run.path("src/doc/unstable-book")
93    }
94
95    fn is_default_step(builder: &Builder<'_>) -> bool {
96        builder.config.docs
97    }
98
99    fn make_run(run: RunConfig<'_>) {
100        run.builder.ensure(UnstableBook { target: run.target });
101    }
102
103    fn run(self, builder: &Builder<'_>) {
104        builder.ensure(UnstableBookGen { target: self.target });
105        builder.ensure(RustbookSrc {
106            target: self.target,
107            name: "unstable-book".to_owned(),
108            src: builder.md_doc_out(self.target).join("unstable-book"),
109            parent: Some(self),
110            languages: vec![],
111            build_compiler: None,
112        })
113    }
114}
115
116#[derive(Debug, Clone, Hash, PartialEq, Eq)]
117struct RustbookSrc<P: Step> {
118    target: TargetSelection,
119    name: String,
120    src: PathBuf,
121    parent: Option<P>,
122    languages: Vec<&'static str>,
123    /// Compiler whose rustdoc should be used to document things using `mdbook-spec`.
124    build_compiler: Option<Compiler>,
125}
126
127impl<P: Step> Step for RustbookSrc<P> {
128    type Output = ();
129
130    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
131        run.never()
132    }
133
134    /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
135    ///
136    /// This will not actually generate any documentation if the documentation has
137    /// already been generated.
138    fn run(self, builder: &Builder<'_>) {
139        let target = self.target;
140        let name = self.name;
141        let src = self.src;
142        let out = builder.doc_out(target);
143        t!(fs::create_dir_all(&out));
144
145        let out = out.join(&name);
146        let index = out.join("index.html");
147        let rustbook = builder.tool_exe(Tool::Rustbook);
148
149        if !builder.config.dry_run()
150            && (!up_to_date(&src, &index) || !up_to_date(&rustbook, &index))
151        {
152            builder.info(&format!("Rustbook ({target}) - {name}"));
153            let _ = fs::remove_dir_all(&out);
154
155            let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
156
157            if let Some(compiler) = self.build_compiler {
158                let mut rustdoc = builder.rustdoc_for_compiler(compiler);
159                rustdoc.pop();
160                let old_path = env::var_os("PATH").unwrap_or_default();
161                let new_path =
162                    env::join_paths(std::iter::once(rustdoc).chain(env::split_paths(&old_path)))
163                        .expect("could not add rustdoc to PATH");
164
165                rustbook_cmd.env("PATH", new_path);
166                builder.add_rustc_lib_path(compiler, &mut rustbook_cmd);
167            }
168
169            rustbook_cmd
170                .arg("build")
171                .arg(&src)
172                .arg("-d")
173                .arg(&out)
174                .arg("--rust-root")
175                .arg(&builder.src)
176                .run(builder);
177
178            for lang in &self.languages {
179                let out = out.join(lang);
180
181                builder.info(&format!("Rustbook ({target}) - {name} - {lang}"));
182                let _ = fs::remove_dir_all(&out);
183
184                builder
185                    .tool_cmd(Tool::Rustbook)
186                    .arg("build")
187                    .arg(&src)
188                    .arg("-d")
189                    .arg(&out)
190                    .arg("-l")
191                    .arg(lang)
192                    .run(builder);
193            }
194        }
195
196        if self.parent.is_some() {
197            builder.maybe_open_in_browser::<P>(index)
198        }
199    }
200
201    fn metadata(&self) -> Option<StepMetadata> {
202        let mut metadata = StepMetadata::doc(&format!("{} (book)", self.name), self.target);
203        if let Some(compiler) = self.build_compiler {
204            metadata = metadata.built_by(compiler);
205        }
206
207        Some(metadata)
208    }
209}
210
211#[derive(Debug, Clone, Hash, PartialEq, Eq)]
212pub struct TheBook {
213    /// Compiler whose rustdoc will be used to generated documentation.
214    build_compiler: Compiler,
215    target: TargetSelection,
216}
217
218impl Step for TheBook {
219    type Output = ();
220
221    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
222        run.path("src/doc/book")
223    }
224
225    fn is_default_step(builder: &Builder<'_>) -> bool {
226        builder.config.docs
227    }
228
229    fn make_run(run: RunConfig<'_>) {
230        run.builder.ensure(TheBook {
231            build_compiler: prepare_doc_compiler(run.builder, run.target, run.builder.top_stage),
232            target: run.target,
233        });
234    }
235
236    /// Builds the book and associated stuff.
237    ///
238    /// We need to build:
239    ///
240    /// * Book
241    /// * Older edition redirects
242    /// * Version info and CSS
243    /// * Index page
244    /// * Redirect pages
245    fn run(self, builder: &Builder<'_>) {
246        builder.require_submodule("src/doc/book", None);
247
248        let build_compiler = self.build_compiler;
249        let target = self.target;
250
251        let absolute_path = builder.src.join("src/doc/book");
252        let redirect_path = absolute_path.join("redirects");
253
254        // build book
255        builder.ensure(RustbookSrc {
256            target,
257            name: "book".to_owned(),
258            src: absolute_path.clone(),
259            parent: Some(self),
260            languages: vec![],
261            build_compiler: None,
262        });
263
264        // building older edition redirects
265        for edition in &["first-edition", "second-edition", "2018-edition"] {
266            builder.ensure(RustbookSrc {
267                target,
268                name: format!("book/{edition}"),
269                src: absolute_path.join(edition),
270                // There should only be one book that is marked as the parent for each target, so
271                // treat the other editions as not having a parent.
272                parent: Option::<Self>::None,
273                languages: vec![],
274                build_compiler: None,
275            });
276        }
277
278        // build the version info page and CSS
279        let shared_assets = builder.ensure(SharedAssets { target });
280
281        // build the redirect pages
282        let _guard = builder.msg(Kind::Doc, "book redirect pages", None, build_compiler, target);
283        if builder.config.dry_run() {
284            return;
285        }
286
287        for file in t!(fs::read_dir(redirect_path)) {
288            let file = t!(file);
289            let path = file.path();
290            let path = path.to_str().unwrap();
291
292            invoke_rustdoc(builder, build_compiler, &shared_assets, target, path);
293        }
294    }
295}
296
297fn invoke_rustdoc(
298    builder: &Builder<'_>,
299    build_compiler: Compiler,
300    shared_assets: &SharedAssetsPaths,
301    target: TargetSelection,
302    markdown: &str,
303) {
304    let out = builder.doc_out(target);
305
306    let path = builder.src.join("src/doc").join(markdown);
307
308    let header = builder.src.join("src/doc/redirect.inc");
309    let footer = builder.src.join("src/doc/footer.inc");
310
311    let mut cmd = builder.rustdoc_cmd(build_compiler);
312
313    let out = out.join("book");
314
315    cmd.arg("--html-after-content")
316        .arg(&footer)
317        .arg("--html-before-content")
318        .arg(&shared_assets.version_info)
319        .arg("--html-in-header")
320        .arg(&header)
321        .arg("--markdown-no-toc")
322        .arg("--markdown-playground-url")
323        .arg("https://play.rust-lang.org/")
324        .arg("-o")
325        .arg(&out)
326        .arg(&path)
327        .arg("--markdown-css")
328        .arg("../rust.css")
329        .arg("-Zunstable-options");
330
331    if !builder.config.docs_minification {
332        cmd.arg("--disable-minification");
333    }
334
335    cmd.run(builder);
336}
337
338#[derive(Debug, Clone, Hash, PartialEq, Eq)]
339pub struct Standalone {
340    build_compiler: Compiler,
341    target: TargetSelection,
342}
343
344impl Step for Standalone {
345    type Output = ();
346
347    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
348        run.path("src/doc").alias("standalone")
349    }
350
351    fn is_default_step(builder: &Builder<'_>) -> bool {
352        builder.config.docs
353    }
354
355    fn make_run(run: RunConfig<'_>) {
356        run.builder.ensure(Standalone {
357            build_compiler: prepare_doc_compiler(
358                run.builder,
359                run.builder.host_target,
360                run.builder.top_stage,
361            ),
362            target: run.target,
363        });
364    }
365
366    /// Generates all standalone documentation as compiled by the rustdoc in `stage`
367    /// for the `target` into `out`.
368    ///
369    /// This will list all of `src/doc` looking for markdown files and appropriately
370    /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
371    /// `STAMP` along with providing the various header/footer HTML we've customized.
372    ///
373    /// In the end, this is just a glorified wrapper around rustdoc!
374    fn run(self, builder: &Builder<'_>) {
375        let target = self.target;
376        let build_compiler = self.build_compiler;
377        let _guard = builder.msg(Kind::Doc, "standalone", None, build_compiler, target);
378        let out = builder.doc_out(target);
379        t!(fs::create_dir_all(&out));
380
381        let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
382
383        let favicon = builder.src.join("src/doc/favicon.inc");
384        let footer = builder.src.join("src/doc/footer.inc");
385        let full_toc = builder.src.join("src/doc/full-toc.inc");
386
387        for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
388            let file = t!(file);
389            let path = file.path();
390            let filename = path.file_name().unwrap().to_str().unwrap();
391            if !filename.ends_with(".md") || filename == "README.md" {
392                continue;
393            }
394
395            let html = out.join(filename).with_extension("html");
396            let rustdoc = builder.rustdoc_for_compiler(build_compiler);
397            if up_to_date(&path, &html)
398                && up_to_date(&footer, &html)
399                && up_to_date(&favicon, &html)
400                && up_to_date(&full_toc, &html)
401                && (builder.config.dry_run() || up_to_date(&version_info, &html))
402                && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
403            {
404                continue;
405            }
406
407            let mut cmd = builder.rustdoc_cmd(build_compiler);
408
409            cmd.arg("--html-after-content")
410                .arg(&footer)
411                .arg("--html-before-content")
412                .arg(&version_info)
413                .arg("--html-in-header")
414                .arg(&favicon)
415                .arg("--markdown-no-toc")
416                .arg("-Zunstable-options")
417                .arg("--index-page")
418                .arg(builder.src.join("src/doc/index.md"))
419                .arg("--markdown-playground-url")
420                .arg("https://play.rust-lang.org/")
421                .arg("-o")
422                .arg(&out)
423                .arg(&path);
424
425            if !builder.config.docs_minification {
426                cmd.arg("--disable-minification");
427            }
428
429            if filename == "not_found.md" {
430                cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
431            } else {
432                cmd.arg("--markdown-css").arg("rust.css");
433            }
434            cmd.run(builder);
435        }
436
437        // We open doc/index.html as the default if invoked as `x.py doc --open`
438        // with no particular explicit doc requested (e.g. library/core).
439        if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
440            let index = out.join("index.html");
441            builder.open_in_browser(index);
442        }
443    }
444
445    fn metadata(&self) -> Option<StepMetadata> {
446        Some(StepMetadata::doc("standalone", self.target).built_by(self.build_compiler))
447    }
448}
449
450#[derive(Debug, Clone, Hash, PartialEq, Eq)]
451pub struct Releases {
452    build_compiler: Compiler,
453    target: TargetSelection,
454}
455
456impl Step for Releases {
457    type Output = ();
458
459    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
460        run.path("RELEASES.md").alias("releases")
461    }
462
463    fn is_default_step(builder: &Builder<'_>) -> bool {
464        builder.config.docs
465    }
466
467    fn make_run(run: RunConfig<'_>) {
468        run.builder.ensure(Releases {
469            build_compiler: prepare_doc_compiler(
470                run.builder,
471                run.builder.host_target,
472                run.builder.top_stage,
473            ),
474            target: run.target,
475        });
476    }
477
478    /// Generates HTML release notes to include in the final docs bundle.
479    ///
480    /// This uses the same stylesheet and other tools as Standalone, but the
481    /// RELEASES.md file is included at the root of the repository and gets
482    /// the headline added. In the end, the conversion is done by Rustdoc.
483    fn run(self, builder: &Builder<'_>) {
484        let target = self.target;
485        let build_compiler = self.build_compiler;
486        let _guard = builder.msg(Kind::Doc, "releases", None, build_compiler, target);
487        let out = builder.doc_out(target);
488        t!(fs::create_dir_all(&out));
489
490        builder.ensure(Standalone { build_compiler, target });
491
492        let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
493
494        let favicon = builder.src.join("src/doc/favicon.inc");
495        let footer = builder.src.join("src/doc/footer.inc");
496        let full_toc = builder.src.join("src/doc/full-toc.inc");
497
498        let html = out.join("releases.html");
499        let tmppath = out.join("releases.md");
500        let inpath = builder.src.join("RELEASES.md");
501        let rustdoc = builder.rustdoc_for_compiler(build_compiler);
502        if !up_to_date(&inpath, &html)
503            || !up_to_date(&footer, &html)
504            || !up_to_date(&favicon, &html)
505            || !up_to_date(&full_toc, &html)
506            || !(builder.config.dry_run()
507                || up_to_date(&version_info, &html)
508                || up_to_date(&rustdoc, &html))
509        {
510            let mut tmpfile = t!(fs::File::create(&tmppath));
511            t!(tmpfile.write_all(b"% Rust Release Notes\n\n"));
512            t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile));
513            mem::drop(tmpfile);
514            let mut cmd = builder.rustdoc_cmd(build_compiler);
515
516            cmd.arg("--html-after-content")
517                .arg(&footer)
518                .arg("--html-before-content")
519                .arg(&version_info)
520                .arg("--html-in-header")
521                .arg(&favicon)
522                .arg("--markdown-no-toc")
523                .arg("--markdown-css")
524                .arg("rust.css")
525                .arg("-Zunstable-options")
526                .arg("--index-page")
527                .arg(builder.src.join("src/doc/index.md"))
528                .arg("--markdown-playground-url")
529                .arg("https://play.rust-lang.org/")
530                .arg("-o")
531                .arg(&out)
532                .arg(&tmppath);
533
534            if !builder.config.docs_minification {
535                cmd.arg("--disable-minification");
536            }
537
538            cmd.run(builder);
539        }
540
541        // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md`
542        // with no particular explicit doc requested (e.g. library/core).
543        if builder.was_invoked_explicitly::<Self>(Kind::Doc) {
544            builder.open_in_browser(&html);
545        }
546    }
547
548    fn metadata(&self) -> Option<StepMetadata> {
549        Some(StepMetadata::doc("releases", self.target).built_by(self.build_compiler))
550    }
551}
552
553#[derive(Debug, Clone)]
554pub struct SharedAssetsPaths {
555    pub version_info: PathBuf,
556}
557
558#[derive(Debug, Clone, Hash, PartialEq, Eq)]
559pub struct SharedAssets {
560    target: TargetSelection,
561}
562
563impl Step for SharedAssets {
564    type Output = SharedAssetsPaths;
565
566    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
567        // Other tasks depend on this, no need to execute it on its own
568        run.never()
569    }
570
571    fn is_default_step(_builder: &Builder<'_>) -> bool {
572        false
573    }
574
575    /// Generate shared resources used by other pieces of documentation.
576    fn run(self, builder: &Builder<'_>) -> Self::Output {
577        let out = builder.doc_out(self.target);
578
579        let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
580        let version_info = out.join("version_info.html");
581        if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
582            let info = t!(fs::read_to_string(&version_input))
583                .replace("VERSION", &builder.rust_release())
584                .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
585                .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
586            t!(fs::write(&version_info, info));
587        }
588
589        builder.copy_link(
590            &builder.src.join("src").join("doc").join("rust.css"),
591            &out.join("rust.css"),
592            FileType::Regular,
593        );
594
595        builder.copy_link(
596            &builder
597                .src
598                .join("src")
599                .join("librustdoc")
600                .join("html")
601                .join("static")
602                .join("images")
603                .join("favicon.svg"),
604            &out.join("favicon.svg"),
605            FileType::Regular,
606        );
607        builder.copy_link(
608            &builder
609                .src
610                .join("src")
611                .join("librustdoc")
612                .join("html")
613                .join("static")
614                .join("images")
615                .join("favicon-32x32.png"),
616            &out.join("favicon-32x32.png"),
617            FileType::Regular,
618        );
619
620        SharedAssetsPaths { version_info }
621    }
622}
623
624/// Document the standard library using `build_compiler`.
625#[derive(Debug, Clone, Hash, PartialEq, Eq)]
626pub struct Std {
627    build_compiler: Compiler,
628    target: TargetSelection,
629    format: DocumentationFormat,
630    crates: Vec<String>,
631}
632
633impl Std {
634    pub(crate) fn from_build_compiler(
635        build_compiler: Compiler,
636        target: TargetSelection,
637        format: DocumentationFormat,
638    ) -> Self {
639        Std { build_compiler, target, format, crates: vec![] }
640    }
641}
642
643impl Step for Std {
644    /// Path to a directory with the built documentation.
645    type Output = PathBuf;
646
647    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
648        run.crate_or_deps("sysroot").path("library")
649    }
650
651    fn is_default_step(builder: &Builder<'_>) -> bool {
652        builder.config.docs
653    }
654
655    fn make_run(run: RunConfig<'_>) {
656        let crates = compile::std_crates_for_run_make(&run);
657        let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
658        if crates.is_empty() && target_is_no_std {
659            return;
660        }
661        run.builder.ensure(Std {
662            build_compiler: run.builder.compiler_for_std(run.builder.top_stage),
663            target: run.target,
664            format: if run.builder.config.cmd.json() {
665                DocumentationFormat::Json
666            } else {
667                DocumentationFormat::Html
668            },
669            crates,
670        });
671    }
672
673    /// Compile all standard library documentation.
674    ///
675    /// This will generate all documentation for the standard library and its
676    /// dependencies. This is largely just a wrapper around `cargo doc`.
677    fn run(self, builder: &Builder<'_>) -> Self::Output {
678        let target = self.target;
679        let crates = if self.crates.is_empty() {
680            builder
681                .in_tree_crates("sysroot", Some(target))
682                .iter()
683                .map(|c| c.name.to_string())
684                .collect()
685        } else {
686            self.crates
687        };
688
689        let out = match self.format {
690            DocumentationFormat::Html => builder.doc_out(target),
691            DocumentationFormat::Json => builder.json_doc_out(target),
692        };
693
694        t!(fs::create_dir_all(&out));
695
696        if self.format == DocumentationFormat::Html {
697            builder.ensure(SharedAssets { target: self.target });
698        }
699
700        let index_page = builder
701            .src
702            .join("src/doc/index.md")
703            .into_os_string()
704            .into_string()
705            .expect("non-utf8 paths are unsupported");
706        let mut extra_args = match self.format {
707            DocumentationFormat::Html => {
708                vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page]
709            }
710            DocumentationFormat::Json => vec!["--output-format", "json"],
711        };
712
713        if !builder.config.docs_minification {
714            extra_args.push("--disable-minification");
715        }
716        // For `--index-page` and `--output-format=json`.
717        extra_args.push("-Zunstable-options");
718
719        doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates);
720
721        // Open if the format is HTML
722        if let DocumentationFormat::Html = self.format {
723            if builder.paths.iter().any(|path| path.ends_with("library")) {
724                // For `x.py doc library --open`, open `std` by default.
725                let index = out.join("std").join("index.html");
726                builder.maybe_open_in_browser::<Self>(index);
727            } else {
728                for requested_crate in crates {
729                    if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
730                        let index = out.join(requested_crate).join("index.html");
731                        builder.maybe_open_in_browser::<Self>(index);
732                        break;
733                    }
734                }
735            }
736        }
737
738        out
739    }
740
741    fn metadata(&self) -> Option<StepMetadata> {
742        Some(
743            StepMetadata::doc("std", self.target)
744                .built_by(self.build_compiler)
745                .with_metadata(format!("crates=[{}]", self.crates.join(","))),
746        )
747    }
748}
749
750/// Name of the crates that are visible to consumers of the standard library.
751/// Documentation for internal crates is handled by the rustc step, so internal crates will show
752/// up there.
753///
754/// Order here is important!
755/// Crates need to be processed starting from the leaves, otherwise rustdoc will not
756/// create correct links between crates because rustdoc depends on the
757/// existence of the output directories to know if it should be a local
758/// or remote link.
759const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
760
761#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
762pub enum DocumentationFormat {
763    Html,
764    Json,
765}
766
767impl DocumentationFormat {
768    fn as_str(&self) -> &str {
769        match self {
770            DocumentationFormat::Html => "HTML",
771            DocumentationFormat::Json => "JSON",
772        }
773    }
774}
775
776/// Build the documentation for public standard library crates.
777fn doc_std(
778    builder: &Builder<'_>,
779    format: DocumentationFormat,
780    build_compiler: Compiler,
781    target: TargetSelection,
782    out: &Path,
783    extra_args: &[&str],
784    requested_crates: &[String],
785) {
786    let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" };
787    let target_dir =
788        builder.stage_out(build_compiler, Mode::Std).join(target).join(target_doc_dir_name);
789
790    // This is directory where the compiler will place the output of the command.
791    // We will then copy the files from this directory into the final `out` directory, the specified
792    // as a function parameter.
793    let out_dir = target_dir.join(target).join("doc");
794
795    let mut cargo = builder::Cargo::new(
796        builder,
797        build_compiler,
798        Mode::Std,
799        SourceType::InTree,
800        target,
801        Kind::Doc,
802    );
803
804    compile::std_cargo(builder, target, &mut cargo, requested_crates);
805    cargo
806        .arg("--no-deps")
807        .arg("--target-dir")
808        .arg(&*target_dir.to_string_lossy())
809        .arg("-Zskip-rustdoc-fingerprint")
810        .arg("-Zrustdoc-map")
811        .rustdocflag("--extern-html-root-url")
812        .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
813        .rustdocflag("--extern-html-root-takes-precedence")
814        .rustdocflag("--resource-suffix")
815        .rustdocflag(&builder.version);
816    for arg in extra_args {
817        cargo.rustdocflag(arg);
818    }
819
820    if builder.config.library_docs_private_items {
821        cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items");
822    }
823
824    let description =
825        format!("library{} in {} format", crate_description(requested_crates), format.as_str());
826    let _guard = builder.msg(Kind::Doc, description, Mode::Std, build_compiler, target);
827
828    cargo.into_cmd().run(builder);
829    builder.cp_link_r(&out_dir, out);
830}
831
832/// Prepare a compiler that will be able to document something for `target` at `stage`.
833pub fn prepare_doc_compiler(
834    builder: &Builder<'_>,
835    target: TargetSelection,
836    stage: u32,
837) -> Compiler {
838    assert!(stage > 0, "Cannot document anything in stage 0");
839    let build_compiler = builder.compiler(stage - 1, builder.host_target);
840    builder.std(build_compiler, target);
841    build_compiler
842}
843
844/// Document the compiler for the given `target` using rustdoc from `build_compiler`.
845#[derive(Debug, Clone, Hash, PartialEq, Eq)]
846pub struct Rustc {
847    build_compiler: Compiler,
848    target: TargetSelection,
849    crates: Vec<String>,
850}
851
852impl Rustc {
853    /// Document `stage` compiler for the given `target`.
854    pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self {
855        let build_compiler = prepare_doc_compiler(builder, target, stage);
856        Self::from_build_compiler(builder, build_compiler, target)
857    }
858
859    fn from_build_compiler(
860        builder: &Builder<'_>,
861        build_compiler: Compiler,
862        target: TargetSelection,
863    ) -> Self {
864        let crates = builder
865            .in_tree_crates("rustc-main", Some(target))
866            .into_iter()
867            .map(|krate| krate.name.to_string())
868            .collect();
869        Self { build_compiler, target, crates }
870    }
871}
872
873impl Step for Rustc {
874    type Output = ();
875    const IS_HOST: bool = true;
876
877    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
878        run.crate_or_deps("rustc-main").path("compiler")
879    }
880
881    fn is_default_step(builder: &Builder<'_>) -> bool {
882        builder.config.compiler_docs
883    }
884
885    fn make_run(run: RunConfig<'_>) {
886        run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target));
887    }
888
889    /// Generates compiler documentation.
890    ///
891    /// This will generate all documentation for compiler and dependencies.
892    /// Compiler documentation is distributed separately, so we make sure
893    /// we do not merge it with the other documentation from std, test and
894    /// proc_macros. This is largely just a wrapper around `cargo doc`.
895    fn run(self, builder: &Builder<'_>) {
896        let target = self.target;
897
898        // This is the intended out directory for compiler documentation.
899        let out = builder.compiler_doc_out(target);
900        t!(fs::create_dir_all(&out));
901
902        // Build the standard library, so that proc-macros can use it.
903        // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.)
904        let build_compiler = self.build_compiler;
905        builder.std(build_compiler, builder.config.host_target);
906
907        let _guard = builder.msg(
908            Kind::Doc,
909            format!("compiler{}", crate_description(&self.crates)),
910            Mode::Rustc,
911            build_compiler,
912            target,
913        );
914
915        // Build cargo command.
916        let mut cargo = builder::Cargo::new(
917            builder,
918            build_compiler,
919            Mode::Rustc,
920            SourceType::InTree,
921            target,
922            Kind::Doc,
923        );
924
925        cargo.rustdocflag("--document-private-items");
926        // Since we always pass --document-private-items, there's no need to warn about linking to private items.
927        cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
928        cargo.rustdocflag("--enable-index-page");
929        cargo.rustdocflag("-Znormalize-docs");
930        cargo.rustdocflag("--show-type-layout");
931        // FIXME: `--generate-link-to-definition` tries to resolve cfged out code
932        // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
933        // If there is any bug, please comment out the next line.
934        cargo.rustdocflag("--generate-link-to-definition");
935        // FIXME: Currently, `--generate-macro-expansion` option is buggy in `beta` rustdoc. To
936        // allow CI to pass, we only enable the option in stage 2 and higher.
937        // cfg(bootstrap)
938        // ^ Adding this so it's not forgotten when the new release is done.
939        if builder.top_stage > 1 {
940            cargo.rustdocflag("--generate-macro-expansion");
941        }
942
943        compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
944        cargo.arg("-Zskip-rustdoc-fingerprint");
945
946        // Only include compiler crates, no dependencies of those, such as `libc`.
947        // Do link to dependencies on `docs.rs` however using `rustdoc-map`.
948        cargo.arg("--no-deps");
949        cargo.arg("-Zrustdoc-map");
950
951        // FIXME: `-Zrustdoc-map` does not yet correctly work for transitive dependencies,
952        // once this is no longer an issue the special case for `ena` can be removed.
953        cargo.rustdocflag("--extern-html-root-url");
954        cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
955
956        let mut to_open = None;
957
958        let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
959        for krate in &*self.crates {
960            // Create all crate output directories first to make sure rustdoc uses
961            // relative links.
962            // FIXME: Cargo should probably do this itself.
963            let dir_name = krate.replace('-', "_");
964            t!(fs::create_dir_all(out_dir.join(&*dir_name)));
965            cargo.arg("-p").arg(krate);
966            if to_open.is_none() {
967                to_open = Some(dir_name);
968            }
969        }
970
971        // This uses a shared directory so that librustdoc documentation gets
972        // correctly built and merged with the rustc documentation.
973        //
974        // This is needed because rustdoc is built in a different directory from
975        // rustc. rustdoc needs to be able to see everything, for example when
976        // merging the search index, or generating local (relative) links.
977        symlink_dir_force(&builder.config, &out, &out_dir);
978        // Cargo puts proc macros in `target/doc` even if you pass `--target`
979        // explicitly (https://github.com/rust-lang/cargo/issues/7677).
980        let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
981        symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
982
983        cargo.into_cmd().run(builder);
984
985        if !builder.config.dry_run() {
986            // Sanity check on linked compiler crates
987            for krate in &*self.crates {
988                let dir_name = krate.replace('-', "_");
989                // Making sure the directory exists and is not empty.
990                assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
991            }
992        }
993
994        if builder.paths.iter().any(|path| path.ends_with("compiler")) {
995            // For `x.py doc compiler --open`, open `rustc_middle` by default.
996            let index = out.join("rustc_middle").join("index.html");
997            builder.open_in_browser(index);
998        } else if let Some(krate) = to_open {
999            // Let's open the first crate documentation page:
1000            let index = out.join(krate).join("index.html");
1001            builder.open_in_browser(index);
1002        }
1003    }
1004
1005    fn metadata(&self) -> Option<StepMetadata> {
1006        Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
1007    }
1008}
1009
1010macro_rules! tool_doc {
1011    (
1012        $tool: ident,
1013        $path: literal,
1014        mode = $mode:expr
1015        $(, is_library = $is_library:expr )?
1016        $(, crates = $crates:expr )?
1017        // Subset of nightly features that are allowed to be used when documenting
1018        $(, allow_features: $allow_features:expr )?
1019       ) => {
1020        #[derive(Debug, Clone, Hash, PartialEq, Eq)]
1021        pub struct $tool {
1022            build_compiler: Compiler,
1023            mode: Mode,
1024            target: TargetSelection,
1025        }
1026
1027        impl Step for $tool {
1028            type Output = ();
1029            const IS_HOST: bool = true;
1030
1031            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1032                run.path($path)
1033            }
1034
1035            fn is_default_step(builder: &Builder<'_>) -> bool {
1036                builder.config.compiler_docs
1037            }
1038
1039            fn make_run(run: RunConfig<'_>) {
1040                let target = run.target;
1041                let build_compiler = match $mode {
1042                    Mode::ToolRustcPrivate => {
1043                        // Rustdoc needs the rustc sysroot available to build.
1044                        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
1045
1046                        // Build rustc docs so that we generate relative links.
1047                        run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
1048                        compilers.build_compiler()
1049                    }
1050                    Mode::ToolTarget => {
1051                        // when shipping multiple docs together in one folder,
1052                        // they all need to use the same rustdoc version
1053                        prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1054                    }
1055                    _ => {
1056                        panic!("Unexpected tool mode for documenting: {:?}", $mode);
1057                    }
1058                };
1059
1060                run.builder.ensure($tool { build_compiler, mode: $mode, target });
1061            }
1062
1063            /// Generates documentation for a tool.
1064            ///
1065            /// This is largely just a wrapper around `cargo doc`.
1066            fn run(self, builder: &Builder<'_>) {
1067                let mut source_type = SourceType::InTree;
1068
1069                if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1070                    source_type = SourceType::Submodule;
1071                    builder.require_submodule(&submodule_path, None);
1072                }
1073
1074                let $tool { build_compiler, mode, target } = self;
1075
1076                // This is the intended out directory for compiler documentation.
1077                let out = builder.compiler_doc_out(target);
1078                t!(fs::create_dir_all(&out));
1079
1080                // Build cargo command.
1081                let mut cargo = prepare_tool_cargo(
1082                    builder,
1083                    build_compiler,
1084                    mode,
1085                    target,
1086                    Kind::Doc,
1087                    $path,
1088                    source_type,
1089                    &[],
1090                );
1091                let allow_features = {
1092                    let mut _value = "";
1093                    $( _value = $allow_features; )?
1094                    _value
1095                };
1096
1097                if !allow_features.is_empty() {
1098                    cargo.allow_features(allow_features);
1099                }
1100
1101                cargo.arg("-Zskip-rustdoc-fingerprint");
1102                // Only include compiler crates, no dependencies of those, such as `libc`.
1103                cargo.arg("--no-deps");
1104
1105                if false $(|| $is_library)? {
1106                    cargo.arg("--lib");
1107                }
1108
1109                $(for krate in $crates {
1110                    cargo.arg("-p").arg(krate);
1111                })?
1112
1113                cargo.rustdocflag("--document-private-items");
1114                // Since we always pass --document-private-items, there's no need to warn about linking to private items.
1115                cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1116                cargo.rustdocflag("--enable-index-page");
1117                cargo.rustdocflag("--show-type-layout");
1118                cargo.rustdocflag("--generate-link-to-definition");
1119
1120                let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1121                $(for krate in $crates {
1122                    let dir_name = krate.replace("-", "_");
1123                    t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1124                })?
1125
1126                // Symlink compiler docs to the output directory of rustdoc documentation.
1127                symlink_dir_force(&builder.config, &out, &out_dir);
1128                let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1129                symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1130
1131                let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1132                cargo.into_cmd().run(builder);
1133
1134                if !builder.config.dry_run() {
1135                    // Sanity check on linked doc directories
1136                    $(for krate in $crates {
1137                        let dir_name = krate.replace("-", "_");
1138                        // Making sure the directory exists and is not empty.
1139                        assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1140                    })?
1141                }
1142            }
1143
1144            fn metadata(&self) -> Option<StepMetadata> {
1145                Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1146            }
1147        }
1148    }
1149}
1150
1151// NOTE: make sure to register these in `Builder::get_step_description`.
1152tool_doc!(
1153    BuildHelper,
1154    "src/build_helper",
1155    // ideally, this would use ToolBootstrap,
1156    // but we distribute these docs together in the same folder
1157    // as a bunch of stage1 tools, and you can't mix rustdoc versions
1158    // because that breaks cross-crate data (particularly search)
1159    mode = Mode::ToolTarget,
1160    is_library = true,
1161    crates = ["build_helper"]
1162);
1163tool_doc!(
1164    Rustdoc,
1165    "src/tools/rustdoc",
1166    mode = Mode::ToolRustcPrivate,
1167    crates = ["rustdoc", "rustdoc-json-types"]
1168);
1169tool_doc!(
1170    Rustfmt,
1171    "src/tools/rustfmt",
1172    mode = Mode::ToolRustcPrivate,
1173    crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1174);
1175tool_doc!(
1176    Clippy,
1177    "src/tools/clippy",
1178    mode = Mode::ToolRustcPrivate,
1179    crates = ["clippy_config", "clippy_utils"]
1180);
1181tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1182tool_doc!(
1183    Cargo,
1184    "src/tools/cargo",
1185    mode = Mode::ToolTarget,
1186    crates = [
1187        "cargo",
1188        "cargo-credential",
1189        "cargo-platform",
1190        "cargo-test-macro",
1191        "cargo-test-support",
1192        "cargo-util",
1193        "cargo-util-schemas",
1194        "crates-io",
1195        "mdman",
1196        "rustfix",
1197    ],
1198    // Required because of the im-rc dependency of Cargo, which automatically opts into the
1199    // "specialization" feature in its build script when it detects a nightly toolchain.
1200    allow_features: "specialization"
1201);
1202tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
1203tool_doc!(
1204    Bootstrap,
1205    "src/bootstrap",
1206    mode = Mode::ToolTarget,
1207    is_library = true,
1208    crates = ["bootstrap"]
1209);
1210tool_doc!(
1211    RunMakeSupport,
1212    "src/tools/run-make-support",
1213    mode = Mode::ToolTarget,
1214    is_library = true,
1215    crates = ["run_make_support"]
1216);
1217tool_doc!(
1218    Compiletest,
1219    "src/tools/compiletest",
1220    mode = Mode::ToolTarget,
1221    is_library = true,
1222    crates = ["compiletest"]
1223);
1224
1225#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1226pub struct ErrorIndex {
1227    compilers: RustcPrivateCompilers,
1228}
1229
1230impl Step for ErrorIndex {
1231    type Output = ();
1232    const IS_HOST: bool = true;
1233
1234    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1235        run.path("src/tools/error_index_generator")
1236    }
1237
1238    fn is_default_step(builder: &Builder<'_>) -> bool {
1239        builder.config.docs
1240    }
1241
1242    fn make_run(run: RunConfig<'_>) {
1243        run.builder.ensure(ErrorIndex {
1244            compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1245        });
1246    }
1247
1248    /// Generates the HTML rendered error-index by running the
1249    /// `error_index_generator` tool.
1250    fn run(self, builder: &Builder<'_>) {
1251        builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1252        let out = builder.doc_out(self.compilers.target());
1253        t!(fs::create_dir_all(&out));
1254        tool::ErrorIndex::command(builder, self.compilers)
1255            .arg("html")
1256            .arg(&out)
1257            .arg(&builder.version)
1258            .run(builder);
1259
1260        let index = out.join("error-index.html");
1261        builder.maybe_open_in_browser::<Self>(index);
1262    }
1263
1264    fn metadata(&self) -> Option<StepMetadata> {
1265        Some(
1266            StepMetadata::doc("error-index", self.compilers.target())
1267                .built_by(self.compilers.build_compiler()),
1268        )
1269    }
1270}
1271
1272#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1273pub struct UnstableBookGen {
1274    target: TargetSelection,
1275}
1276
1277impl Step for UnstableBookGen {
1278    type Output = ();
1279    const IS_HOST: bool = true;
1280
1281    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1282        run.path("src/tools/unstable-book-gen")
1283    }
1284
1285    fn is_default_step(builder: &Builder<'_>) -> bool {
1286        builder.config.docs
1287    }
1288
1289    fn make_run(run: RunConfig<'_>) {
1290        run.builder.ensure(UnstableBookGen { target: run.target });
1291    }
1292
1293    fn run(self, builder: &Builder<'_>) {
1294        let target = self.target;
1295
1296        builder.info(&format!("Generating unstable book md files ({target})"));
1297        let out = builder.md_doc_out(target).join("unstable-book");
1298        builder.create_dir(&out);
1299        builder.remove_dir(&out);
1300        let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1301        cmd.arg(builder.src.join("library"));
1302        cmd.arg(builder.src.join("compiler"));
1303        cmd.arg(builder.src.join("src"));
1304        cmd.arg(out);
1305
1306        cmd.run(builder);
1307    }
1308}
1309
1310fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1311    if config.dry_run() {
1312        return;
1313    }
1314    if let Ok(m) = fs::symlink_metadata(link) {
1315        if m.file_type().is_dir() {
1316            t!(fs::remove_dir_all(link));
1317        } else {
1318            // handle directory junctions on windows by falling back to
1319            // `remove_dir`.
1320            t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1321        }
1322    }
1323
1324    t!(
1325        symlink_dir(config, original, link),
1326        format!("failed to create link from {} -> {}", link.display(), original.display())
1327    );
1328}
1329
1330/// Builds the Rust compiler book.
1331#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1332pub struct RustcBook {
1333    build_compiler: Compiler,
1334    target: TargetSelection,
1335    /// Test that the examples of lints in the book produce the correct lints in the expected
1336    /// format.
1337    validate: bool,
1338}
1339
1340impl RustcBook {
1341    pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1342        Self { build_compiler, target, validate: true }
1343    }
1344}
1345
1346impl Step for RustcBook {
1347    type Output = ();
1348    const IS_HOST: bool = true;
1349
1350    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1351        run.path("src/doc/rustc")
1352    }
1353
1354    fn is_default_step(builder: &Builder<'_>) -> bool {
1355        builder.config.docs
1356    }
1357
1358    fn make_run(run: RunConfig<'_>) {
1359        // Bump the stage to 2, because the rustc book requires an in-tree compiler.
1360        // At the same time, since this step is enabled by default, we don't want `x doc` to fail
1361        // in stage 1.
1362        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1363            run.builder.top_stage
1364        } else {
1365            2
1366        };
1367
1368        run.builder.ensure(RustcBook {
1369            build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1370            target: run.target,
1371            validate: false,
1372        });
1373    }
1374
1375    /// Builds the rustc book.
1376    ///
1377    /// The lints are auto-generated by a tool, and then merged into the book
1378    /// in the "md-doc" directory in the build output directory. Then
1379    /// "rustbook" is used to convert it to HTML.
1380    fn run(self, builder: &Builder<'_>) {
1381        let out_base = builder.md_doc_out(self.target).join("rustc");
1382        t!(fs::create_dir_all(&out_base));
1383        let out_listing = out_base.join("src/lints");
1384        builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1385        builder.info(&format!("Generating lint docs ({})", self.target));
1386
1387        let rustc = builder.rustc(self.build_compiler);
1388        // The tool runs `rustc` for extracting output examples, so it needs a
1389        // functional sysroot.
1390        builder.std(self.build_compiler, self.target);
1391        let mut cmd = builder.tool_cmd(Tool::LintDocs);
1392        cmd.arg("--build-rustc-stage");
1393        cmd.arg(self.build_compiler.stage.to_string());
1394        cmd.arg("--src");
1395        cmd.arg(builder.src.join("compiler"));
1396        cmd.arg("--out");
1397        cmd.arg(&out_listing);
1398        cmd.arg("--rustc");
1399        cmd.arg(&rustc);
1400        cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1401        if let Some(target_linker) = builder.linker(self.target) {
1402            cmd.arg("--rustc-linker").arg(target_linker);
1403        }
1404        if builder.is_verbose() {
1405            cmd.arg("--verbose");
1406        }
1407        if self.validate {
1408            cmd.arg("--validate");
1409        }
1410        // We need to validate nightly features, even on the stable channel.
1411        // Set this unconditionally as the stage0 compiler may be being used to
1412        // document.
1413        cmd.env("RUSTC_BOOTSTRAP", "1");
1414
1415        // If the lib directories are in an unusual location (changed in
1416        // bootstrap.toml), then this needs to explicitly update the dylib search
1417        // path.
1418        builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1419        let doc_generator_guard =
1420            builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1421        cmd.run(builder);
1422        drop(doc_generator_guard);
1423
1424        // Run rustbook/mdbook to generate the HTML pages.
1425        builder.ensure(RustbookSrc {
1426            target: self.target,
1427            name: "rustc".to_owned(),
1428            src: out_base,
1429            parent: Some(self),
1430            languages: vec![],
1431            build_compiler: None,
1432        });
1433    }
1434}
1435
1436/// Documents the reference.
1437/// It has to always be done using a stage 1+ compiler, because it references in-tree
1438/// compiler/stdlib concepts.
1439#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1440pub struct Reference {
1441    build_compiler: Compiler,
1442    target: TargetSelection,
1443}
1444
1445impl Step for Reference {
1446    type Output = ();
1447
1448    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1449        run.path("src/doc/reference")
1450    }
1451
1452    fn is_default_step(builder: &Builder<'_>) -> bool {
1453        builder.config.docs
1454    }
1455
1456    fn make_run(run: RunConfig<'_>) {
1457        // Bump the stage to 2, because the reference requires an in-tree compiler.
1458        // At the same time, since this step is enabled by default, we don't want `x doc` to fail
1459        // in stage 1.
1460        // FIXME: create a shared method on builder for auto-bumping, and print some warning when
1461        // it happens.
1462        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1463            run.builder.top_stage
1464        } else {
1465            2
1466        };
1467
1468        run.builder.ensure(Reference {
1469            build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1470            target: run.target,
1471        });
1472    }
1473
1474    /// Builds the reference book.
1475    fn run(self, builder: &Builder<'_>) {
1476        builder.require_submodule("src/doc/reference", None);
1477
1478        // This is needed for generating links to the standard library using
1479        // the mdbook-spec plugin.
1480        builder.std(self.build_compiler, builder.config.host_target);
1481
1482        // Run rustbook/mdbook to generate the HTML pages.
1483        builder.ensure(RustbookSrc {
1484            target: self.target,
1485            name: "reference".to_owned(),
1486            src: builder.src.join("src/doc/reference"),
1487            build_compiler: Some(self.build_compiler),
1488            parent: Some(self),
1489            languages: vec![],
1490        });
1491    }
1492}