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, 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::*;
24 use std::process::Command;
26 use {Build, Compiler, Mode};
27 use util::{cp_r, symlink_dir};
28 use build_helper::up_to_date;
31 ($($name:ident, $path:expr, $book_name:expr;)+) => {
34 pub struct $name<'a> {
38 impl<'a> Step<'a> for $name<'a> {
40 const NAME: &'static str = concat!(stringify!($book_name), " - book");
41 const DEFAULT: bool = true;
43 fn should_run(_builder: &Builder, path: &Path) -> bool {
47 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
48 if path.is_none() && !builder.build.config.docs {
49 // Not a default rule if docs are disabled.
53 builder.ensure($name {
58 fn run(self, builder: &Builder) {
59 builder.ensure(Rustbook {
70 // rules.doc("doc-nomicon", "src/doc/nomicon")
72 // s.name("tool-rustbook")
73 // .host(&build.build)
74 // .target(&build.build)
77 // .default(build.config.docs)
78 // .run(move |s| doc::rustbook(build, s.target, "nomicon"));
79 Nomicon, "src/doc/book", "nomicon";
80 // rules.doc("doc-reference", "src/doc/reference")
82 // s.name("tool-rustbook")
83 // .host(&build.build)
84 // .target(&build.build)
87 // .default(build.config.docs)
88 // .run(move |s| doc::rustbook(build, s.target, "reference"));
89 Reference, "src/doc/reference", "reference";
98 impl<'a> Step<'a> for Rustbook<'a> {
101 /// Invoke `rustbook` for `target` for the doc book `name`.
103 /// This will not actually generate any documentation if the documentation has
104 /// already been generated.
105 fn run(self, builder: &Builder) {
106 let src = builder.build.src.join("src/doc");
107 builder.ensure(RustbookSrc {
115 //rules.doc("doc-unstable-book", "src/doc/unstable-book")
117 // s.name("tool-rustbook")
118 // .host(&build.build)
119 // .target(&build.build)
122 // .dep(move |s| s.name("doc-unstable-book-gen"))
123 // .default(build.config.docs)
124 // .run(move |s| doc::rustbook_src(build,
127 // &build.md_doc_out(s.target)));
129 pub struct UnstableBook<'a> {
133 impl<'a> Step<'a> for UnstableBook<'a> {
135 const NAME: &'static str = "unstable book documentation";
136 const DEFAULT: bool = true;
138 fn should_run(_builder: &Builder, path: &Path) -> bool {
139 path.ends_with("src/doc/unstable-book")
142 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
143 if path.is_none() && !builder.build.config.docs {
144 // Not a default rule if docs are disabled.
148 builder.ensure(UnstableBook {
153 fn run(self, builder: &Builder) {
154 builder.ensure(UnstableBookGen {
157 builder.ensure(RustbookSrc {
159 name: "unstable-book",
160 src: &builder.build.md_doc_out(self.target),
166 pub struct RustbookSrc<'a> {
172 impl<'a> Step<'a> for RustbookSrc<'a> {
175 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
177 /// This will not actually generate any documentation if the documentation has
178 /// already been generated.
179 fn run(self, builder: &Builder) {
180 let build = builder.build;
181 let target = self.target;
182 let name = self.name;
184 let out = build.doc_out(target);
185 t!(fs::create_dir_all(&out));
187 let out = out.join(name);
188 let src = src.join(name);
189 let index = out.join("index.html");
190 let rustbook = builder.tool_exe(Tool::Rustbook);
191 if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
194 println!("Rustbook ({}) - {}", target, name);
195 let _ = fs::remove_dir_all(&out);
196 build.run(builder.tool_cmd(Tool::Rustbook)
204 // rules.doc("doc-book", "src/doc/book")
206 // s.name("tool-rustbook")
207 // .host(&build.build)
208 // .target(&build.build)
211 // .default(build.config.docs)
212 // .run(move |s| doc::book(build, s.target, "book"));
215 pub struct TheBook<'a> {
220 impl<'a> Step<'a> for TheBook<'a> {
223 /// Build the book and associated stuff.
225 /// We need to build:
227 /// * Book (first edition)
228 /// * Book (second edition)
231 fn run(self, builder: &Builder) {
232 let build = builder.build;
233 let target = self.target;
234 let name = self.name;
235 // build book first edition
236 builder.ensure(Rustbook {
238 name: &format!("{}/first-edition", name),
241 // build book second edition
242 builder.ensure(Rustbook {
244 name: &format!("{}/second-edition", name),
247 // build the index page
248 let index = format!("{}/index.md", name);
249 println!("Documenting book index ({})", target);
250 invoke_rustdoc(build, target, &index);
252 // build the redirect pages
253 println!("Documenting book redirect pages ({})", target);
254 for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
256 let path = file.path();
257 let path = path.to_str().unwrap();
259 invoke_rustdoc(build, target, path);
264 fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
265 let out = build.doc_out(target);
267 let compiler = Compiler::new(0, &build.build);
269 let path = build.src.join("src/doc").join(markdown);
271 let rustdoc = build.rustdoc(&compiler);
273 let favicon = build.src.join("src/doc/favicon.inc");
274 let footer = build.src.join("src/doc/footer.inc");
276 let version_input = build.src.join("src/doc/version_info.html.template");
277 let version_info = out.join("version_info.html");
279 if !up_to_date(&version_input, &version_info) {
280 let mut info = String::new();
281 t!(t!(File::open(&version_input)).read_to_string(&mut info));
282 let info = info.replace("VERSION", &build.rust_release())
283 .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
284 .replace("STAMP", build.rust_info.sha().unwrap_or(""));
285 t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
288 let mut cmd = Command::new(&rustdoc);
290 build.add_rustc_lib_path(&compiler, &mut cmd);
292 let out = out.join("book");
294 t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
296 cmd.arg("--html-after-content").arg(&footer)
297 .arg("--html-before-content").arg(&version_info)
298 .arg("--html-in-header").arg(&favicon)
299 .arg("--markdown-playground-url")
300 .arg("https://play.rust-lang.org/")
303 .arg("--markdown-css")
309 // rules.doc("doc-standalone", "src/doc")
312 // .host(&build.build)
313 // .target(&build.build)
316 // .default(build.config.docs)
317 // .run(move |s| doc::standalone(build, s.target));
320 pub struct Standalone<'a> {
324 impl<'a> Step<'a> for Standalone<'a> {
326 const DEFAULT: bool = true;
328 fn should_run(_builder: &Builder, path: &Path) -> bool {
329 path.ends_with("src/doc")
332 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
333 if path.is_none() && !builder.build.config.docs {
334 // Not a default rule if docs are disabled.
338 builder.ensure(Standalone {
343 /// Generates all standalone documentation as compiled by the rustdoc in `stage`
344 /// for the `target` into `out`.
346 /// This will list all of `src/doc` looking for markdown files and appropriately
347 /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
348 /// `STAMP` alongw ith providing the various header/footer HTML we've cutomized.
350 /// In the end, this is just a glorified wrapper around rustdoc!
351 fn run(self, builder: &Builder) {
352 let build = builder.build;
353 let target = self.target;
354 println!("Documenting standalone ({})", target);
355 let out = build.doc_out(target);
356 t!(fs::create_dir_all(&out));
358 let compiler = builder.compiler(0, &build.build);
360 let favicon = build.src.join("src/doc/favicon.inc");
361 let footer = build.src.join("src/doc/footer.inc");
362 let full_toc = build.src.join("src/doc/full-toc.inc");
363 t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
365 let version_input = build.src.join("src/doc/version_info.html.template");
366 let version_info = out.join("version_info.html");
368 if !up_to_date(&version_input, &version_info) {
369 let mut info = String::new();
370 t!(t!(File::open(&version_input)).read_to_string(&mut info));
371 let info = info.replace("VERSION", &build.rust_release())
372 .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
373 .replace("STAMP", build.rust_info.sha().unwrap_or(""));
374 t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
377 for file in t!(fs::read_dir(build.src.join("src/doc"))) {
379 let path = file.path();
380 let filename = path.file_name().unwrap().to_str().unwrap();
381 if !filename.ends_with(".md") || filename == "README.md" {
385 let html = out.join(filename).with_extension("html");
386 let rustdoc = build.rustdoc(&compiler);
387 if up_to_date(&path, &html) &&
388 up_to_date(&footer, &html) &&
389 up_to_date(&favicon, &html) &&
390 up_to_date(&full_toc, &html) &&
391 up_to_date(&version_info, &html) &&
392 up_to_date(&rustdoc, &html) {
396 let mut cmd = Command::new(&rustdoc);
397 build.add_rustc_lib_path(&compiler, &mut cmd);
398 cmd.arg("--html-after-content").arg(&footer)
399 .arg("--html-before-content").arg(&version_info)
400 .arg("--html-in-header").arg(&favicon)
401 .arg("--markdown-playground-url")
402 .arg("https://play.rust-lang.org/")
406 if filename == "not_found.md" {
407 cmd.arg("--markdown-no-toc")
408 .arg("--markdown-css")
409 .arg("https://doc.rust-lang.org/rust.css");
411 cmd.arg("--markdown-css").arg("rust.css");
418 // for (krate, path, default) in krates("std") {
419 // rules.doc(&krate.doc_step, path)
420 // .dep(|s| s.name("libstd-link"))
421 // .default(default && build.config.docs)
422 // .run(move |s| doc::std(build, s.stage, s.target));
431 impl<'a> Step<'a> for Std<'a> {
433 const DEFAULT: bool = true;
435 fn should_run(builder: &Builder, path: &Path) -> bool {
436 builder.crates("std").into_iter().any(|(_, krate_path)| {
437 path.ends_with(krate_path)
441 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
444 stage: builder.top_stage,
449 if let Some(path) = path {
450 for (_, krate_path) in builder.crates("std") {
451 if path.ends_with(krate_path) {
456 if builder.build.config.docs {
462 /// Compile all standard library documentation.
464 /// This will generate all documentation for the standard library and its
465 /// dependencies. This is largely just a wrapper around `cargo doc`.
466 fn run(self, builder: &Builder) {
467 let build = builder.build;
468 let stage = self.stage;
469 let target = self.target;
470 println!("Documenting stage{} std ({})", stage, target);
471 let out = build.doc_out(target);
472 t!(fs::create_dir_all(&out));
473 let compiler = builder.compiler(stage, &build.build);
474 let compiler = if build.force_use_stage1(compiler, target) {
475 builder.compiler(1, compiler.host)
480 builder.ensure(compile::Std { compiler, target });
481 let out_dir = build.stage_out(&compiler, Mode::Libstd)
482 .join(target).join("doc");
483 let rustdoc = build.rustdoc(&compiler);
485 // Here what we're doing is creating a *symlink* (directory junction on
486 // Windows) to the final output location. This is not done as an
487 // optimization but rather for correctness. We've got three trees of
488 // documentation, one for std, one for test, and one for rustc. It's then
489 // our job to merge them all together.
491 // Unfortunately rustbuild doesn't know nearly as well how to merge doc
492 // trees as rustdoc does itself, so instead of actually having three
493 // separate trees we just have rustdoc output to the same location across
496 // This way rustdoc generates output directly into the output, and rustdoc
497 // will also directly handle merging.
498 let my_out = build.crate_doc_out(target);
499 build.clear_if_dirty(&my_out, &rustdoc);
500 t!(symlink_dir_force(&my_out, &out_dir));
502 let mut cargo = build.cargo(&compiler, Mode::Libstd, target, "doc");
503 cargo.arg("--manifest-path")
504 .arg(build.src.join("src/libstd/Cargo.toml"))
505 .arg("--features").arg(build.std_features());
507 // We don't want to build docs for internal std dependencies unless
508 // in compiler-docs mode. When not in that mode, we whitelist the crates
509 // for which docs must be built.
510 if !build.config.compiler_docs {
511 cargo.arg("--no-deps");
512 for krate in &["alloc", "collections", "core", "std", "std_unicode"] {
513 cargo.arg("-p").arg(krate);
514 // Create all crate output directories first to make sure rustdoc uses
516 // FIXME: Cargo should probably do this itself.
517 t!(fs::create_dir_all(out_dir.join(krate)));
522 build.run(&mut cargo);
527 // for (krate, path, default) in krates("test") {
528 // rules.doc(&krate.doc_step, path)
529 // .dep(|s| s.name("libtest-link"))
530 // // Needed so rustdoc generates relative links to std.
531 // .dep(|s| s.name("doc-crate-std"))
532 // .default(default && build.config.compiler_docs)
533 // .run(move |s| doc::test(build, s.stage, s.target));
537 pub struct Test<'a> {
542 impl<'a> Step<'a> for Test<'a> {
544 const DEFAULT: bool = true;
546 fn should_run(builder: &Builder, path: &Path) -> bool {
547 builder.crates("test").into_iter().any(|(_, krate_path)| {
548 path.ends_with(krate_path)
552 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
554 builder.ensure(Test {
555 stage: builder.top_stage,
560 if let Some(path) = path {
561 for (_, krate_path) in builder.crates("test") {
562 if path.ends_with(krate_path) {
567 if builder.build.config.docs {
573 /// Compile all libtest documentation.
575 /// This will generate all documentation for libtest and its dependencies. This
576 /// is largely just a wrapper around `cargo doc`.
577 fn run(self, builder: &Builder) {
578 let build = builder.build;
579 let stage = self.stage;
580 let target = self.target;
581 println!("Documenting stage{} test ({})", stage, target);
582 let out = build.doc_out(target);
583 t!(fs::create_dir_all(&out));
584 let compiler = builder.compiler(stage, &build.build);
585 let compiler = if build.force_use_stage1(compiler, target) {
586 builder.compiler(1, compiler.host)
591 // Build libstd docs so that we generate relative links
592 builder.ensure(Std { stage, target });
594 builder.ensure(compile::Test { compiler, target });
595 let out_dir = build.stage_out(&compiler, Mode::Libtest)
596 .join(target).join("doc");
597 let rustdoc = build.rustdoc(&compiler);
599 // See docs in std above for why we symlink
600 let my_out = build.crate_doc_out(target);
601 build.clear_if_dirty(&my_out, &rustdoc);
602 t!(symlink_dir_force(&my_out, &out_dir));
604 let mut cargo = build.cargo(&compiler, Mode::Libtest, target, "doc");
605 cargo.arg("--manifest-path")
606 .arg(build.src.join("src/libtest/Cargo.toml"));
607 build.run(&mut cargo);
612 // for (krate, path, default) in krates("rustc-main") {
613 // rules.doc(&krate.doc_step, path)
614 // .dep(|s| s.name("librustc-link"))
615 // // Needed so rustdoc generates relative links to std.
616 // .dep(|s| s.name("doc-crate-std"))
618 // .default(default && build.config.docs)
619 // .run(move |s| doc::rustc(build, s.stage, s.target));
624 pub struct Rustc<'a> {
629 impl<'a> Step<'a> for Rustc<'a> {
631 const DEFAULT: bool = true;
632 const ONLY_HOSTS: bool = true;
634 fn should_run(builder: &Builder, path: &Path) -> bool {
635 builder.crates("rustc-main").into_iter().any(|(_, krate_path)| {
636 path.ends_with(krate_path)
640 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
642 builder.ensure(Rustc {
643 stage: builder.top_stage,
648 if let Some(path) = path {
649 for (_, krate_path) in builder.crates("rustc-main") {
650 if path.ends_with(krate_path) {
655 if builder.build.config.compiler_docs {
661 /// Generate all compiler documentation.
663 /// This will generate all documentation for the compiler libraries and their
664 /// dependencies. This is largely just a wrapper around `cargo doc`.
665 fn run(self, builder: &Builder) {
666 let build = builder.build;
667 let stage = self.stage;
668 let target = self.target;
669 println!("Documenting stage{} compiler ({})", stage, target);
670 let out = build.doc_out(target);
671 t!(fs::create_dir_all(&out));
672 let compiler = builder.compiler(stage, &build.build);
673 let compiler = if build.force_use_stage1(compiler, target) {
674 builder.compiler(1, compiler.host)
679 // Build libstd docs so that we generate relative links
680 builder.ensure(Std { stage, target });
682 builder.ensure(compile::Rustc { compiler, target });
683 let out_dir = build.stage_out(&compiler, Mode::Librustc)
684 .join(target).join("doc");
685 let rustdoc = build.rustdoc(&compiler);
687 // See docs in std above for why we symlink
688 let my_out = build.crate_doc_out(target);
689 build.clear_if_dirty(&my_out, &rustdoc);
690 t!(symlink_dir_force(&my_out, &out_dir));
692 let mut cargo = build.cargo(&compiler, Mode::Librustc, target, "doc");
693 cargo.arg("--manifest-path")
694 .arg(build.src.join("src/rustc/Cargo.toml"))
695 .arg("--features").arg(build.rustc_features());
697 if build.config.compiler_docs {
698 // src/rustc/Cargo.toml contains bin crates called rustc and rustdoc
699 // which would otherwise overwrite the docs for the real rustc and
700 // rustdoc lib crates.
701 cargo.arg("-p").arg("rustc_driver")
702 .arg("-p").arg("rustdoc");
704 // Like with libstd above if compiler docs aren't enabled then we're not
705 // documenting internal dependencies, so we have a whitelist.
706 cargo.arg("--no-deps");
707 for krate in &["proc_macro"] {
708 cargo.arg("-p").arg(krate);
712 build.run(&mut cargo);
717 // rules.doc("doc-error-index", "src/tools/error_index_generator")
718 // .dep(move |s| s.name("tool-error-index").target(&build.build).stage(0))
719 // .dep(move |s| s.name("librustc-link"))
720 // .default(build.config.docs)
722 // .run(move |s| doc::error_index(build, s.target));
725 pub struct ErrorIndex<'a> {
729 impl<'a> Step<'a> for ErrorIndex<'a> {
731 const DEFAULT: bool = true;
732 const ONLY_HOSTS: bool = true;
734 fn should_run(_builder: &Builder, path: &Path) -> bool {
735 path.ends_with("src/tools/error_index_generator")
738 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
739 if path.is_none() && !builder.build.config.docs {
740 // Not a default rule if docs are disabled.
744 builder.ensure(ErrorIndex {
749 /// Generates the HTML rendered error-index by running the
750 /// `error_index_generator` tool.
751 fn run(self, builder: &Builder) {
752 let builder = builder.build;
753 let target = self.target;
755 builder.ensure(compile::Rustc {
756 compiler: builder.compiler(0, &build.build),
760 println!("Documenting error index ({})", target);
761 let out = build.doc_out(target);
762 t!(fs::create_dir_all(&out));
763 let compiler = Compiler::new(0, &build.build);
764 let mut index = builder.tool_cmd(Tool::ErrorIndex);
766 index.arg(out.join("error-index.html"));
768 // FIXME: shouldn't have to pass this env var
769 index.env("CFG_BUILD", &build.build);
771 build.run(&mut index);
775 // rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen")
777 // s.name("tool-unstable-book-gen")
778 // .host(&build.build)
779 // .target(&build.build)
782 // .dep(move |s| s.name("libstd-link"))
783 // .default(build.config.docs)
785 // .run(move |s| doc::unstable_book_gen(build, s.target));
788 pub struct UnstableBookGen<'a> {
792 impl<'a> Step<'a> for UnstableBookGen<'a> {
794 const DEFAULT: bool = true;
795 const ONLY_HOSTS: bool = true;
797 fn should_run(_builder: &Builder, path: &Path) -> bool {
798 path.ends_with("src/doc/unstable-book")
801 fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
802 if path.is_none() && !builder.build.config.docs {
803 // Not a default rule if docs are disabled.
807 builder.ensure(UnstableBookGen {
812 fn run(self, builder: &Builder) {
813 let build = builder.build;
814 let target = self.target;
816 builder.ensure(compile::Std {
817 compiler: builder.compiler(builder.top_stage, &build.build),
821 println!("Generating unstable book md files ({})", target);
822 let out = build.md_doc_out(target).join("unstable-book");
823 t!(fs::create_dir_all(&out));
824 t!(fs::remove_dir_all(&out));
825 let compiler = Compiler::new(0, &build.build);
826 let mut cmd = build.tool_cmd(&compiler, "unstable-book-gen");
827 cmd.arg(build.src.join("src"));
834 fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
835 if let Ok(m) = fs::symlink_metadata(dst) {
836 if m.file_type().is_dir() {
837 try!(fs::remove_dir_all(dst));
839 // handle directory junctions on windows by falling back to
841 try!(fs::remove_file(dst).or_else(|_| {
847 symlink_dir(src, dst)