]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/doc.rs
Move rule configs out of step
[rust.git] / src / bootstrap / doc.rs
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.
4 //
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.
10
11 //! Documentation generation for rustbuild.
12 //!
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.
16 //!
17 //! Everything here is basically just a shim around calling either `rustbook` or
18 //! `rustdoc`.
19
20 use std::fs::{self, File};
21 use std::io::prelude::*;
22 use std::io;
23 use std::path::Path;
24 use std::process::Command;
25
26 use {Build, Compiler, Mode};
27 use util::{cp_r, symlink_dir};
28 use build_helper::up_to_date;
29
30 // rules.doc("doc-nomicon", "src/doc/nomicon")
31 //      .dep(move |s| {
32 //          s.name("tool-rustbook")
33 //           .host(&build.build)
34 //           .target(&build.build)
35 //           .stage(0)
36 //      })
37 //      .default(build.config.docs)
38 //      .run(move |s| doc::rustbook(build, s.target, "nomicon"));
39 // rules.doc("doc-reference", "src/doc/reference")
40 //      .dep(move |s| {
41 //          s.name("tool-rustbook")
42 //           .host(&build.build)
43 //           .target(&build.build)
44 //           .stage(0)
45 //      })
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`.
49 ///
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);
55 }
56
57 //rules.doc("doc-unstable-book", "src/doc/unstable-book")
58 //     .dep(move |s| {
59 //         s.name("tool-rustbook")
60 //          .host(&build.build)
61 //          .target(&build.build)
62 //          .stage(0)
63 //     })
64 //     .dep(move |s| s.name("doc-unstable-book-gen"))
65 //     .default(build.config.docs)
66 //     .run(move |s| doc::rustbook_src(build,
67 //                                     s.target,
68 //                                     "unstable-book",
69 //                                     &build.md_doc_out(s.target)));
70
71
72 /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
73 ///
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));
79
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) {
86         return
87     }
88     println!("Rustbook ({}) - {}", target, name);
89     let _ = fs::remove_dir_all(&out);
90     build.run(build.tool_cmd(&compiler, "rustbook")
91                    .arg("build")
92                    .arg(&src)
93                    .arg("-d")
94                    .arg(out));
95 }
96
97 // rules.doc("doc-book", "src/doc/book")
98 //      .dep(move |s| {
99 //          s.name("tool-rustbook")
100 //           .host(&build.build)
101 //           .target(&build.build)
102 //           .stage(0)
103 //      })
104 //      .default(build.config.docs)
105 //      .run(move |s| doc::book(build, s.target, "book"));
106 /// Build the book and associated stuff.
107 ///
108 /// We need to build:
109 ///
110 /// * Book (first edition)
111 /// * Book (second edition)
112 /// * Index page
113 /// * Redirect pages
114 pub fn book(build: &Build, target: &str, name: &str) {
115     // build book first edition
116     rustbook(build, target, &format!("{}/first-edition", name));
117
118     // build book second edition
119     rustbook(build, target, &format!("{}/second-edition", name));
120
121     // build the index page
122     let index = format!("{}/index.md", name);
123     println!("Documenting book index ({})", target);
124     invoke_rustdoc(build, target, &index);
125
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"))) {
129         let file = t!(file);
130         let path = file.path();
131         let path = path.to_str().unwrap();
132
133         invoke_rustdoc(build, target, path);
134     }
135 }
136
137 fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
138     let out = build.doc_out(target);
139
140     let compiler = Compiler::new(0, &build.build);
141
142     let path = build.src.join("src/doc").join(markdown);
143
144     let rustdoc = build.rustdoc(&compiler);
145
146     let favicon = build.src.join("src/doc/favicon.inc");
147     let footer = build.src.join("src/doc/footer.inc");
148
149     let version_input = build.src.join("src/doc/version_info.html.template");
150     let version_info = out.join("version_info.html");
151
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()));
159     }
160
161     let mut cmd = Command::new(&rustdoc);
162
163     build.add_rustc_lib_path(&compiler, &mut cmd);
164
165     let out = out.join("book");
166
167     t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
168
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/")
174         .arg("-o").arg(&out)
175         .arg(&path)
176         .arg("--markdown-css")
177         .arg("rust.css");
178
179     build.run(&mut cmd);
180 }
181
182 // rules.doc("doc-standalone", "src/doc")
183 //      .dep(move |s| {
184 //          s.name("rustc")
185 //           .host(&build.build)
186 //           .target(&build.build)
187 //           .stage(0)
188 //      })
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`.
193 ///
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.
197 ///
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));
203
204     let compiler = Compiler::new(0, &build.build);
205
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")));
210
211     let version_input = build.src.join("src/doc/version_info.html.template");
212     let version_info = out.join("version_info.html");
213
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()));
221     }
222
223     for file in t!(fs::read_dir(build.src.join("src/doc"))) {
224         let file = t!(file);
225         let path = file.path();
226         let filename = path.file_name().unwrap().to_str().unwrap();
227         if !filename.ends_with(".md") || filename == "README.md" {
228             continue
229         }
230
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) {
239             continue
240         }
241
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/")
249            .arg("-o").arg(&out)
250            .arg(&path);
251
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");
256         } else {
257             cmd.arg("--markdown-css").arg("rust.css");
258         }
259         build.run(&mut cmd);
260     }
261 }
262
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));
268 // }
269 /// Compile all standard library documentation.
270 ///
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)
280     } else {
281         compiler
282     };
283     let out_dir = build.stage_out(&compiler, Mode::Libstd)
284                        .join(target).join("doc");
285     let rustdoc = build.rustdoc(&compiler);
286
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.
292     //
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
296     // all of them.
297     //
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));
303
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());
308
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
317             // relative links.
318             // FIXME: Cargo should probably do this itself.
319             t!(fs::create_dir_all(out_dir.join(krate)));
320         }
321     }
322
323
324     build.run(&mut cargo);
325     cp_r(&my_out, &out);
326 }
327
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));
335 // }
336 /// Compile all libtest documentation.
337 ///
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)
347     } else {
348         compiler
349     };
350     let out_dir = build.stage_out(&compiler, Mode::Libtest)
351                        .join(target).join("doc");
352     let rustdoc = build.rustdoc(&compiler);
353
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));
358
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);
363     cp_r(&my_out, &out);
364 }
365
366
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"))
372 //          .host(true)
373 //          .default(default && build.config.docs)
374 //          .run(move |s| doc::rustc(build, s.stage, s.target));
375 // }
376 //
377 /// Generate all compiler documentation.
378 ///
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)
388     } else {
389         compiler
390     };
391     let out_dir = build.stage_out(&compiler, Mode::Librustc)
392                        .join(target).join("doc");
393     let rustdoc = build.rustdoc(&compiler);
394
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));
399
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());
404
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");
411     } else {
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);
417         }
418     }
419
420     build.run(&mut cargo);
421     cp_r(&my_out, &out);
422 }
423
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)
428 //      .host(true)
429 //      .run(move |s| doc::error_index(build, s.target));
430
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");
439     index.arg("html");
440     index.arg(out.join("error-index.html"));
441
442     // FIXME: shouldn't have to pass this env var
443     index.env("CFG_BUILD", &build.build);
444
445     build.run(&mut index);
446 }
447
448 // rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen")
449 //      .dep(move |s| {
450 //          s.name("tool-unstable-book-gen")
451 //           .host(&build.build)
452 //           .target(&build.build)
453 //           .stage(0)
454 //      })
455 //      .dep(move |s| s.name("libstd-link"))
456 //      .default(build.config.docs)
457 //      .host(true)
458 //      .run(move |s| doc::unstable_book_gen(build, s.target));
459
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"));
468     cmd.arg(out);
469
470     build.run(&mut cmd);
471 }
472
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));
477         } else {
478             // handle directory junctions on windows by falling back to
479             // `remove_dir`.
480             try!(fs::remove_file(dst).or_else(|_| {
481                 fs::remove_dir(dst)
482             }));
483         }
484     }
485
486     symlink_dir(src, dst)
487 }