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