]> git.proxmox.com Git - rustc.git/blame - src/bootstrap/doc.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / bootstrap / doc.rs
CommitLineData
83c7162d 1//! Documentation generation for rustbuilder.
a7813a04
XL
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
2c00a5a8 5//! nomicon, rust by example, standalone documentation, etc.
a7813a04
XL
6//!
7//! Everything here is basically just a shim around calling either `rustbook` or
8//! `rustdoc`.
9
2b03887a 10use std::ffi::OsStr;
0731742a 11use std::fs;
8bb4bdeb 12use std::io;
dfeec247 13use std::path::{Path, PathBuf};
7453a54e 14
9c376795 15use crate::builder::crate_description;
5099ac24 16use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
dfeec247 17use crate::cache::{Interned, INTERNER};
0731742a 18use crate::compile;
3dfed10e 19use crate::config::{Config, TargetSelection};
dfeec247 20use crate::tool::{self, prepare_tool_cargo, SourceType, Tool};
5e7ed085
FG
21use crate::util::{symlink_dir, t, up_to_date};
22use crate::Mode;
3b2f2976 23
136023e0
XL
24macro_rules! submodule_helper {
25 ($path:expr, submodule) => {
26 $path
27 };
28 ($path:expr, submodule = $submodule:literal) => {
29 $submodule
30 };
31}
32
3b2f2976 33macro_rules! book {
136023e0 34 ($($name:ident, $path:expr, $book_name:expr $(, submodule $(= $submodule:literal)? )? ;)+) => {
3b2f2976
XL
35 $(
36 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
37 pub struct $name {
3dfed10e 38 target: TargetSelection,
3b2f2976
XL
39 }
40
41 impl Step for $name {
42 type Output = ();
43 const DEFAULT: bool = true;
44
9fa01778 45 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 46 let builder = run.builder;
83c7162d 47 run.path($path).default_condition(builder.config.docs)
3b2f2976
XL
48 }
49
9fa01778 50 fn make_run(run: RunConfig<'_>) {
3b2f2976
XL
51 run.builder.ensure($name {
52 target: run.target,
53 });
54 }
55
9fa01778 56 fn run(self, builder: &Builder<'_>) {
136023e0
XL
57 $(
58 let path = Path::new(submodule_helper!( $path, submodule $( = $submodule )? ));
59 builder.update_submodule(&path);
60 )?
dc9dc135 61 builder.ensure(RustbookSrc {
3b2f2976
XL
62 target: self.target,
63 name: INTERNER.intern_str($book_name),
dfeec247 64 src: INTERNER.intern_path(builder.src.join($path)),
3b2f2976
XL
65 })
66 }
67 }
68 )+
69 }
041b39d2
XL
70}
71
9fa01778
XL
72// NOTE: When adding a book here, make sure to ALSO build the book by
73// adding a build step in `src/bootstrap/builder.rs`!
136023e0
XL
74// NOTE: Make sure to add the corresponding submodule when adding a new book.
75// FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules
76// and checking against it?).
3b2f2976 77book!(
136023e0 78 CargoBook, "src/tools/cargo/src/doc", "cargo", submodule = "src/tools/cargo";
064997fb 79 ClippyBook, "src/tools/clippy/book", "clippy";
136023e0
XL
80 EditionGuide, "src/doc/edition-guide", "edition-guide", submodule;
81 EmbeddedBook, "src/doc/embedded-book", "embedded-book", submodule;
82 Nomicon, "src/doc/nomicon", "nomicon", submodule;
83 Reference, "src/doc/reference", "reference", submodule;
84 RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule;
416331ca 85 RustdocBook, "src/doc/rustdoc", "rustdoc";
2b03887a 86 StyleGuide, "src/doc/style-guide", "style-guide";
3b2f2976
XL
87);
88
3dfed10e 89// "library/std" -> ["library", "std"]
f9f354fc
XL
90//
91// Used for deciding whether a particular step is one requested by the user on
92// the `x.py doc` command line, which determines whether `--open` will open that
93// page.
c295e0f8 94pub(crate) fn components_simplified(path: &PathBuf) -> Vec<&str> {
f9f354fc
XL
95 path.iter().map(|component| component.to_str().unwrap_or("???")).collect()
96}
97
3b2f2976
XL
98#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
99pub struct UnstableBook {
3dfed10e 100 target: TargetSelection,
3b2f2976 101}
cc61c64b 102
3b2f2976
XL
103impl Step for UnstableBook {
104 type Output = ();
105 const DEFAULT: bool = true;
cc61c64b 106
9fa01778 107 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 108 let builder = run.builder;
83c7162d 109 run.path("src/doc/unstable-book").default_condition(builder.config.docs)
3b2f2976
XL
110 }
111
9fa01778 112 fn make_run(run: RunConfig<'_>) {
dfeec247 113 run.builder.ensure(UnstableBook { target: run.target });
3b2f2976
XL
114 }
115
9fa01778 116 fn run(self, builder: &Builder<'_>) {
dfeec247 117 builder.ensure(UnstableBookGen { target: self.target });
3b2f2976
XL
118 builder.ensure(RustbookSrc {
119 target: self.target,
120 name: INTERNER.intern_str("unstable-book"),
dfeec247 121 src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
3b2f2976
XL
122 })
123 }
124}
125
126#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
127struct RustbookSrc {
3dfed10e 128 target: TargetSelection,
3b2f2976
XL
129 name: Interned<String>,
130 src: Interned<PathBuf>,
131}
132
133impl Step for RustbookSrc {
134 type Output = ();
135
9fa01778 136 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976
XL
137 run.never()
138 }
139
140 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
141 ///
142 /// This will not actually generate any documentation if the documentation has
143 /// already been generated.
9fa01778 144 fn run(self, builder: &Builder<'_>) {
3b2f2976
XL
145 let target = self.target;
146 let name = self.name;
147 let src = self.src;
83c7162d 148 let out = builder.doc_out(target);
3b2f2976
XL
149 t!(fs::create_dir_all(&out));
150
151 let out = out.join(name);
3b2f2976
XL
152 let index = out.join("index.html");
153 let rustbook = builder.tool_exe(Tool::Rustbook);
83c7162d 154 let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
487cf647 155 if builder.config.dry_run() || up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
dfeec247 156 return;
3b2f2976 157 }
83c7162d 158 builder.info(&format!("Rustbook ({}) - {}", target, name));
3b2f2976 159 let _ = fs::remove_dir_all(&out);
9fa01778 160
dfeec247 161 builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
3b2f2976
XL
162 }
163}
164
165#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
166pub struct TheBook {
167 compiler: Compiler,
3dfed10e 168 target: TargetSelection,
3b2f2976 169}
cc61c64b 170
3b2f2976
XL
171impl Step for TheBook {
172 type Output = ();
173 const DEFAULT: bool = true;
174
9fa01778 175 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 176 let builder = run.builder;
83c7162d 177 run.path("src/doc/book").default_condition(builder.config.docs)
3b2f2976
XL
178 }
179
9fa01778 180 fn make_run(run: RunConfig<'_>) {
3b2f2976 181 run.builder.ensure(TheBook {
83c7162d 182 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
3b2f2976 183 target: run.target,
3b2f2976
XL
184 });
185 }
186
9fa01778 187 /// Builds the book and associated stuff.
3b2f2976
XL
188 ///
189 /// We need to build:
190 ///
dfeec247
XL
191 /// * Book
192 /// * Older edition redirects
abe05a73 193 /// * Version info and CSS
3b2f2976
XL
194 /// * Index page
195 /// * Redirect pages
9fa01778 196 fn run(self, builder: &Builder<'_>) {
136023e0
XL
197 let relative_path = Path::new("src").join("doc").join("book");
198 builder.update_submodule(&relative_path);
199
abe05a73 200 let compiler = self.compiler;
3b2f2976 201 let target = self.target;
13cf67c4
XL
202
203 // build book
dc9dc135 204 builder.ensure(RustbookSrc {
3b2f2976 205 target,
dfeec247 206 name: INTERNER.intern_str("book"),
136023e0 207 src: INTERNER.intern_path(builder.src.join(&relative_path)),
3b2f2976
XL
208 });
209
13cf67c4 210 // building older edition redirects
dfeec247
XL
211 for edition in &["first-edition", "second-edition", "2018-edition"] {
212 builder.ensure(RustbookSrc {
213 target,
214 name: INTERNER.intern_string(format!("book/{}", edition)),
136023e0 215 src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)),
dfeec247
XL
216 });
217 }
94b46f34 218
abe05a73 219 // build the version info page and CSS
2b03887a 220 let shared_assets = builder.ensure(SharedAssets { target });
abe05a73 221
3b2f2976 222 // build the redirect pages
83c7162d 223 builder.info(&format!("Documenting book redirect pages ({})", target));
136023e0 224 for file in t!(fs::read_dir(builder.src.join(&relative_path).join("redirects"))) {
3b2f2976
XL
225 let file = t!(file);
226 let path = file.path();
227 let path = path.to_str().unwrap();
228
2b03887a 229 invoke_rustdoc(builder, compiler, &shared_assets, target, path);
3b2f2976 230 }
f9f354fc 231
487cf647
FG
232 let out = builder.doc_out(target);
233 let index = out.join("book").join("index.html");
234 builder.maybe_open_in_browser::<Self>(index);
3b2f2976
XL
235 }
236}
237
9fa01778
XL
238fn invoke_rustdoc(
239 builder: &Builder<'_>,
240 compiler: Compiler,
2b03887a 241 shared_assets: &SharedAssetsPaths,
3dfed10e 242 target: TargetSelection,
9fa01778
XL
243 markdown: &str,
244) {
83c7162d 245 let out = builder.doc_out(target);
3b2f2976 246
83c7162d 247 let path = builder.src.join("src/doc").join(markdown);
cc61c64b 248
532ac7d7 249 let header = builder.src.join("src/doc/redirect.inc");
83c7162d 250 let footer = builder.src.join("src/doc/footer.inc");
cc61c64b 251
532ac7d7 252 let mut cmd = builder.rustdoc_cmd(compiler);
cc61c64b
XL
253
254 let out = out.join("book");
255
dfeec247
XL
256 cmd.arg("--html-after-content")
257 .arg(&footer)
258 .arg("--html-before-content")
2b03887a 259 .arg(&shared_assets.version_info)
dfeec247
XL
260 .arg("--html-in-header")
261 .arg(&header)
0531ce1d 262 .arg("--markdown-no-toc")
dfeec247
XL
263 .arg("--markdown-playground-url")
264 .arg("https://play.rust-lang.org/")
265 .arg("-o")
266 .arg(&out)
267 .arg(&path)
268 .arg("--markdown-css")
269 .arg("../rust.css");
cc61c64b 270
6a06907d
XL
271 if !builder.config.docs_minification {
272 cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
273 }
274
83c7162d 275 builder.run(&mut cmd);
cc61c64b
XL
276}
277
3b2f2976
XL
278#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
279pub struct Standalone {
280 compiler: Compiler,
3dfed10e 281 target: TargetSelection,
3b2f2976 282}
7453a54e 283
3b2f2976
XL
284impl Step for Standalone {
285 type Output = ();
286 const DEFAULT: bool = true;
7453a54e 287
9fa01778 288 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 289 let builder = run.builder;
2b03887a 290 run.path("src/doc").alias("standalone").default_condition(builder.config.docs)
3b2f2976 291 }
7453a54e 292
9fa01778 293 fn make_run(run: RunConfig<'_>) {
3b2f2976 294 run.builder.ensure(Standalone {
83c7162d 295 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
3b2f2976
XL
296 target: run.target,
297 });
7453a54e
SL
298 }
299
3b2f2976
XL
300 /// Generates all standalone documentation as compiled by the rustdoc in `stage`
301 /// for the `target` into `out`.
302 ///
303 /// This will list all of `src/doc` looking for markdown files and appropriately
304 /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
305 /// `STAMP` along with providing the various header/footer HTML we've customized.
306 ///
307 /// In the end, this is just a glorified wrapper around rustdoc!
9fa01778 308 fn run(self, builder: &Builder<'_>) {
3b2f2976
XL
309 let target = self.target;
310 let compiler = self.compiler;
83c7162d
XL
311 builder.info(&format!("Documenting standalone ({})", target));
312 let out = builder.doc_out(target);
3b2f2976
XL
313 t!(fs::create_dir_all(&out));
314
2b03887a
FG
315 let version_info = builder.ensure(SharedAssets { target: self.target }).version_info;
316
83c7162d
XL
317 let favicon = builder.src.join("src/doc/favicon.inc");
318 let footer = builder.src.join("src/doc/footer.inc");
319 let full_toc = builder.src.join("src/doc/full-toc.inc");
7453a54e 320
83c7162d 321 for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
3b2f2976
XL
322 let file = t!(file);
323 let path = file.path();
324 let filename = path.file_name().unwrap().to_str().unwrap();
325 if !filename.ends_with(".md") || filename == "README.md" {
dfeec247 326 continue;
3b2f2976
XL
327 }
328
329 let html = out.join(filename).with_extension("html");
532ac7d7 330 let rustdoc = builder.rustdoc(compiler);
dfeec247
XL
331 if up_to_date(&path, &html)
332 && up_to_date(&footer, &html)
333 && up_to_date(&favicon, &html)
334 && up_to_date(&full_toc, &html)
487cf647
FG
335 && (builder.config.dry_run() || up_to_date(&version_info, &html))
336 && (builder.config.dry_run() || up_to_date(&rustdoc, &html))
dfeec247
XL
337 {
338 continue;
3b2f2976
XL
339 }
340
532ac7d7 341 let mut cmd = builder.rustdoc_cmd(compiler);
ba9703b0
XL
342 // Needed for --index-page flag
343 cmd.arg("-Z").arg("unstable-options");
344
dfeec247
XL
345 cmd.arg("--html-after-content")
346 .arg(&footer)
347 .arg("--html-before-content")
348 .arg(&version_info)
349 .arg("--html-in-header")
350 .arg(&favicon)
351 .arg("--markdown-no-toc")
352 .arg("--index-page")
353 .arg(&builder.src.join("src/doc/index.md"))
354 .arg("--markdown-playground-url")
355 .arg("https://play.rust-lang.org/")
356 .arg("-o")
357 .arg(&out)
358 .arg(&path);
3b2f2976 359
6a06907d
XL
360 if !builder.config.docs_minification {
361 cmd.arg("--disable-minification");
362 }
363
3b2f2976 364 if filename == "not_found.md" {
2b03887a 365 cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css");
3b2f2976 366 } else {
2b03887a 367 cmd.arg("--markdown-css").arg("rust.css");
3b2f2976 368 }
83c7162d 369 builder.run(&mut cmd);
7453a54e 370 }
f9f354fc
XL
371
372 // We open doc/index.html as the default if invoked as `x.py doc --open`
3dfed10e 373 // with no particular explicit doc requested (e.g. library/core).
5099ac24 374 if builder.paths.is_empty() || builder.was_invoked_explicitly::<Self>(Kind::Doc) {
f9f354fc 375 let index = out.join("index.html");
487cf647 376 builder.open_in_browser(&index);
f9f354fc 377 }
3b2f2976
XL
378 }
379}
380
2b03887a
FG
381#[derive(Debug, Clone)]
382pub struct SharedAssetsPaths {
383 pub version_info: PathBuf,
384}
385
386#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
387pub struct SharedAssets {
388 target: TargetSelection,
389}
390
391impl Step for SharedAssets {
392 type Output = SharedAssetsPaths;
393 const DEFAULT: bool = false;
394
395 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
396 // Other tasks depend on this, no need to execute it on its own
397 run.never()
398 }
399
400 // Generate shared resources used by other pieces of documentation.
401 fn run(self, builder: &Builder<'_>) -> Self::Output {
402 let out = builder.doc_out(self.target);
403
404 let version_input = builder.src.join("src").join("doc").join("version_info.html.template");
405 let version_info = out.join("version_info.html");
487cf647 406 if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
2b03887a
FG
407 let info = t!(fs::read_to_string(&version_input))
408 .replace("VERSION", &builder.rust_release())
487cf647
FG
409 .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
410 .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
2b03887a
FG
411 t!(fs::write(&version_info, &info));
412 }
413
414 builder.copy(&builder.src.join("src").join("doc").join("rust.css"), &out.join("rust.css"));
415
416 SharedAssetsPaths { version_info }
417 }
418}
419
3b2f2976
XL
420#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
421pub struct Std {
2c00a5a8 422 pub stage: u32,
3dfed10e 423 pub target: TargetSelection,
487cf647 424 pub format: DocumentationFormat,
3b2f2976
XL
425}
426
427impl Step for Std {
428 type Output = ();
429 const DEFAULT: bool = true;
430
9fa01778 431 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 432 let builder = run.builder;
04454e1e 433 run.all_krates("test").path("library").default_condition(builder.config.docs)
3b2f2976
XL
434 }
435
9fa01778 436 fn make_run(run: RunConfig<'_>) {
487cf647
FG
437 run.builder.ensure(Std {
438 stage: run.builder.top_stage,
439 target: run.target,
440 format: if run.builder.config.cmd.json() {
441 DocumentationFormat::JSON
442 } else {
443 DocumentationFormat::HTML
444 },
445 });
3b2f2976 446 }
7453a54e 447
3b2f2976
XL
448 /// Compile all standard library documentation.
449 ///
450 /// This will generate all documentation for the standard library and its
451 /// dependencies. This is largely just a wrapper around `cargo doc`.
9fa01778 452 fn run(self, builder: &Builder<'_>) {
3b2f2976
XL
453 let stage = self.stage;
454 let target = self.target;
487cf647
FG
455 let out = match self.format {
456 DocumentationFormat::HTML => builder.doc_out(target),
457 DocumentationFormat::JSON => builder.json_doc_out(target),
458 };
459
3b2f2976 460 t!(fs::create_dir_all(&out));
3b2f2976 461
487cf647
FG
462 if self.format == DocumentationFormat::HTML {
463 builder.ensure(SharedAssets { target: self.target });
464 }
3b2f2976 465
2b03887a 466 let index_page = builder.src.join("src/doc/index.md").into_os_string();
487cf647
FG
467 let mut extra_args = match self.format {
468 DocumentationFormat::HTML => vec![
469 OsStr::new("--markdown-css"),
470 OsStr::new("rust.css"),
471 OsStr::new("--markdown-no-toc"),
472 OsStr::new("--index-page"),
473 &index_page,
474 ],
475 DocumentationFormat::JSON => vec![OsStr::new("--output-format"), OsStr::new("json")],
476 };
a1dfa0c6 477
2b03887a
FG
478 if !builder.config.docs_minification {
479 extra_args.push(OsStr::new("--disable-minification"));
480 }
17df50a5 481
2b03887a 482 let requested_crates = builder
17df50a5
XL
483 .paths
484 .iter()
485 .map(components_simplified)
486 .filter_map(|path| {
04454e1e
FG
487 if path.len() >= 2 && path.get(0) == Some(&"library") {
488 // single crate
17df50a5
XL
489 Some(path[1].to_owned())
490 } else if !path.is_empty() {
04454e1e 491 // ??
17df50a5
XL
492 Some(path[0].to_owned())
493 } else {
04454e1e 494 // all library crates
17df50a5
XL
495 None
496 }
497 })
498 .collect::<Vec<_>>();
499
487cf647
FG
500 doc_std(builder, self.format, stage, target, &out, &extra_args, &requested_crates);
501
502 // Don't open if the format is json
503 if let DocumentationFormat::JSON = self.format {
504 return;
505 }
f9f354fc 506
3dfed10e 507 // Look for library/std, library/core etc in the `x.py doc` arguments and
f9f354fc 508 // open the corresponding rendered docs.
2b03887a 509 for requested_crate in requested_crates {
9c376795
FG
510 if requested_crate == "library" {
511 // For `x.py doc library --open`, open `std` by default.
512 let index = out.join("std").join("index.html");
513 builder.open_in_browser(index);
514 } else if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
cdc7bbd5 515 let index = out.join(requested_crate).join("index.html");
487cf647 516 builder.open_in_browser(index);
f9f354fc
XL
517 }
518 }
8bb4bdeb 519 }
3b2f2976 520}
8bb4bdeb 521
2b03887a
FG
522/// Name of the crates that are visible to consumers of the standard library.
523/// Documentation for internal crates is handled by the rustc step, so internal crates will show
524/// up there.
525///
526/// Order here is important!
527/// Crates need to be processed starting from the leaves, otherwise rustdoc will not
528/// create correct links between crates because rustdoc depends on the
529/// existence of the output directories to know if it should be a local
530/// or remote link.
531const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
532
533#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
487cf647 534pub enum DocumentationFormat {
2b03887a
FG
535 HTML,
536 JSON,
537}
538
539impl DocumentationFormat {
540 fn as_str(&self) -> &str {
541 match self {
542 DocumentationFormat::HTML => "HTML",
543 DocumentationFormat::JSON => "JSON",
544 }
545 }
546}
547
548/// Build the documentation for public standard library crates.
549///
550/// `requested_crates` can be used to build only a subset of the crates. If empty, all crates will
551/// be built.
552fn doc_std(
553 builder: &Builder<'_>,
554 format: DocumentationFormat,
555 stage: u32,
556 target: TargetSelection,
557 out: &Path,
558 extra_args: &[&OsStr],
559 requested_crates: &[String],
560) {
561 builder.info(&format!(
9c376795
FG
562 "Documenting{} stage{} library ({}) in {} format",
563 crate_description(requested_crates),
2b03887a
FG
564 stage,
565 target,
566 format.as_str()
567 ));
568 if builder.no_std(target) == Some(true) {
569 panic!(
570 "building std documentation for no_std target {target} is not supported\n\
571 Set `docs = false` in the config to disable documentation."
572 );
573 }
574 let compiler = builder.compiler(stage, builder.config.build);
487cf647
FG
575
576 let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" };
577 let target_dir =
578 builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name);
579
2b03887a
FG
580 // This is directory where the compiler will place the output of the command.
581 // We will then copy the files from this directory into the final `out` directory, the specified
582 // as a function parameter.
487cf647 583 let out_dir = target_dir.join(target.triple).join("doc");
2b03887a
FG
584
585 let run_cargo_rustdoc_for = |package: &str| {
586 let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustdoc");
587 compile::std_cargo(builder, target, compiler.stage, &mut cargo);
588 cargo
487cf647
FG
589 .arg("--target-dir")
590 .arg(&*target_dir.to_string_lossy())
2b03887a
FG
591 .arg("-p")
592 .arg(package)
593 .arg("-Zskip-rustdoc-fingerprint")
594 .arg("--")
595 .arg("-Z")
596 .arg("unstable-options")
597 .arg("--resource-suffix")
598 .arg(&builder.version)
599 .args(extra_args);
600 builder.run(&mut cargo.into());
601 };
602
603 for krate in STD_PUBLIC_CRATES {
604 run_cargo_rustdoc_for(krate);
605 if requested_crates.iter().any(|p| p == krate) {
606 // No need to document more of the libraries if we have the one we want.
607 break;
608 }
609 }
610
611 builder.cp_r(&out_dir, &out);
612}
613
0531ce1d
XL
614#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
615pub struct Rustc {
136023e0
XL
616 pub stage: u32,
617 pub target: TargetSelection,
0531ce1d
XL
618}
619
620impl Step for Rustc {
621 type Output = ();
622 const DEFAULT: bool = true;
623 const ONLY_HOSTS: bool = true;
624
9fa01778 625 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
0531ce1d 626 let builder = run.builder;
064997fb
FG
627 run.crate_or_deps("rustc-main")
628 .path("compiler")
629 .default_condition(builder.config.compiler_docs)
0531ce1d
XL
630 }
631
9fa01778 632 fn make_run(run: RunConfig<'_>) {
dfeec247 633 run.builder.ensure(Rustc { stage: run.builder.top_stage, target: run.target });
0531ce1d
XL
634 }
635
9fa01778 636 /// Generates compiler documentation.
0531ce1d
XL
637 ///
638 /// This will generate all documentation for compiler and dependencies.
639 /// Compiler documentation is distributed separately, so we make sure
640 /// we do not merge it with the other documentation from std, test and
641 /// proc_macros. This is largely just a wrapper around `cargo doc`.
9fa01778 642 fn run(self, builder: &Builder<'_>) {
0531ce1d
XL
643 let stage = self.stage;
644 let target = self.target;
94b46f34 645
c295e0f8
XL
646 let paths = builder
647 .paths
648 .iter()
04454e1e
FG
649 .filter(|path| {
650 let components = components_simplified(path);
651 components.len() >= 2 && components[0] == "compiler"
c295e0f8
XL
652 })
653 .collect::<Vec<_>>();
654
5869c6ff
XL
655 // This is the intended out directory for compiler documentation.
656 let out = builder.compiler_doc_out(target);
657 t!(fs::create_dir_all(&out));
658
04454e1e
FG
659 // Build the standard library, so that proc-macros can use it.
660 // (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.)
5869c6ff 661 let compiler = builder.compiler(stage, builder.config.build);
064997fb 662 builder.ensure(compile::Std::new(compiler, builder.config.build));
04454e1e
FG
663
664 builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
94b46f34 665
3dfed10e
XL
666 // This uses a shared directory so that librustdoc documentation gets
667 // correctly built and merged with the rustc documentation. This is
668 // needed because rustdoc is built in a different directory from
669 // rustc. rustdoc needs to be able to see everything, for example when
670 // merging the search index, or generating local (relative) links.
671 let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc");
83c7162d 672 t!(symlink_dir_force(&builder.config, &out, &out_dir));
5869c6ff
XL
673 // Cargo puts proc macros in `target/doc` even if you pass `--target`
674 // explicitly (https://github.com/rust-lang/cargo/issues/7677).
675 let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
676 t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir));
0531ce1d 677
94b46f34 678 // Build cargo command.
f035d41b 679 let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc");
3dfed10e 680 cargo.rustdocflag("--document-private-items");
cdc7bbd5
XL
681 // Since we always pass --document-private-items, there's no need to warn about linking to private items.
682 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
3dfed10e
XL
683 cargo.rustdocflag("--enable-index-page");
684 cargo.rustdocflag("-Zunstable-options");
5869c6ff 685 cargo.rustdocflag("-Znormalize-docs");
17df50a5 686 cargo.rustdocflag("--show-type-layout");
c295e0f8 687 cargo.rustdocflag("--generate-link-to-definition");
60c5eb7d 688 compile::rustc_cargo(builder, &mut cargo, target);
c295e0f8 689 cargo.arg("-Zunstable-options");
17df50a5 690 cargo.arg("-Zskip-rustdoc-fingerprint");
0531ce1d
XL
691
692 // Only include compiler crates, no dependencies of those, such as `libc`.
c295e0f8 693 // Do link to dependencies on `docs.rs` however using `rustdoc-map`.
0531ce1d 694 cargo.arg("--no-deps");
c295e0f8
XL
695 cargo.arg("-Zrustdoc-map");
696
697 // FIXME: `-Zrustdoc-map` does not yet correctly work for transitive dependencies,
698 // once this is no longer an issue the special case for `ena` can be removed.
699 cargo.rustdocflag("--extern-html-root-url");
700 cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
0531ce1d 701
04454e1e
FG
702 let root_crates = if paths.is_empty() {
703 vec![
704 INTERNER.intern_str("rustc_driver"),
705 INTERNER.intern_str("rustc_codegen_llvm"),
706 INTERNER.intern_str("rustc_codegen_ssa"),
707 ]
c295e0f8 708 } else {
04454e1e
FG
709 paths.into_iter().map(|p| builder.crate_paths[p]).collect()
710 };
711 // Find dependencies for top level crates.
712 let compiler_crates = root_crates.iter().flat_map(|krate| {
713 builder.in_tree_crates(krate, Some(target)).into_iter().map(|krate| krate.name)
714 });
0531ce1d 715
c295e0f8 716 let mut to_open = None;
04454e1e 717 for krate in compiler_crates {
0731742a
XL
718 // Create all crate output directories first to make sure rustdoc uses
719 // relative links.
720 // FIXME: Cargo should probably do this itself.
721 t!(fs::create_dir_all(out_dir.join(krate)));
0531ce1d 722 cargo.arg("-p").arg(krate);
c295e0f8
XL
723 if to_open.is_none() {
724 to_open = Some(krate);
725 }
0531ce1d
XL
726 }
727
e1599b0c 728 builder.run(&mut cargo.into());
c295e0f8
XL
729 // Let's open the first crate documentation page:
730 if let Some(krate) = to_open {
731 let index = out.join(krate).join("index.html");
487cf647 732 builder.open_in_browser(index);
c295e0f8 733 }
0531ce1d
XL
734 }
735}
736
136023e0 737macro_rules! tool_doc {
9c376795 738 ($tool: ident, $should_run: literal, $path: literal, $(rustc_tool = $rustc_tool:literal, )? $(in_tree = $in_tree:literal, )? [$($krate: literal),+ $(,)?] $(,)?) => {
136023e0
XL
739 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
740 pub struct $tool {
136023e0
XL
741 target: TargetSelection,
742 }
94b46f34 743
136023e0
XL
744 impl Step for $tool {
745 type Output = ();
746 const DEFAULT: bool = true;
747 const ONLY_HOSTS: bool = true;
94b46f34 748
136023e0 749 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3c0e092e 750 let builder = run.builder;
064997fb 751 run.crate_or_deps($should_run).default_condition(builder.config.compiler_docs)
136023e0 752 }
94b46f34 753
136023e0 754 fn make_run(run: RunConfig<'_>) {
04454e1e 755 run.builder.ensure($tool { target: run.target });
136023e0 756 }
94b46f34 757
136023e0
XL
758 /// Generates compiler documentation.
759 ///
760 /// This will generate all documentation for compiler and dependencies.
761 /// Compiler documentation is distributed separately, so we make sure
762 /// we do not merge it with the other documentation from std, test and
763 /// proc_macros. This is largely just a wrapper around `cargo doc`.
764 fn run(self, builder: &Builder<'_>) {
04454e1e 765 let stage = builder.top_stage;
136023e0 766 let target = self.target;
04454e1e
FG
767
768 // This is the intended out directory for compiler documentation.
769 let out = builder.compiler_doc_out(target);
770 t!(fs::create_dir_all(&out));
771
04454e1e 772 let compiler = builder.compiler(stage, builder.config.build);
9c376795
FG
773 builder.ensure(compile::Std::new(compiler, target));
774
775 if true $(&& $rustc_tool)? {
776 // Build rustc docs so that we generate relative links.
777 builder.ensure(Rustc { stage, target });
778
779 // Rustdoc needs the rustc sysroot available to build.
780 // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed
781 // with strange errors, but only on a full bors test ...
782 builder.ensure(compile::Rustc::new(compiler, target));
783 }
784
785 let source_type = if true $(&& $in_tree)? {
786 SourceType::InTree
787 } else {
788 SourceType::Submodule
789 };
04454e1e 790
c295e0f8
XL
791 builder.info(
792 &format!(
793 "Documenting stage{} {} ({})",
794 stage,
795 stringify!($tool).to_lowercase(),
796 target,
797 ),
798 );
94b46f34 799
136023e0 800 // Symlink compiler docs to the output directory of rustdoc documentation.
9c376795
FG
801 let out_dirs = [
802 builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc"),
803 // Cargo uses a different directory for proc macros.
804 builder.stage_out(compiler, Mode::ToolRustc).join("doc"),
805 ];
806 for out_dir in out_dirs {
807 t!(fs::create_dir_all(&out_dir));
808 t!(symlink_dir_force(&builder.config, &out, &out_dir));
809 }
136023e0
XL
810
811 // Build cargo command.
812 let mut cargo = prepare_tool_cargo(
813 builder,
814 compiler,
815 Mode::ToolRustc,
816 target,
817 "doc",
818 $path,
9c376795 819 source_type,
136023e0
XL
820 &[],
821 );
822
823 cargo.arg("-Zskip-rustdoc-fingerprint");
824 // Only include compiler crates, no dependencies of those, such as `libc`.
825 cargo.arg("--no-deps");
826 $(
827 cargo.arg("-p").arg($krate);
828 )+
829
94222f64 830 cargo.rustdocflag("--document-private-items");
136023e0
XL
831 cargo.rustdocflag("--enable-index-page");
832 cargo.rustdocflag("--show-type-layout");
c295e0f8 833 cargo.rustdocflag("--generate-link-to-definition");
136023e0 834 cargo.rustdocflag("-Zunstable-options");
487cf647 835 builder.run(&mut cargo.into());
136023e0
XL
836 }
837 }
94b46f34
XL
838 }
839}
840
487cf647 841tool_doc!(Rustdoc, "rustdoc-tool", "src/tools/rustdoc", ["rustdoc", "rustdoc-json-types"],);
136023e0
XL
842tool_doc!(
843 Rustfmt,
844 "rustfmt-nightly",
845 "src/tools/rustfmt",
846 ["rustfmt-nightly", "rustfmt-config_proc_macro"],
136023e0 847);
487cf647
FG
848tool_doc!(Clippy, "clippy", "src/tools/clippy", ["clippy_utils"]);
849tool_doc!(Miri, "miri", "src/tools/miri", ["miri"]);
9c376795
FG
850tool_doc!(
851 Cargo,
852 "cargo",
853 "src/tools/cargo",
854 rustc_tool = false,
855 in_tree = false,
856 [
857 "cargo",
858 "cargo-platform",
859 "cargo-util",
860 "crates-io",
861 "cargo-test-macro",
862 "cargo-test-support",
863 "cargo-credential",
864 "cargo-credential-1password",
865 "mdman",
866 // FIXME: this trips a license check in tidy.
867 // "resolver-tests",
868 // FIXME: we should probably document these, but they're different per-platform so we can't use `tool_doc`.
869 // "cargo-credential-gnome-secret",
870 // "cargo-credential-macos-keychain",
871 // "cargo-credential-wincred",
872 ]
873);
136023e0 874
f035d41b 875#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
3b2f2976 876pub struct ErrorIndex {
3dfed10e 877 pub target: TargetSelection,
54a0048b
SL
878}
879
3b2f2976
XL
880impl Step for ErrorIndex {
881 type Output = ();
882 const DEFAULT: bool = true;
883 const ONLY_HOSTS: bool = true;
884
9fa01778 885 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 886 let builder = run.builder;
83c7162d 887 run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
3b2f2976
XL
888 }
889
9fa01778 890 fn make_run(run: RunConfig<'_>) {
f035d41b 891 let target = run.target;
5869c6ff 892 run.builder.ensure(ErrorIndex { target });
3b2f2976
XL
893 }
894
895 /// Generates the HTML rendered error-index by running the
896 /// `error_index_generator` tool.
9fa01778 897 fn run(self, builder: &Builder<'_>) {
f035d41b
XL
898 builder.info(&format!("Documenting error index ({})", self.target));
899 let out = builder.doc_out(self.target);
3b2f2976 900 t!(fs::create_dir_all(&out));
5869c6ff 901 let mut index = tool::ErrorIndex::command(builder);
3b2f2976 902 index.arg("html");
f2b60f7d 903 index.arg(out);
1b1a35ee 904 index.arg(&builder.version);
3b2f2976 905
83c7162d 906 builder.run(&mut index);
3b2f2976
XL
907 }
908}
909
910#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
911pub struct UnstableBookGen {
3dfed10e 912 target: TargetSelection,
54a0048b 913}
8bb4bdeb 914
3b2f2976
XL
915impl Step for UnstableBookGen {
916 type Output = ();
917 const DEFAULT: bool = true;
918 const ONLY_HOSTS: bool = true;
041b39d2 919
9fa01778 920 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3b2f2976 921 let builder = run.builder;
83c7162d 922 run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
3b2f2976
XL
923 }
924
9fa01778 925 fn make_run(run: RunConfig<'_>) {
dfeec247 926 run.builder.ensure(UnstableBookGen { target: run.target });
3b2f2976
XL
927 }
928
9fa01778 929 fn run(self, builder: &Builder<'_>) {
3b2f2976
XL
930 let target = self.target;
931
83c7162d
XL
932 builder.info(&format!("Generating unstable book md files ({})", target));
933 let out = builder.md_doc_out(target).join("unstable-book");
934 builder.create_dir(&out);
935 builder.remove_dir(&out);
3b2f2976 936 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
3dfed10e 937 cmd.arg(builder.src.join("library"));
1b1a35ee 938 cmd.arg(builder.src.join("compiler"));
83c7162d 939 cmd.arg(builder.src.join("src"));
3b2f2976
XL
940 cmd.arg(out);
941
83c7162d 942 builder.run(&mut cmd);
3b2f2976 943 }
041b39d2
XL
944}
945
83c7162d 946fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
487cf647 947 if config.dry_run() {
83c7162d
XL
948 return Ok(());
949 }
8bb4bdeb
XL
950 if let Ok(m) = fs::symlink_metadata(dst) {
951 if m.file_type().is_dir() {
a1dfa0c6 952 fs::remove_dir_all(dst)?;
8bb4bdeb
XL
953 } else {
954 // handle directory junctions on windows by falling back to
955 // `remove_dir`.
dfeec247 956 fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?;
8bb4bdeb
XL
957 }
958 }
959
83c7162d 960 symlink_dir(config, src, dst)
8bb4bdeb 961}
1b1a35ee
XL
962
963#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
964pub struct RustcBook {
965 pub compiler: Compiler,
966 pub target: TargetSelection,
fc512014 967 pub validate: bool,
1b1a35ee
XL
968}
969
970impl Step for RustcBook {
971 type Output = ();
972 const DEFAULT: bool = true;
973 const ONLY_HOSTS: bool = true;
974
975 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
976 let builder = run.builder;
977 run.path("src/doc/rustc").default_condition(builder.config.docs)
978 }
979
980 fn make_run(run: RunConfig<'_>) {
981 run.builder.ensure(RustcBook {
982 compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
983 target: run.target,
fc512014 984 validate: false,
1b1a35ee
XL
985 });
986 }
987
988 /// Builds the rustc book.
989 ///
990 /// The lints are auto-generated by a tool, and then merged into the book
991 /// in the "md-doc" directory in the build output directory. Then
992 /// "rustbook" is used to convert it to HTML.
993 fn run(self, builder: &Builder<'_>) {
994 let out_base = builder.md_doc_out(self.target).join("rustc");
995 t!(fs::create_dir_all(&out_base));
996 let out_listing = out_base.join("src/lints");
997 builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base);
998 builder.info(&format!("Generating lint docs ({})", self.target));
999
1000 let rustc = builder.rustc(self.compiler);
1001 // The tool runs `rustc` for extracting output examples, so it needs a
1002 // functional sysroot.
064997fb 1003 builder.ensure(compile::Std::new(self.compiler, self.target));
1b1a35ee
XL
1004 let mut cmd = builder.tool_cmd(Tool::LintDocs);
1005 cmd.arg("--src");
1006 cmd.arg(builder.src.join("compiler"));
1007 cmd.arg("--out");
1008 cmd.arg(&out_listing);
1009 cmd.arg("--rustc");
1010 cmd.arg(&rustc);
1011 cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
487cf647 1012 if builder.is_verbose() {
1b1a35ee
XL
1013 cmd.arg("--verbose");
1014 }
fc512014
XL
1015 if self.validate {
1016 cmd.arg("--validate");
1017 }
923072b8
FG
1018 if !builder.unstable_features() {
1019 // We need to validate nightly features, even on the stable channel.
1020 cmd.env("RUSTC_BOOTSTRAP", "1");
1021 }
1b1a35ee
XL
1022 // If the lib directories are in an unusual location (changed in
1023 // config.toml), then this needs to explicitly update the dylib search
1024 // path.
1025 builder.add_rustc_lib_path(self.compiler, &mut cmd);
1026 builder.run(&mut cmd);
1027 // Run rustbook/mdbook to generate the HTML pages.
1028 builder.ensure(RustbookSrc {
1029 target: self.target,
1030 name: INTERNER.intern_str("rustc"),
1031 src: INTERNER.intern_path(out_base),
1032 });
487cf647
FG
1033
1034 let out = builder.doc_out(self.target);
1035 let index = out.join("rustc").join("index.html");
1036 builder.maybe_open_in_browser::<Self>(index);
1b1a35ee
XL
1037 }
1038}