]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/doc.rs
Change code to work with the new system
[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 macro_rules! book {
31     ($($name:ident, $path:expr, $book_name:expr;)+) => {
32         $(
33         #[derive(Serialize)]
34         pub struct $name<'a> {
35             target: &'a str,
36         }
37
38         impl<'a> Step<'a> for $name<'a> {
39             type Output = ();
40             const NAME: &'static str = concat!(stringify!($book_name), " - book");
41             const DEFAULT: bool = true;
42
43             fn should_run(_builder: &Builder, path: &Path) -> bool {
44                 path.ends_with($path)
45             }
46
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.
50                     return;
51                 }
52
53                 builder.ensure($name {
54                     target,
55                 });
56             }
57
58             fn run(self, builder: &Builder) {
59                 builder.ensure(Rustbook {
60                     target: self.target,
61                     name: $book_name,
62                 })
63             }
64         }
65         )+
66     }
67 }
68
69 book!(
70     // rules.doc("doc-nomicon", "src/doc/nomicon")
71     //      .dep(move |s| {
72     //          s.name("tool-rustbook")
73     //           .host(&build.build)
74     //           .target(&build.build)
75     //           .stage(0)
76     //      })
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")
81     //      .dep(move |s| {
82     //          s.name("tool-rustbook")
83     //           .host(&build.build)
84     //           .target(&build.build)
85     //           .stage(0)
86     //      })
87     //      .default(build.config.docs)
88     //      .run(move |s| doc::rustbook(build, s.target, "reference"));
89     Reference, "src/doc/reference", "reference";
90 );
91
92 #[derive(Serialize)]
93 struct Rustbook<'a> {
94     target: &'a str,
95     name: &'a str,
96 }
97
98 impl<'a> Step<'a> for Rustbook<'a> {
99     type Output = ();
100
101     /// Invoke `rustbook` for `target` for the doc book `name`.
102     ///
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 {
108             target: self.target,
109             name: self.name,
110             src: &src,
111         });
112     }
113 }
114
115 //rules.doc("doc-unstable-book", "src/doc/unstable-book")
116 //     .dep(move |s| {
117 //         s.name("tool-rustbook")
118 //          .host(&build.build)
119 //          .target(&build.build)
120 //          .stage(0)
121 //     })
122 //     .dep(move |s| s.name("doc-unstable-book-gen"))
123 //     .default(build.config.docs)
124 //     .run(move |s| doc::rustbook_src(build,
125 //                                     s.target,
126 //                                     "unstable-book",
127 //                                     &build.md_doc_out(s.target)));
128 #[derive(Serialize)]
129 pub struct UnstableBook<'a> {
130     target: &'a str,
131 }
132
133 impl<'a> Step<'a> for UnstableBook<'a> {
134     type Output = ();
135     const NAME: &'static str = "unstable book documentation";
136     const DEFAULT: bool = true;
137
138     fn should_run(_builder: &Builder, path: &Path) -> bool {
139         path.ends_with("src/doc/unstable-book")
140     }
141
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.
145             return;
146         }
147
148         builder.ensure(UnstableBook {
149             target,
150         });
151     }
152
153     fn run(self, builder: &Builder) {
154         builder.ensure(UnstableBookGen {
155             target: self.target,
156         });
157         builder.ensure(RustbookSrc {
158             target: self.target,
159             name: "unstable-book",
160             src: &builder.build.md_doc_out(self.target),
161         })
162     }
163 }
164
165 #[derive(Serialize)]
166 pub struct RustbookSrc<'a> {
167     target: &'a str,
168     name: &'a str,
169     src: &'a Path,
170 }
171
172 impl<'a> Step<'a> for RustbookSrc<'a> {
173     type Output = ();
174
175     /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
176     ///
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;
183         let src = self.src;
184         let out = build.doc_out(target);
185         t!(fs::create_dir_all(&out));
186
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) {
192             return
193         }
194         println!("Rustbook ({}) - {}", target, name);
195         let _ = fs::remove_dir_all(&out);
196         build.run(builder.tool_cmd(Tool::Rustbook)
197                        .arg("build")
198                        .arg(&src)
199                        .arg("-d")
200                        .arg(out));
201     }
202 }
203
204 // rules.doc("doc-book", "src/doc/book")
205 //      .dep(move |s| {
206 //          s.name("tool-rustbook")
207 //           .host(&build.build)
208 //           .target(&build.build)
209 //           .stage(0)
210 //      })
211 //      .default(build.config.docs)
212 //      .run(move |s| doc::book(build, s.target, "book"));
213
214 #[derive(Serialize)]
215 pub struct TheBook<'a> {
216     target: &'a str,
217     name: &'a str,
218 }
219
220 impl<'a> Step<'a> for TheBook<'a> {
221     type Output = ();
222
223     /// Build the book and associated stuff.
224     ///
225     /// We need to build:
226     ///
227     /// * Book (first edition)
228     /// * Book (second edition)
229     /// * Index page
230     /// * Redirect pages
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 {
237             target: target,
238             name: &format!("{}/first-edition", name),
239         });
240
241         // build book second edition
242         builder.ensure(Rustbook {
243             target: target,
244             name: &format!("{}/second-edition", name),
245         });
246
247         // build the index page
248         let index = format!("{}/index.md", name);
249         println!("Documenting book index ({})", target);
250         invoke_rustdoc(build, target, &index);
251
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"))) {
255             let file = t!(file);
256             let path = file.path();
257             let path = path.to_str().unwrap();
258
259             invoke_rustdoc(build, target, path);
260         }
261     }
262 }
263
264 fn invoke_rustdoc(build: &Build, target: &str, markdown: &str) {
265     let out = build.doc_out(target);
266
267     let compiler = Compiler::new(0, &build.build);
268
269     let path = build.src.join("src/doc").join(markdown);
270
271     let rustdoc = build.rustdoc(&compiler);
272
273     let favicon = build.src.join("src/doc/favicon.inc");
274     let footer = build.src.join("src/doc/footer.inc");
275
276     let version_input = build.src.join("src/doc/version_info.html.template");
277     let version_info = out.join("version_info.html");
278
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()));
286     }
287
288     let mut cmd = Command::new(&rustdoc);
289
290     build.add_rustc_lib_path(&compiler, &mut cmd);
291
292     let out = out.join("book");
293
294     t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
295
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/")
301         .arg("-o").arg(&out)
302         .arg(&path)
303         .arg("--markdown-css")
304         .arg("rust.css");
305
306     build.run(&mut cmd);
307 }
308
309 // rules.doc("doc-standalone", "src/doc")
310 //      .dep(move |s| {
311 //          s.name("rustc")
312 //           .host(&build.build)
313 //           .target(&build.build)
314 //           .stage(0)
315 //      })
316 //      .default(build.config.docs)
317 //      .run(move |s| doc::standalone(build, s.target));
318
319 #[derive(Serialize)]
320 pub struct Standalone<'a> {
321     target: &'a str,
322 }
323
324 impl<'a> Step<'a> for Standalone<'a> {
325     type Output = ();
326     const DEFAULT: bool = true;
327
328     fn should_run(_builder: &Builder, path: &Path) -> bool {
329         path.ends_with("src/doc")
330     }
331
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.
335             return;
336         }
337
338         builder.ensure(Standalone {
339             target,
340         });
341     }
342
343     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
344     /// for the `target` into `out`.
345     ///
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.
349     ///
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));
357
358         let compiler = builder.compiler(0, &build.build);
359
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")));
364
365         let version_input = build.src.join("src/doc/version_info.html.template");
366         let version_info = out.join("version_info.html");
367
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()));
375         }
376
377         for file in t!(fs::read_dir(build.src.join("src/doc"))) {
378             let file = t!(file);
379             let path = file.path();
380             let filename = path.file_name().unwrap().to_str().unwrap();
381             if !filename.ends_with(".md") || filename == "README.md" {
382                 continue
383             }
384
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) {
393                 continue
394             }
395
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/")
403                .arg("-o").arg(&out)
404                .arg(&path);
405
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");
410             } else {
411                 cmd.arg("--markdown-css").arg("rust.css");
412             }
413             build.run(&mut cmd);
414         }
415     }
416 }
417
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));
423 // }
424
425 #[derive(Serialize)]
426 pub struct Std<'a> {
427     stage: u32,
428     target: &'a str,
429 }
430
431 impl<'a> Step<'a> for Std<'a> {
432     type Output = ();
433     const DEFAULT: bool = true;
434
435     fn should_run(builder: &Builder, path: &Path) -> bool {
436         builder.crates("std").into_iter().any(|(_, krate_path)| {
437             path.ends_with(krate_path)
438         })
439     }
440
441     fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
442         let run = || {
443             builder.ensure(Std {
444                 stage: builder.top_stage,
445                 target
446             });
447         };
448
449         if let Some(path) = path {
450             for (_, krate_path) in builder.crates("std") {
451                 if path.ends_with(krate_path) {
452                     run();
453                 }
454             }
455         } else {
456             if builder.build.config.docs {
457                 run();
458             }
459         }
460     }
461
462     /// Compile all standard library documentation.
463     ///
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)
476         } else {
477             compiler
478         };
479
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);
484
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.
490         //
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
494         // all of them.
495         //
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));
501
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());
506
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
515                 // relative links.
516                 // FIXME: Cargo should probably do this itself.
517                 t!(fs::create_dir_all(out_dir.join(krate)));
518             }
519         }
520
521
522         build.run(&mut cargo);
523         cp_r(&my_out, &out);
524     }
525 }
526
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));
534 // }
535
536 #[derive(Serialize)]
537 pub struct Test<'a> {
538     stage: u32,
539     test: &'a str,
540 }
541
542 impl<'a> Step<'a> for Test<'a> {
543     type Output = ();
544     const DEFAULT: bool = true;
545
546     fn should_run(builder: &Builder, path: &Path) -> bool {
547         builder.crates("test").into_iter().any(|(_, krate_path)| {
548             path.ends_with(krate_path)
549         })
550     }
551
552     fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
553         let run = || {
554             builder.ensure(Test {
555                 stage: builder.top_stage,
556                 target
557             });
558         };
559
560         if let Some(path) = path {
561             for (_, krate_path) in builder.crates("test") {
562                 if path.ends_with(krate_path) {
563                     run();
564                 }
565             }
566         } else {
567             if builder.build.config.docs {
568                 run();
569             }
570         }
571     }
572
573     /// Compile all libtest documentation.
574     ///
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)
587         } else {
588             compiler
589         };
590
591         // Build libstd docs so that we generate relative links
592         builder.ensure(Std { stage, target });
593
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);
598
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));
603
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);
608         cp_r(&my_out, &out);
609     }
610 }
611
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"))
617 //          .host(true)
618 //          .default(default && build.config.docs)
619 //          .run(move |s| doc::rustc(build, s.stage, s.target));
620 // }
621 //
622
623 #[derive(Serialize)]
624 pub struct Rustc<'a> {
625     stage: u32,
626     target: &'a str,
627 }
628
629 impl<'a> Step<'a> for Rustc<'a> {
630     type Output = ();
631     const DEFAULT: bool = true;
632     const ONLY_HOSTS: bool = true;
633
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)
637         })
638     }
639
640     fn make_run(builder: &Builder, path: Option<&Path>, _host: &str, target: &str) {
641         let run = || {
642             builder.ensure(Rustc {
643                 stage: builder.top_stage,
644                 target
645             });
646         };
647
648         if let Some(path) = path {
649             for (_, krate_path) in builder.crates("rustc-main") {
650                 if path.ends_with(krate_path) {
651                     run();
652                 }
653             }
654         } else {
655             if builder.build.config.compiler_docs {
656                 run();
657             }
658         }
659     }
660
661     /// Generate all compiler documentation.
662     ///
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)
675         } else {
676             compiler
677         };
678
679         // Build libstd docs so that we generate relative links
680         builder.ensure(Std { stage, target });
681
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);
686
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));
691
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());
696
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");
703         } else {
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);
709             }
710         }
711
712         build.run(&mut cargo);
713         cp_r(&my_out, &out);
714     }
715 }
716
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)
721 //      .host(true)
722 //      .run(move |s| doc::error_index(build, s.target));
723
724 #[derive(Serialize)]
725 pub struct ErrorIndex<'a> {
726     target: &'a str,
727 }
728
729 impl<'a> Step<'a> for ErrorIndex<'a> {
730     type Output = ();
731     const DEFAULT: bool = true;
732     const ONLY_HOSTS: bool = true;
733
734     fn should_run(_builder: &Builder, path: &Path) -> bool {
735         path.ends_with("src/tools/error_index_generator")
736     }
737
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.
741             return;
742         }
743
744         builder.ensure(ErrorIndex {
745             target,
746         });
747     }
748
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;
754
755         builder.ensure(compile::Rustc {
756             compiler: builder.compiler(0, &build.build),
757             target,
758         });
759
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);
765         index.arg("html");
766         index.arg(out.join("error-index.html"));
767
768         // FIXME: shouldn't have to pass this env var
769         index.env("CFG_BUILD", &build.build);
770
771         build.run(&mut index);
772     }
773 }
774
775 // rules.doc("doc-unstable-book-gen", "src/tools/unstable-book-gen")
776 //      .dep(move |s| {
777 //          s.name("tool-unstable-book-gen")
778 //           .host(&build.build)
779 //           .target(&build.build)
780 //           .stage(0)
781 //      })
782 //      .dep(move |s| s.name("libstd-link"))
783 //      .default(build.config.docs)
784 //      .host(true)
785 //      .run(move |s| doc::unstable_book_gen(build, s.target));
786
787 #[derive(Serialize)]
788 pub struct UnstableBookGen<'a> {
789     target: &'a str,
790 }
791
792 impl<'a> Step<'a> for UnstableBookGen<'a> {
793     type Output = ();
794     const DEFAULT: bool = true;
795     const ONLY_HOSTS: bool = true;
796
797     fn should_run(_builder: &Builder, path: &Path) -> bool {
798         path.ends_with("src/doc/unstable-book")
799     }
800
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.
804             return;
805         }
806
807         builder.ensure(UnstableBookGen {
808             target,
809         });
810     }
811
812     fn run(self, builder: &Builder) {
813         let build = builder.build;
814         let target = self.target;
815
816         builder.ensure(compile::Std {
817             compiler: builder.compiler(builder.top_stage, &build.build),
818             target,
819         });
820
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"));
828         cmd.arg(out);
829
830         build.run(&mut cmd);
831     }
832 }
833
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));
838         } else {
839             // handle directory junctions on windows by falling back to
840             // `remove_dir`.
841             try!(fs::remove_file(dst).or_else(|_| {
842                 fs::remove_dir(dst)
843             }));
844         }
845     }
846
847     symlink_dir(src, dst)
848 }