]>
Commit | Line | Data |
---|---|---|
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 | 10 | use std::ffi::OsStr; |
0731742a | 11 | use std::fs; |
8bb4bdeb | 12 | use std::io; |
dfeec247 | 13 | use std::path::{Path, PathBuf}; |
7453a54e | 14 | |
9c376795 | 15 | use crate::builder::crate_description; |
5099ac24 | 16 | use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; |
dfeec247 | 17 | use crate::cache::{Interned, INTERNER}; |
0731742a | 18 | use crate::compile; |
3dfed10e | 19 | use crate::config::{Config, TargetSelection}; |
dfeec247 | 20 | use crate::tool::{self, prepare_tool_cargo, SourceType, Tool}; |
5e7ed085 FG |
21 | use crate::util::{symlink_dir, t, up_to_date}; |
22 | use crate::Mode; | |
3b2f2976 | 23 | |
136023e0 XL |
24 | macro_rules! submodule_helper { |
25 | ($path:expr, submodule) => { | |
26 | $path | |
27 | }; | |
28 | ($path:expr, submodule = $submodule:literal) => { | |
29 | $submodule | |
30 | }; | |
31 | } | |
32 | ||
3b2f2976 | 33 | macro_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 | 77 | book!( |
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 | 94 | pub(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)] |
99 | pub struct UnstableBook { | |
3dfed10e | 100 | target: TargetSelection, |
3b2f2976 | 101 | } |
cc61c64b | 102 | |
3b2f2976 XL |
103 | impl 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)] | |
127 | struct RustbookSrc { | |
3dfed10e | 128 | target: TargetSelection, |
3b2f2976 XL |
129 | name: Interned<String>, |
130 | src: Interned<PathBuf>, | |
131 | } | |
132 | ||
133 | impl 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)] | |
166 | pub struct TheBook { | |
167 | compiler: Compiler, | |
3dfed10e | 168 | target: TargetSelection, |
3b2f2976 | 169 | } |
cc61c64b | 170 | |
3b2f2976 XL |
171 | impl 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 |
238 | fn 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)] |
279 | pub struct Standalone { | |
280 | compiler: Compiler, | |
3dfed10e | 281 | target: TargetSelection, |
3b2f2976 | 282 | } |
7453a54e | 283 | |
3b2f2976 XL |
284 | impl 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)] |
382 | pub struct SharedAssetsPaths { | |
383 | pub version_info: PathBuf, | |
384 | } | |
385 | ||
386 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
387 | pub struct SharedAssets { | |
388 | target: TargetSelection, | |
389 | } | |
390 | ||
391 | impl 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)] |
421 | pub struct Std { | |
2c00a5a8 | 422 | pub stage: u32, |
3dfed10e | 423 | pub target: TargetSelection, |
487cf647 | 424 | pub format: DocumentationFormat, |
3b2f2976 XL |
425 | } |
426 | ||
427 | impl 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. | |
531 | const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"]; | |
532 | ||
533 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] | |
487cf647 | 534 | pub enum DocumentationFormat { |
2b03887a FG |
535 | HTML, |
536 | JSON, | |
537 | } | |
538 | ||
539 | impl 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. | |
552 | fn 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)] |
615 | pub struct Rustc { | |
136023e0 XL |
616 | pub stage: u32, |
617 | pub target: TargetSelection, | |
0531ce1d XL |
618 | } |
619 | ||
620 | impl 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 | 737 | macro_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 | 841 | tool_doc!(Rustdoc, "rustdoc-tool", "src/tools/rustdoc", ["rustdoc", "rustdoc-json-types"],); |
136023e0 XL |
842 | tool_doc!( |
843 | Rustfmt, | |
844 | "rustfmt-nightly", | |
845 | "src/tools/rustfmt", | |
846 | ["rustfmt-nightly", "rustfmt-config_proc_macro"], | |
136023e0 | 847 | ); |
487cf647 FG |
848 | tool_doc!(Clippy, "clippy", "src/tools/clippy", ["clippy_utils"]); |
849 | tool_doc!(Miri, "miri", "src/tools/miri", ["miri"]); | |
9c376795 FG |
850 | tool_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 | 876 | pub struct ErrorIndex { |
3dfed10e | 877 | pub target: TargetSelection, |
54a0048b SL |
878 | } |
879 | ||
3b2f2976 XL |
880 | impl 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)] | |
911 | pub struct UnstableBookGen { | |
3dfed10e | 912 | target: TargetSelection, |
54a0048b | 913 | } |
8bb4bdeb | 914 | |
3b2f2976 XL |
915 | impl 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 | 946 | fn 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)] | |
964 | pub struct RustcBook { | |
965 | pub compiler: Compiler, | |
966 | pub target: TargetSelection, | |
fc512014 | 967 | pub validate: bool, |
1b1a35ee XL |
968 | } |
969 | ||
970 | impl 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 | } |