1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Documentation generation for rustbuild.
13 //! This module implements generation for all bits and pieces of documentation
14 //! for the Rust project. This notably includes suites like the rust book, the
15 //! nomicon, rust by example, standalone documentation, etc.
17 //! Everything here is basically just a shim around calling either `rustbook` or
20 use std::fs::{self, File};
21 use std::io::prelude::*;
23 use std::path::{PathBuf, Path};
26 use build_helper::up_to_date;
28 use util::{cp_r, symlink_dir};
29 use builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
32 use cache::{INTERNER, Interned};
35 ($($name:ident, $path:expr, $book_name:expr;)+) => {
37 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
39 target: Interned<String>,
44 const DEFAULT: bool = true;
46 fn should_run(run: ShouldRun) -> ShouldRun {
47 let builder = run.builder;
48 run.path($path).default_condition(builder.build.config.docs)
51 fn make_run(run: RunConfig) {
52 run.builder.ensure($name {
57 fn run(self, builder: &Builder) {
58 builder.ensure(Rustbook {
60 name: INTERNER.intern_str($book_name),
69 Nomicon, "src/doc/nomicon", "nomicon";
70 Reference, "src/doc/reference", "reference";
71 Rustdoc, "src/doc/rustdoc", "rustdoc";
72 RustByExample, "src/doc/rust-by-example", "rust-by-example";
75 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
77 target: Interned<String>,
78 name: Interned<String>,
81 impl Step for Rustbook {
84 // rustbook is never directly called, and only serves as a shim for the nomicon and the
86 fn should_run(run: ShouldRun) -> ShouldRun {
90 /// Invoke `rustbook` for `target` for the doc book `name`.
92 /// This will not actually generate any documentation if the documentation has
93 /// already been generated.
94 fn run(self, builder: &Builder) {
95 let src = builder.build.src.join("src/doc");
96 builder.ensure(RustbookSrc {
99 src: INTERNER.intern_path(src),
104 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
105 pub struct UnstableBook {
106 target: Interned<String>,
109 impl Step for UnstableBook {
111 const DEFAULT: bool = true;
113 fn should_run(run: ShouldRun) -> ShouldRun {
114 let builder = run.builder;
115 run.path("src/doc/unstable-book").default_condition(builder.build.config.docs)
118 fn make_run(run: RunConfig) {
119 run.builder.ensure(UnstableBook {
124 fn run(self, builder: &Builder) {
125 builder.ensure(UnstableBookGen {
128 builder.ensure(RustbookSrc {
130 name: INTERNER.intern_str("unstable-book"),
131 src: builder.build.md_doc_out(self.target),
136 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
137 pub struct CargoBook {
138 target: Interned<String>,
139 name: Interned<String>,
142 impl Step for CargoBook {
144 const DEFAULT: bool = true;
146 fn should_run(run: ShouldRun) -> ShouldRun {
147 let builder = run.builder;
148 run.path("src/tools/cargo/src/doc/book").default_condition(builder.build.config.docs)
151 fn make_run(run: RunConfig) {
152 run.builder.ensure(CargoBook {
154 name: INTERNER.intern_str("cargo"),
158 fn run(self, builder: &Builder) {
159 let build = builder.build;
161 let target = self.target;
162 let name = self.name;
163 let src = build.src.join("src/tools/cargo/src/doc");
165 let out = build.doc_out(target);
166 t!(fs::create_dir_all(&out));
168 let out = out.join(name);
170 println!("Cargo Book ({}) - {}", target, name);
172 let _ = fs::remove_dir_all(&out);
174 build.run(builder.tool_cmd(Tool::Rustbook)
182 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
184 target: Interned<String>,
185 name: Interned<String>,
186 src: Interned<PathBuf>,
189 impl Step for RustbookSrc {
192 fn should_run(run: ShouldRun) -> ShouldRun {
196 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
198 /// This will not actually generate any documentation if the documentation has
199 /// already been generated.
200 fn run(self, builder: &Builder) {
201 let build = builder.build;
202 let target = self.target;
203 let name = self.name;
205 let out = build.doc_out(target);
206 t!(fs::create_dir_all(&out));
208 let out = out.join(name);
209 let src = src.join(name);
210 let index = out.join("index.html");
211 let rustbook = builder.tool_exe(Tool::Rustbook);
212 if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
215 println!("Rustbook ({}) - {}", target, name);
216 let _ = fs::remove_dir_all(&out);
217 build.run(builder.tool_cmd(Tool::Rustbook)
225 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
228 target: Interned<String>,
232 impl Step for TheBook {
234 const DEFAULT: bool = true;
236 fn should_run(run: ShouldRun) -> ShouldRun {
237 let builder = run.builder;
238 run.path("src/doc/book").default_condition(builder.build.config.docs)
241 fn make_run(run: RunConfig) {
242 run.builder.ensure(TheBook {
243 compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
249 /// Build the book and associated stuff.
251 /// We need to build:
253 /// * Book (first edition)
254 /// * Book (second edition)
255 /// * Version info and CSS
258 fn run(self, builder: &Builder) {
259 let build = builder.build;
260 let compiler = self.compiler;
261 let target = self.target;
262 let name = self.name;
263 // build book first edition
264 builder.ensure(Rustbook {
266 name: INTERNER.intern_string(format!("{}/first-edition", name)),
269 // build book second edition
270 builder.ensure(Rustbook {
272 name: INTERNER.intern_string(format!("{}/second-edition", name)),
275 // build the version info page and CSS
276 builder.ensure(Standalone {
281 // build the index page
282 let index = format!("{}/index.md", name);
283 println!("Documenting book index ({})", target);
284 invoke_rustdoc(builder, compiler, target, &index);
286 // build the redirect pages
287 println!("Documenting book redirect pages ({})", target);
288 for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
290 let path = file.path();
291 let path = path.to_str().unwrap();
293 invoke_rustdoc(builder, compiler, target, path);
298 fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned<String>, markdown: &str) {
299 let build = builder.build;
300 let out = build.doc_out(target);
302 let path = build.src.join("src/doc").join(markdown);
304 let favicon = build.src.join("src/doc/favicon.inc");
305 let footer = build.src.join("src/doc/footer.inc");
306 let version_info = out.join("version_info.html");
308 let mut cmd = builder.rustdoc_cmd(compiler.host);
310 let out = out.join("book");
312 cmd.arg("--html-after-content").arg(&footer)
313 .arg("--html-before-content").arg(&version_info)
314 .arg("--html-in-header").arg(&favicon)
315 .arg("--markdown-no-toc")
316 .arg("--markdown-playground-url")
317 .arg("https://play.rust-lang.org/")
320 .arg("--markdown-css")
326 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
327 pub struct Standalone {
329 target: Interned<String>,
332 impl Step for Standalone {
334 const DEFAULT: bool = true;
336 fn should_run(run: ShouldRun) -> ShouldRun {
337 let builder = run.builder;
338 run.path("src/doc").default_condition(builder.build.config.docs)
341 fn make_run(run: RunConfig) {
342 run.builder.ensure(Standalone {
343 compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
348 /// Generates all standalone documentation as compiled by the rustdoc in `stage`
349 /// for the `target` into `out`.
351 /// This will list all of `src/doc` looking for markdown files and appropriately
352 /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
353 /// `STAMP` along with providing the various header/footer HTML we've customized.
355 /// In the end, this is just a glorified wrapper around rustdoc!
356 fn run(self, builder: &Builder) {
357 let build = builder.build;
358 let target = self.target;
359 let compiler = self.compiler;
360 println!("Documenting standalone ({})", target);
361 let out = build.doc_out(target);
362 t!(fs::create_dir_all(&out));
364 let favicon = build.src.join("src/doc/favicon.inc");
365 let footer = build.src.join("src/doc/footer.inc");
366 let full_toc = build.src.join("src/doc/full-toc.inc");
367 t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
369 let version_input = build.src.join("src/doc/version_info.html.template");
370 let version_info = out.join("version_info.html");
372 if !up_to_date(&version_input, &version_info) {
373 let mut info = String::new();
374 t!(t!(File::open(&version_input)).read_to_string(&mut info));
375 let info = info.replace("VERSION", &build.rust_release())
376 .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
377 .replace("STAMP", build.rust_info.sha().unwrap_or(""));
378 t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
381 for file in t!(fs::read_dir(build.src.join("src/doc"))) {
383 let path = file.path();
384 let filename = path.file_name().unwrap().to_str().unwrap();
385 if !filename.ends_with(".md") || filename == "README.md" {
389 let html = out.join(filename).with_extension("html");
390 let rustdoc = builder.rustdoc(compiler.host);
391 if up_to_date(&path, &html) &&
392 up_to_date(&footer, &html) &&
393 up_to_date(&favicon, &html) &&
394 up_to_date(&full_toc, &html) &&
395 up_to_date(&version_info, &html) &&
396 up_to_date(&rustdoc, &html) {
400 let mut cmd = builder.rustdoc_cmd(compiler.host);
401 cmd.arg("--html-after-content").arg(&footer)
402 .arg("--html-before-content").arg(&version_info)
403 .arg("--html-in-header").arg(&favicon)
404 .arg("--markdown-playground-url")
405 .arg("https://play.rust-lang.org/")
409 if filename == "not_found.md" {
410 cmd.arg("--markdown-no-toc")
411 .arg("--markdown-css")
412 .arg("https://doc.rust-lang.org/rust.css");
414 cmd.arg("--markdown-css").arg("rust.css");
421 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
424 pub target: Interned<String>,
429 const DEFAULT: bool = true;
431 fn should_run(run: ShouldRun) -> ShouldRun {
432 let builder = run.builder;
433 run.all_krates("std").default_condition(builder.build.config.docs)
436 fn make_run(run: RunConfig) {
437 run.builder.ensure(Std {
438 stage: run.builder.top_stage,
443 /// Compile all standard library documentation.
445 /// This will generate all documentation for the standard library and its
446 /// dependencies. This is largely just a wrapper around `cargo doc`.
447 fn run(self, builder: &Builder) {
448 let build = builder.build;
449 let stage = self.stage;
450 let target = self.target;
451 println!("Documenting stage{} std ({})", stage, target);
452 let out = build.doc_out(target);
453 t!(fs::create_dir_all(&out));
454 let compiler = builder.compiler(stage, build.build);
455 let rustdoc = builder.rustdoc(compiler.host);
456 let compiler = if build.force_use_stage1(compiler, target) {
457 builder.compiler(1, compiler.host)
462 builder.ensure(compile::Std { compiler, target });
463 let out_dir = build.stage_out(compiler, Mode::Libstd)
464 .join(target).join("doc");
466 // Here what we're doing is creating a *symlink* (directory junction on
467 // Windows) to the final output location. This is not done as an
468 // optimization but rather for correctness. We've got three trees of
469 // documentation, one for std, one for test, and one for rustc. It's then
470 // our job to merge them all together.
472 // Unfortunately rustbuild doesn't know nearly as well how to merge doc
473 // trees as rustdoc does itself, so instead of actually having three
474 // separate trees we just have rustdoc output to the same location across
477 // This way rustdoc generates output directly into the output, and rustdoc
478 // will also directly handle merging.
479 let my_out = build.crate_doc_out(target);
480 build.clear_if_dirty(&my_out, &rustdoc);
481 t!(symlink_dir_force(&my_out, &out_dir));
483 let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
484 compile::std_cargo(build, &compiler, target, &mut cargo);
486 // We don't want to build docs for internal std dependencies unless
487 // in compiler-docs mode. When not in that mode, we whitelist the crates
488 // for which docs must be built.
489 if !build.config.compiler_docs {
490 cargo.arg("--no-deps");
491 for krate in &["alloc", "core", "std", "std_unicode"] {
492 cargo.arg("-p").arg(krate);
493 // Create all crate output directories first to make sure rustdoc uses
495 // FIXME: Cargo should probably do this itself.
496 t!(fs::create_dir_all(out_dir.join(krate)));
501 build.run(&mut cargo);
506 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
509 target: Interned<String>,
514 const DEFAULT: bool = true;
516 fn should_run(run: ShouldRun) -> ShouldRun {
517 let builder = run.builder;
518 run.krate("test").default_condition(builder.config.compiler_docs)
521 fn make_run(run: RunConfig) {
522 run.builder.ensure(Test {
523 stage: run.builder.top_stage,
528 /// Compile all libtest documentation.
530 /// This will generate all documentation for libtest and its dependencies. This
531 /// is largely just a wrapper around `cargo doc`.
532 fn run(self, builder: &Builder) {
533 let build = builder.build;
534 let stage = self.stage;
535 let target = self.target;
536 println!("Documenting stage{} test ({})", stage, target);
537 let out = build.doc_out(target);
538 t!(fs::create_dir_all(&out));
539 let compiler = builder.compiler(stage, build.build);
540 let rustdoc = builder.rustdoc(compiler.host);
541 let compiler = if build.force_use_stage1(compiler, target) {
542 builder.compiler(1, compiler.host)
547 // Build libstd docs so that we generate relative links
548 builder.ensure(Std { stage, target });
550 builder.ensure(compile::Test { compiler, target });
551 let out_dir = build.stage_out(compiler, Mode::Libtest)
552 .join(target).join("doc");
554 // See docs in std above for why we symlink
555 let my_out = build.crate_doc_out(target);
556 build.clear_if_dirty(&my_out, &rustdoc);
557 t!(symlink_dir_force(&my_out, &out_dir));
559 let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc");
560 compile::test_cargo(build, &compiler, target, &mut cargo);
561 build.run(&mut cargo);
566 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
569 target: Interned<String>,
572 impl Step for Rustc {
574 const DEFAULT: bool = true;
575 const ONLY_HOSTS: bool = true;
577 fn should_run(run: ShouldRun) -> ShouldRun {
578 let builder = run.builder;
579 run.krate("rustc-main").default_condition(builder.build.config.docs)
582 fn make_run(run: RunConfig) {
583 run.builder.ensure(Rustc {
584 stage: run.builder.top_stage,
589 /// Generate all compiler documentation.
591 /// This will generate all documentation for the compiler libraries and their
592 /// dependencies. This is largely just a wrapper around `cargo doc`.
593 fn run(self, builder: &Builder) {
594 let build = builder.build;
595 let stage = self.stage;
596 let target = self.target;
597 println!("Documenting stage{} compiler ({})", stage, target);
598 let out = build.doc_out(target);
599 t!(fs::create_dir_all(&out));
600 let compiler = builder.compiler(stage, build.build);
601 let rustdoc = builder.rustdoc(compiler.host);
602 let compiler = if build.force_use_stage1(compiler, target) {
603 builder.compiler(1, compiler.host)
608 // Build libstd docs so that we generate relative links
609 builder.ensure(Std { stage, target });
611 builder.ensure(compile::Rustc { compiler, target });
612 let out_dir = build.stage_out(compiler, Mode::Librustc)
613 .join(target).join("doc");
615 // See docs in std above for why we symlink
616 let my_out = build.crate_doc_out(target);
617 build.clear_if_dirty(&my_out, &rustdoc);
618 t!(symlink_dir_force(&my_out, &out_dir));
620 let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
621 compile::rustc_cargo(build, &mut cargo);
623 if build.config.compiler_docs {
624 // src/rustc/Cargo.toml contains a bin crate called rustc which
625 // would otherwise overwrite the docs for the real rustc lib crate.
626 cargo.arg("-p").arg("rustc_driver");
628 // Like with libstd above if compiler docs aren't enabled then we're not
629 // documenting internal dependencies, so we have a whitelist.
630 cargo.arg("--no-deps");
631 for krate in &["proc_macro"] {
632 cargo.arg("-p").arg(krate);
636 build.run(&mut cargo);
641 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
642 pub struct ErrorIndex {
643 target: Interned<String>,
646 impl Step for ErrorIndex {
648 const DEFAULT: bool = true;
649 const ONLY_HOSTS: bool = true;
651 fn should_run(run: ShouldRun) -> ShouldRun {
652 let builder = run.builder;
653 run.path("src/tools/error_index_generator").default_condition(builder.build.config.docs)
656 fn make_run(run: RunConfig) {
657 run.builder.ensure(ErrorIndex {
662 /// Generates the HTML rendered error-index by running the
663 /// `error_index_generator` tool.
664 fn run(self, builder: &Builder) {
665 let build = builder.build;
666 let target = self.target;
668 println!("Documenting error index ({})", target);
669 let out = build.doc_out(target);
670 t!(fs::create_dir_all(&out));
671 let mut index = builder.tool_cmd(Tool::ErrorIndex);
673 index.arg(out.join("error-index.html"));
675 // FIXME: shouldn't have to pass this env var
676 index.env("CFG_BUILD", &build.build)
677 .env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir());
679 build.run(&mut index);
683 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
684 pub struct UnstableBookGen {
685 target: Interned<String>,
688 impl Step for UnstableBookGen {
690 const DEFAULT: bool = true;
691 const ONLY_HOSTS: bool = true;
693 fn should_run(run: ShouldRun) -> ShouldRun {
694 let builder = run.builder;
695 run.path("src/tools/unstable-book-gen").default_condition(builder.build.config.docs)
698 fn make_run(run: RunConfig) {
699 run.builder.ensure(UnstableBookGen {
704 fn run(self, builder: &Builder) {
705 let build = builder.build;
706 let target = self.target;
708 builder.ensure(compile::Std {
709 compiler: builder.compiler(builder.top_stage, build.build),
713 println!("Generating unstable book md files ({})", target);
714 let out = build.md_doc_out(target).join("unstable-book");
715 t!(fs::create_dir_all(&out));
716 t!(fs::remove_dir_all(&out));
717 let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
718 cmd.arg(build.src.join("src"));
725 fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
726 if let Ok(m) = fs::symlink_metadata(dst) {
727 if m.file_type().is_dir() {
728 try!(fs::remove_dir_all(dst));
730 // handle directory junctions on windows by falling back to
732 try!(fs::remove_file(dst).or_else(|_| {
738 symlink_dir(src, dst)