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;
30 // rules.doc("doc-nomicon", "src/doc/nomicon")
32 // s.name("tool-rustbook")
33 // .host(&build.build)
34 // .target(&build.build)
37 // .default(build.config.docs)
38 // .run(move |s| doc::rustbook(build, s.target, "nomicon"));
39 // rules.doc("doc-reference", "src/doc/reference")
41 // s.name("tool-rustbook")
42 // .host(&build.build)
43 // .target(&build.build)
46 // .default(build.config.docs)
47 // .run(move |s| doc::rustbook(build, s.target, "reference"));
48 /// Invoke `rustbook` for `target` for the doc book `name`.
50 /// This will not actually generate any documentation if the documentation has
51 /// already been generated.
52 pub fn rustbook(build: &Build, target: &str, name: &str) {
53 let src = build.src.join("src/doc");
54 rustbook_src(build, target, name, &src);
57 //rules.doc("doc-unstable-book", "src/doc/unstable-book")
59 // s.name("tool-rustbook")
60 // .host(&build.build)
61 // .target(&build.build)
64 // .dep(move |s| s.name("doc-unstable-book-gen"))
65 // .default(build.config.docs)
66 // .run(move |s| doc::rustbook_src(build,
69 // &build.md_doc_out(s.target)));
72 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
74 /// This will not actually generate any documentation if the documentation has
75 /// already been generated.
76 pub fn rustbook_src(build: &Build, target: &str, name: &str, src: &Path) {
77 let out = build.doc_out(target);
78 t!(fs::create_dir_all(&out));
80 let out = out.join(name);
81 let compiler = Compiler::new(0, &build.build);
82 let src = src.join(name);
83 let index = out.join("index.html");
84 let rustbook = build.tool(&compiler, "rustbook");
85 if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
88 println!("Rustbook ({}) - {}", target, name);
89 let _ = fs::remove_dir_all(&out);
90 build.run(build.tool_cmd(&compiler, "rustbook")
97 // rules.doc("doc-book", "src/doc/book")
99 // s.name("tool-rustbook")
100 // .host(&build.build)
101 // .target(&build.build)
104 // .default(build.config.docs)
105 // .run(move |s| doc::book(build, s.target, "book"));
106 /// Build the book and associated stuff.
108 /// We need to build:
110 /// * Book (first edition)
111 /// * Book (second edition)
114 pub fn book(build: &Build, target: &str, name: &str) {
115 // build book first edition
116 rustbook(build, target, &format!("{}/first-edition", name));
118 // build book second edition
119 rustbook(build, target, &format!("{}/second-edition", name));
121 // build the index page
122 let index = format!("{}/index.md", name);
123 println!("Documenting book index ({})", target);
124 invoke_rustdoc(build, target, &index);
126 // build the redirect pages
127 println!("Documenting book redirect pages ({})", target);
128 for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
130 let path = file.path();
131 let path = path.to_str().unwrap();
133 invoke_rustdoc(build, target, path);
137 fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
138 let out = build.doc_out(target);
140 let compiler = Compiler::new(0, &build.build);
142 let path = build.src.join("src/doc").join(markdown);
144 let rustdoc = build.rustdoc(&compiler);
146 let favicon = build.src.join("src/doc/favicon.inc");
147 let footer = build.src.join("src/doc/footer.inc");
149 let version_input = build.src.join("src/doc/version_info.html.template");
150 let version_info = out.join("version_info.html");
152 if !up_to_date(&version_input, &version_info) {
153 let mut info = String::new();
154 t!(t!(File::open(&version_input)).read_to_string(&mut info));
155 let info = info.replace("VERSION", &build.rust_release())
156 .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
157 .replace("STAMP", build.rust_info.sha().unwrap_or(""));
158 t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
161 let mut cmd = Command::new(&rustdoc);
163 build.add_rustc_lib_path(&compiler, &mut cmd);
165 let out = out.join("book");
167 t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
169 cmd.arg("--html-after-content").arg(&footer)
170 .arg("--html-before-content").arg(&version_info)
171 .arg("--html-in-header").arg(&favicon)
172 .arg("--markdown-playground-url")
173 .arg("https://play.rust-lang.org/")
176 .arg("--markdown-css")
182 // rules.doc("doc-standalone", "src/doc")
185 // .host(&build.build)
186 // .target(&build.build)
189 // .default(build.config.docs)
190 // .run(move |s| doc::standalone(build, s.target));
191 /// Generates all standalone documentation as compiled by the rustdoc in `stage`
192 /// for the `target` into `out`.
194 /// This will list all of `src/doc` looking for markdown files and appropriately
195 /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
196 /// `STAMP` alongw ith providing the various header/footer HTML we've cutomized.
198 /// In the end, this is just a glorified wrapper around rustdoc!
199 pub fn standalone(build: &Build, target: &str) {
200 println!("Documenting standalone ({})", target);
201 let out = build.doc_out(target);
202 t!(fs::create_dir_all(&out));
204 let compiler = Compiler::new(0, &build.build);
206 let favicon = build.src.join("src/doc/favicon.inc");
207 let footer = build.src.join("src/doc/footer.inc");
208 let full_toc = build.src.join("src/doc/full-toc.inc");
209 t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
211 let version_input = build.src.join("src/doc/version_info.html.template");
212 let version_info = out.join("version_info.html");
214 if !up_to_date(&version_input, &version_info) {
215 let mut info = String::new();
216 t!(t!(File::open(&version_input)).read_to_string(&mut info));
217 let info = info.replace("VERSION", &build.rust_release())
218 .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
219 .replace("STAMP", build.rust_info.sha().unwrap_or(""));
220 t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
223 for file in t!(fs::read_dir(build.src.join("src/doc"))) {
225 let path = file.path();
226 let filename = path.file_name().unwrap().to_str().unwrap();
227 if !filename.ends_with(".md") || filename == "README.md" {
231 let html = out.join(filename).with_extension("html");
232 let rustdoc = build.rustdoc(&compiler);
233 if up_to_date(&path, &html) &&
234 up_to_date(&footer, &html) &&
235 up_to_date(&favicon, &html) &&
236 up_to_date(&full_toc, &html) &&
237 up_to_date(&version_info, &html) &&
238 up_to_date(&rustdoc, &html) {
242 let mut cmd = Command::new(&rustdoc);
243 build.add_rustc_lib_path(&compiler, &mut cmd);
244 cmd.arg("--html-after-content").arg(&footer)
245 .arg("--html-before-content").arg(&version_info)
246 .arg("--html-in-header").arg(&favicon)
247 .arg("--markdown-playground-url")
248 .arg("https://play.rust-lang.org/")
252 if filename == "not_found.md" {
253 cmd.arg("--markdown-no-toc")
254 .arg("--markdown-css")
255 .arg("https://doc.rust-lang.org/rust.css");
257 cmd.arg("--markdown-css").arg("rust.css");
263 // for (krate, path, default) in krates("std") {
264 // rules.doc(&krate.doc_step, path)
265 // .dep(|s| s.name("libstd-link"))
266 // .default(default && build.config.docs)
267 // .run(move |s| doc::std(build, s.stage, s.target));
269 /// Compile all standard library documentation.
271 /// This will generate all documentation for the standard library and its
272 /// dependencies. This is largely just a wrapper around `cargo doc`.
273 pub fn std(build: &Build, stage: u32, target: &str) {
274 println!("Documenting stage{} std ({})", stage, target);
275 let out = build.doc_out(target);
276 t!(fs::create_dir_all(&out));
277 let compiler = Compiler::new(stage, &build.build);
278 let compiler = if build.force_use_stage1(&compiler, target) {
279 Compiler::new(1, compiler.host)
283 let out_dir = build.stage_out(&compiler, Mode::Libstd)
284 .join(target).join("doc");
285 let rustdoc = build.rustdoc(&compiler);
287 // Here what we're doing is creating a *symlink* (directory junction on
288 // Windows) to the final output location. This is not done as an
289 // optimization but rather for correctness. We've got three trees of
290 // documentation, one for std, one for test, and one for rustc. It's then
291 // our job to merge them all together.
293 // Unfortunately rustbuild doesn't know nearly as well how to merge doc
294 // trees as rustdoc does itself, so instead of actually having three
295 // separate trees we just have rustdoc output to the same location across
298 // This way rustdoc generates output directly into the output, and rustdoc
299 // will also directly handle merging.
300 let my_out = build.crate_doc_out(target);
301 build.clear_if_dirty(&my_out, &rustdoc);
302 t!(symlink_dir_force(&my_out, &out_dir));
304 let mut cargo = build.cargo(&compiler, Mode::Libstd, target, "doc");
305 cargo.arg("--manifest-path")
306 .arg(build.src.join("src/libstd/Cargo.toml"))
307 .arg("--features").arg(build.std_features());
309 // We don't want to build docs for internal std dependencies unless
310 // in compiler-docs mode. When not in that mode, we whitelist the crates
311 // for which docs must be built.
312 if !build.config.compiler_docs {
313 cargo.arg("--no-deps");
314 for krate in &["alloc", "collections", "core", "std", "std_unicode"] {
315 cargo.arg("-p").arg(krate);
316 // Create all crate output directories first to make sure rustdoc uses
318 // FIXME: Cargo should probably do this itself.
319 t!(fs::create_dir_all(out_dir.join(krate)));
324 build.run(&mut cargo);
328 // for (krate, path, default) in krates("test") {
329 // rules.doc(&krate.doc_step, path)
330 // .dep(|s| s.name("libtest-link"))
331 // // Needed so rustdoc generates relative links to std.
332 // .dep(|s| s.name("doc-crate-std"))
333 // .default(default && build.config.compiler_docs)
334 // .run(move |s| doc::test(build, s.stage, s.target));
336 /// Compile all libtest documentation.
338 /// This will generate all documentation for libtest and its dependencies. This
339 /// is largely just a wrapper around `cargo doc`.
340 pub fn test(build: &Build, stage: u32, target: &str) {
341 println!("Documenting stage{} test ({})", stage, target);
342 let out = build.doc_out(target);
343 t!(fs::create_dir_all(&out));
344 let compiler = Compiler::new(stage, &build.build);
345 let compiler = if build.force_use_stage1(&compiler, target) {
346 Compiler::new(1, compiler.host)
350 let out_dir = build.stage_out(&compiler, Mode::Libtest)
351 .join(target).join("doc");
352 let rustdoc = build.rustdoc(&compiler);
354 // See docs in std above for why we symlink
355 let my_out = build.crate_doc_out(target);
356 build.clear_if_dirty(&my_out, &rustdoc);
357 t!(symlink_dir_force(&my_out, &out_dir));
359 let mut cargo = build.cargo(&compiler, Mode::Libtest, target, "doc");
360 cargo.arg("--manifest-path")
361 .arg(build.src.join("src/libtest/Cargo.toml"));
362 build.run(&mut cargo);
367 // for (krate, path, default) in krates("rustc-main") {
368 // rules.doc(&krate.doc_step, path)
369 // .dep(|s| s.name("librustc-link"))
370 // // Needed so rustdoc generates relative links to std.
371 // .dep(|s| s.name("doc-crate-std"))
373 // .default(default && build.config.docs)
374 // .run(move |s| doc::rustc(build, s.stage, s.target));
377 /// Generate all compiler documentation.
379 /// This will generate all documentation for the compiler libraries and their
380 /// dependencies. This is largely just a wrapper around `cargo doc`.
381 pub fn rustc(build: &Build, stage: u32, target: &str) {
382 println!("Documenting stage{} compiler ({})", stage, target);
383 let out = build.doc_out(target);
384 t!(fs::create_dir_all(&out));
385 let compiler = Compiler::new(stage, &build.build);
386 let compiler = if build.force_use_stage1(&compiler, target) {
387 Compiler::new(1, compiler.host)
391 let out_dir = build.stage_out(&compiler, Mode::Librustc)
392 .join(target).join("doc");
393 let rustdoc = build.rustdoc(&compiler);
395 // See docs in std above for why we symlink
396 let my_out = build.crate_doc_out(target);
397 build.clear_if_dirty(&my_out, &rustdoc);
398 t!(symlink_dir_force(&my_out, &out_dir));
400 let mut cargo = build.cargo(&compiler, Mode::Librustc, target, "doc");
401 cargo.arg("--manifest-path")
402 .arg(build.src.join("src/rustc/Cargo.toml"))
403 .arg("--features").arg(build.rustc_features());
405 if build.config.compiler_docs {
406 // src/rustc/Cargo.toml contains bin crates called rustc and rustdoc
407 // which would otherwise overwrite the docs for the real rustc and
408 // rustdoc lib crates.
409 cargo.arg("-p").arg("rustc_driver")
410 .arg("-p").arg("rustdoc");
412 // Like with libstd above if compiler docs aren't enabled then we're not
413 // documenting internal dependencies, so we have a whitelist.
414 cargo.arg("--no-deps");
415 for krate in &["proc_macro"] {
416 cargo.arg("-p").arg(krate);
420 build.run(&mut cargo);
424 // rules.doc("doc-error-index", "src/tools/error_index_generator")
425 // .dep(move |s| s.name("tool-error-index").target(&build.build).stage(0))
426 // .dep(move |s| s.name("librustc-link"))
427 // .default(build.config.docs)
429 // .run(move |s| doc::error_index(build, s.target));
431 /// Generates the HTML rendered error-index by running the
432 /// `error_index_generator` tool.
433 pub fn error_index(build: &Build, target: &str) {
434 println!("Documenting error index ({})", target);
435 let out = build.doc_out(target);
436 t!(fs::create_dir_all(&out));
437 let compiler = Compiler::new(0, &build.build);
438 let mut index = build.tool_cmd(&compiler, "error_index_generator");
440 index.arg(out.join("error-index.html"));
442 // FIXME: shouldn't have to pass this env var
443 index.env("CFG_BUILD", &build.build);
445 build.run(&mut index);
448 // rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen")
450 // s.name("tool-unstable-book-gen")
451 // .host(&build.build)
452 // .target(&build.build)
455 // .dep(move |s| s.name("libstd-link"))
456 // .default(build.config.docs)
458 // .run(move |s| doc::unstable_book_gen(build, s.target));
460 pub fn unstable_book_gen(build: &Build, target: &str) {
461 println!("Generating unstable book md files ({})", target);
462 let out = build.md_doc_out(target).join("unstable-book");
463 t!(fs::create_dir_all(&out));
464 t!(fs::remove_dir_all(&out));
465 let compiler = Compiler::new(0, &build.build);
466 let mut cmd = build.tool_cmd(&compiler, "unstable-book-gen");
467 cmd.arg(build.src.join("src"));
473 fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
474 if let Ok(m) = fs::symlink_metadata(dst) {
475 if m.file_type().is_dir() {
476 try!(fs::remove_dir_all(dst));
478 // handle directory junctions on windows by falling back to
480 try!(fs::remove_file(dst).or_else(|_| {
486 symlink_dir(src, dst)