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        // Point std library crate links to local docs for offline usage.
957        for krate in STD_PUBLIC_CRATES {
958            cargo.rustdocflag("--extern-html-root-url");
959            cargo.rustdocflag(&format!("{krate}=../"));
960        }
961        cargo.rustdocflag("--extern-html-root-takes-precedence");
962
963        let mut to_open = None;
964
965        let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc");
966        for krate in &*self.crates {
967            // Create all crate output directories first to make sure rustdoc uses
968            // relative links.
969            // FIXME: Cargo should probably do this itself.
970            let dir_name = krate.replace('-', "_");
971            t!(fs::create_dir_all(out_dir.join(&*dir_name)));
972            cargo.arg("-p").arg(krate);
973            if to_open.is_none() {
974                to_open = Some(dir_name);
975            }
976        }
977
978        // This uses a shared directory so that librustdoc documentation gets
979        // correctly built and merged with the rustc documentation.
980        //
981        // This is needed because rustdoc is built in a different directory from
982        // rustc. rustdoc needs to be able to see everything, for example when
983        // merging the search index, or generating local (relative) links.
984        symlink_dir_force(&builder.config, &out, &out_dir);
985        // Cargo puts proc macros in `target/doc` even if you pass `--target`
986        // explicitly (https://github.com/rust-lang/cargo/issues/7677).
987        let proc_macro_out_dir = builder.stage_out(build_compiler, Mode::Rustc).join("doc");
988        symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
989
990        cargo.into_cmd().run(builder);
991
992        if !builder.config.dry_run() {
993            // Sanity check on linked compiler crates
994            for krate in &*self.crates {
995                let dir_name = krate.replace('-', "_");
996                // Making sure the directory exists and is not empty.
997                assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
998            }
999        }
1000
1001        if builder.paths.iter().any(|path| path.ends_with("compiler")) {
1002            // For `x.py doc compiler --open`, open `rustc_middle` by default.
1003            let index = out.join("rustc_middle").join("index.html");
1004            builder.open_in_browser(index);
1005        } else if let Some(krate) = to_open {
1006            // Let's open the first crate documentation page:
1007            let index = out.join(krate).join("index.html");
1008            builder.open_in_browser(index);
1009        }
1010    }
1011
1012    fn metadata(&self) -> Option<StepMetadata> {
1013        Some(StepMetadata::doc("rustc", self.target).built_by(self.build_compiler))
1014    }
1015}
1016
1017macro_rules! tool_doc {
1018    (
1019        $tool: ident,
1020        $path: literal,
1021        mode = $mode:expr
1022        $(, is_library = $is_library:expr )?
1023        $(, crates = $crates:expr )?
1024        // Subset of nightly features that are allowed to be used when documenting
1025        $(, allow_features: $allow_features:expr )?
1026       ) => {
1027        #[derive(Debug, Clone, Hash, PartialEq, Eq)]
1028        pub struct $tool {
1029            build_compiler: Compiler,
1030            mode: Mode,
1031            target: TargetSelection,
1032        }
1033
1034        impl Step for $tool {
1035            type Output = ();
1036            const IS_HOST: bool = true;
1037
1038            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1039                run.path($path)
1040            }
1041
1042            fn is_default_step(builder: &Builder<'_>) -> bool {
1043                builder.config.compiler_docs
1044            }
1045
1046            fn make_run(run: RunConfig<'_>) {
1047                let target = run.target;
1048                let build_compiler = match $mode {
1049                    Mode::ToolRustcPrivate => {
1050                        // Rustdoc needs the rustc sysroot available to build.
1051                        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target);
1052
1053                        // Build rustc docs so that we generate relative links.
1054                        run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target));
1055                        compilers.build_compiler()
1056                    }
1057                    Mode::ToolTarget => {
1058                        // when shipping multiple docs together in one folder,
1059                        // they all need to use the same rustdoc version
1060                        prepare_doc_compiler(run.builder, run.builder.host_target, run.builder.top_stage)
1061                    }
1062                    _ => {
1063                        panic!("Unexpected tool mode for documenting: {:?}", $mode);
1064                    }
1065                };
1066
1067                run.builder.ensure($tool { build_compiler, mode: $mode, target });
1068            }
1069
1070            /// Generates documentation for a tool.
1071            ///
1072            /// This is largely just a wrapper around `cargo doc`.
1073            fn run(self, builder: &Builder<'_>) {
1074                let mut source_type = SourceType::InTree;
1075
1076                if let Some(submodule_path) = submodule_path_of(&builder, $path) {
1077                    source_type = SourceType::Submodule;
1078                    builder.require_submodule(&submodule_path, None);
1079                }
1080
1081                let $tool { build_compiler, mode, target } = self;
1082
1083                // This is the intended out directory for compiler documentation.
1084                let out = builder.compiler_doc_out(target);
1085                t!(fs::create_dir_all(&out));
1086
1087                // Build cargo command.
1088                let mut cargo = prepare_tool_cargo(
1089                    builder,
1090                    build_compiler,
1091                    mode,
1092                    target,
1093                    Kind::Doc,
1094                    $path,
1095                    source_type,
1096                    &[],
1097                );
1098                let allow_features = {
1099                    let mut _value = "";
1100                    $( _value = $allow_features; )?
1101                    _value
1102                };
1103
1104                if !allow_features.is_empty() {
1105                    cargo.allow_features(allow_features);
1106                }
1107
1108                cargo.arg("-Zskip-rustdoc-fingerprint");
1109                // Only include compiler crates, no dependencies of those, such as `libc`.
1110                cargo.arg("--no-deps");
1111
1112                if false $(|| $is_library)? {
1113                    cargo.arg("--lib");
1114                }
1115
1116                $(for krate in $crates {
1117                    cargo.arg("-p").arg(krate);
1118                })?
1119
1120                cargo.rustdocflag("--document-private-items");
1121                // Since we always pass --document-private-items, there's no need to warn about linking to private items.
1122                cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
1123                cargo.rustdocflag("--enable-index-page");
1124                cargo.rustdocflag("--show-type-layout");
1125                cargo.rustdocflag("--generate-link-to-definition");
1126
1127                let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc");
1128                $(for krate in $crates {
1129                    let dir_name = krate.replace("-", "_");
1130                    t!(fs::create_dir_all(out_dir.join(&*dir_name)));
1131                })?
1132
1133                // Symlink compiler docs to the output directory of rustdoc documentation.
1134                symlink_dir_force(&builder.config, &out, &out_dir);
1135                let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc");
1136                symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
1137
1138                let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target);
1139                cargo.into_cmd().run(builder);
1140
1141                if !builder.config.dry_run() {
1142                    // Sanity check on linked doc directories
1143                    $(for krate in $crates {
1144                        let dir_name = krate.replace("-", "_");
1145                        // Making sure the directory exists and is not empty.
1146                        assert!(out.join(&*dir_name).read_dir().unwrap().next().is_some());
1147                    })?
1148                }
1149            }
1150
1151            fn metadata(&self) -> Option<StepMetadata> {
1152                Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler))
1153            }
1154        }
1155    }
1156}
1157
1158// NOTE: make sure to register these in `Builder::get_step_description`.
1159tool_doc!(
1160    BuildHelper,
1161    "src/build_helper",
1162    // ideally, this would use ToolBootstrap,
1163    // but we distribute these docs together in the same folder
1164    // as a bunch of stage1 tools, and you can't mix rustdoc versions
1165    // because that breaks cross-crate data (particularly search)
1166    mode = Mode::ToolTarget,
1167    is_library = true,
1168    crates = ["build_helper"]
1169);
1170tool_doc!(
1171    Rustdoc,
1172    "src/tools/rustdoc",
1173    mode = Mode::ToolRustcPrivate,
1174    crates = ["rustdoc", "rustdoc-json-types"]
1175);
1176tool_doc!(
1177    Rustfmt,
1178    "src/tools/rustfmt",
1179    mode = Mode::ToolRustcPrivate,
1180    crates = ["rustfmt-nightly", "rustfmt-config_proc_macro"]
1181);
1182tool_doc!(
1183    Clippy,
1184    "src/tools/clippy",
1185    mode = Mode::ToolRustcPrivate,
1186    crates = ["clippy_config", "clippy_utils"]
1187);
1188tool_doc!(Miri, "src/tools/miri", mode = Mode::ToolRustcPrivate, crates = ["miri"]);
1189tool_doc!(
1190    Cargo,
1191    "src/tools/cargo",
1192    mode = Mode::ToolTarget,
1193    crates = [
1194        "cargo",
1195        "cargo-credential",
1196        "cargo-platform",
1197        "cargo-test-macro",
1198        "cargo-test-support",
1199        "cargo-util",
1200        "cargo-util-schemas",
1201        "crates-io",
1202        "mdman",
1203        "rustfix",
1204    ],
1205    // Required because of the im-rc dependency of Cargo, which automatically opts into the
1206    // "specialization" feature in its build script when it detects a nightly toolchain.
1207    allow_features: "specialization"
1208);
1209tool_doc!(Tidy, "src/tools/tidy", mode = Mode::ToolTarget, crates = ["tidy"]);
1210tool_doc!(
1211    Bootstrap,
1212    "src/bootstrap",
1213    mode = Mode::ToolTarget,
1214    is_library = true,
1215    crates = ["bootstrap"]
1216);
1217tool_doc!(
1218    RunMakeSupport,
1219    "src/tools/run-make-support",
1220    mode = Mode::ToolTarget,
1221    is_library = true,
1222    crates = ["run_make_support"]
1223);
1224tool_doc!(
1225    Compiletest,
1226    "src/tools/compiletest",
1227    mode = Mode::ToolTarget,
1228    is_library = true,
1229    crates = ["compiletest"]
1230);
1231
1232#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1233pub struct ErrorIndex {
1234    compilers: RustcPrivateCompilers,
1235}
1236
1237impl Step for ErrorIndex {
1238    type Output = ();
1239    const IS_HOST: bool = true;
1240
1241    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1242        run.path("src/tools/error_index_generator")
1243    }
1244
1245    fn is_default_step(builder: &Builder<'_>) -> bool {
1246        builder.config.docs
1247    }
1248
1249    fn make_run(run: RunConfig<'_>) {
1250        run.builder.ensure(ErrorIndex {
1251            compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1252        });
1253    }
1254
1255    /// Generates the HTML rendered error-index by running the
1256    /// `error_index_generator` tool.
1257    fn run(self, builder: &Builder<'_>) {
1258        builder.info(&format!("Documenting error index ({})", self.compilers.target()));
1259        let out = builder.doc_out(self.compilers.target());
1260        t!(fs::create_dir_all(&out));
1261        tool::ErrorIndex::command(builder, self.compilers)
1262            .arg("html")
1263            .arg(&out)
1264            .arg(&builder.version)
1265            .run(builder);
1266
1267        let index = out.join("error-index.html");
1268        builder.maybe_open_in_browser::<Self>(index);
1269    }
1270
1271    fn metadata(&self) -> Option<StepMetadata> {
1272        Some(
1273            StepMetadata::doc("error-index", self.compilers.target())
1274                .built_by(self.compilers.build_compiler()),
1275        )
1276    }
1277}
1278
1279#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1280pub struct UnstableBookGen {
1281    target: TargetSelection,
1282}
1283
1284impl Step for UnstableBookGen {
1285    type Output = ();
1286    const IS_HOST: bool = true;
1287
1288    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1289        run.path("src/tools/unstable-book-gen")
1290    }
1291
1292    fn is_default_step(builder: &Builder<'_>) -> bool {
1293        builder.config.docs
1294    }
1295
1296    fn make_run(run: RunConfig<'_>) {
1297        run.builder.ensure(UnstableBookGen { target: run.target });
1298    }
1299
1300    fn run(self, builder: &Builder<'_>) {
1301        let target = self.target;
1302
1303        builder.info(&format!("Generating unstable book md files ({target})"));
1304        let out = builder.md_doc_out(target).join("unstable-book");
1305        builder.create_dir(&out);
1306        builder.remove_dir(&out);
1307        let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
1308        cmd.arg(builder.src.join("library"));
1309        cmd.arg(builder.src.join("compiler"));
1310        cmd.arg(builder.src.join("src"));
1311        cmd.arg(out);
1312
1313        cmd.run(builder);
1314    }
1315}
1316
1317fn symlink_dir_force(config: &Config, original: &Path, link: &Path) {
1318    if config.dry_run() {
1319        return;
1320    }
1321    if let Ok(m) = fs::symlink_metadata(link) {
1322        if m.file_type().is_dir() {
1323            t!(fs::remove_dir_all(link));
1324        } else {
1325            // handle directory junctions on windows by falling back to
1326            // `remove_dir`.
1327            t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link)));
1328        }
1329    }
1330
1331    t!(
1332        symlink_dir(config, original, link),
1333        format!("failed to create link from {} -> {}", link.display(), original.display())
1334    );
1335}
1336
1337/// Builds the Rust compiler book.
1338#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1339pub struct RustcBook {
1340    build_compiler: Compiler,
1341    target: TargetSelection,
1342    /// Test that the examples of lints in the book produce the correct lints in the expected
1343    /// format.
1344    validate: bool,
1345}
1346
1347impl RustcBook {
1348    pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self {
1349        Self { build_compiler, target, validate: true }
1350    }
1351}
1352
1353impl Step for RustcBook {
1354    type Output = ();
1355    const IS_HOST: bool = true;
1356
1357    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1358        run.path("src/doc/rustc")
1359    }
1360
1361    fn is_default_step(builder: &Builder<'_>) -> bool {
1362        builder.config.docs
1363    }
1364
1365    fn make_run(run: RunConfig<'_>) {
1366        // Bump the stage to 2, because the rustc book requires an in-tree compiler.
1367        // At the same time, since this step is enabled by default, we don't want `x doc` to fail
1368        // in stage 1.
1369        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1370            run.builder.top_stage
1371        } else {
1372            2
1373        };
1374
1375        run.builder.ensure(RustcBook {
1376            build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1377            target: run.target,
1378            validate: false,
1379        });
1380    }
1381
1382    /// Builds the rustc book.
1383    ///
1384    /// The lints are auto-generated by a tool, and then merged into the book
1385    /// in the "md-doc" directory in the build output directory. Then
1386    /// "rustbook" is used to convert it to HTML.
1387    fn run(self, builder: &Builder<'_>) {
1388        let out_base = builder.md_doc_out(self.target).join("rustc");
1389        t!(fs::create_dir_all(&out_base));
1390        let out_listing = out_base.join("src/lints");
1391        builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base);
1392        builder.info(&format!("Generating lint docs ({})", self.target));
1393
1394        let rustc = builder.rustc(self.build_compiler);
1395        // The tool runs `rustc` for extracting output examples, so it needs a
1396        // functional sysroot.
1397        builder.std(self.build_compiler, self.target);
1398        let mut cmd = builder.tool_cmd(Tool::LintDocs);
1399        cmd.arg("--build-rustc-stage");
1400        cmd.arg(self.build_compiler.stage.to_string());
1401        cmd.arg("--src");
1402        cmd.arg(builder.src.join("compiler"));
1403        cmd.arg("--out");
1404        cmd.arg(&out_listing);
1405        cmd.arg("--rustc");
1406        cmd.arg(&rustc);
1407        cmd.arg("--rustc-target").arg(self.target.rustc_target_arg());
1408        if let Some(target_linker) = builder.linker(self.target) {
1409            cmd.arg("--rustc-linker").arg(target_linker);
1410        }
1411        if builder.is_verbose() {
1412            cmd.arg("--verbose");
1413        }
1414        if self.validate {
1415            cmd.arg("--validate");
1416        }
1417        // We need to validate nightly features, even on the stable channel.
1418        // Set this unconditionally as the stage0 compiler may be being used to
1419        // document.
1420        cmd.env("RUSTC_BOOTSTRAP", "1");
1421
1422        // If the lib directories are in an unusual location (changed in
1423        // bootstrap.toml), then this needs to explicitly update the dylib search
1424        // path.
1425        builder.add_rustc_lib_path(self.build_compiler, &mut cmd);
1426        let doc_generator_guard =
1427            builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target);
1428        cmd.run(builder);
1429        drop(doc_generator_guard);
1430
1431        // Run rustbook/mdbook to generate the HTML pages.
1432        builder.ensure(RustbookSrc {
1433            target: self.target,
1434            name: "rustc".to_owned(),
1435            src: out_base,
1436            parent: Some(self),
1437            languages: vec![],
1438            build_compiler: None,
1439        });
1440    }
1441}
1442
1443/// Documents the reference.
1444/// It has to always be done using a stage 1+ compiler, because it references in-tree
1445/// compiler/stdlib concepts.
1446#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1447pub struct Reference {
1448    build_compiler: Compiler,
1449    target: TargetSelection,
1450}
1451
1452impl Step for Reference {
1453    type Output = ();
1454
1455    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1456        run.path("src/doc/reference")
1457    }
1458
1459    fn is_default_step(builder: &Builder<'_>) -> bool {
1460        builder.config.docs
1461    }
1462
1463    fn make_run(run: RunConfig<'_>) {
1464        // Bump the stage to 2, because the reference requires an in-tree compiler.
1465        // At the same time, since this step is enabled by default, we don't want `x doc` to fail
1466        // in stage 1.
1467        // FIXME: create a shared method on builder for auto-bumping, and print some warning when
1468        // it happens.
1469        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 {
1470            run.builder.top_stage
1471        } else {
1472            2
1473        };
1474
1475        run.builder.ensure(Reference {
1476            build_compiler: prepare_doc_compiler(run.builder, run.target, stage),
1477            target: run.target,
1478        });
1479    }
1480
1481    /// Builds the reference book.
1482    fn run(self, builder: &Builder<'_>) {
1483        builder.require_submodule("src/doc/reference", None);
1484
1485        // This is needed for generating links to the standard library using
1486        // the mdbook-spec plugin.
1487        builder.std(self.build_compiler, builder.config.host_target);
1488
1489        // Run rustbook/mdbook to generate the HTML pages.
1490        builder.ensure(RustbookSrc {
1491            target: self.target,
1492            name: "reference".to_owned(),
1493            src: builder.src.join("src/doc/reference"),
1494            build_compiler: Some(self.build_compiler),
1495            parent: Some(self),
1496            languages: vec![],
1497        });
1498    }
1499}