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