]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/doc.rs
Merge branch 'refactor-select' of https://github.com/aravind-pg/rust into update...
[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, rust by example, 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::{PathBuf, Path};
24
25 use Mode;
26 use build_helper::up_to_date;
27
28 use util::{cp_r, symlink_dir};
29 use builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
30 use tool::Tool;
31 use compile;
32 use cache::{INTERNER, Interned};
33
34 macro_rules! book {
35     ($($name:ident, $path:expr, $book_name:expr;)+) => {
36         $(
37             #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
38         pub struct $name {
39             target: Interned<String>,
40         }
41
42         impl Step for $name {
43             type Output = ();
44             const DEFAULT: bool = true;
45
46             fn should_run(run: ShouldRun) -> ShouldRun {
47                 let builder = run.builder;
48                 run.path($path).default_condition(builder.build.config.docs)
49             }
50
51             fn make_run(run: RunConfig) {
52                 run.builder.ensure($name {
53                     target: run.target,
54                 });
55             }
56
57             fn run(self, builder: &Builder) {
58                 builder.ensure(Rustbook {
59                     target: self.target,
60                     name: INTERNER.intern_str($book_name),
61                 })
62             }
63         }
64         )+
65     }
66 }
67
68 book!(
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";
73 );
74
75 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
76 struct Rustbook {
77     target: Interned<String>,
78     name: Interned<String>,
79 }
80
81 impl Step for Rustbook {
82     type Output = ();
83
84     // rustbook is never directly called, and only serves as a shim for the nomicon and the
85     // reference.
86     fn should_run(run: ShouldRun) -> ShouldRun {
87         run.never()
88     }
89
90     /// Invoke `rustbook` for `target` for the doc book `name`.
91     ///
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 {
97             target: self.target,
98             name: self.name,
99             src: INTERNER.intern_path(src),
100         });
101     }
102 }
103
104 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
105 pub struct UnstableBook {
106     target: Interned<String>,
107 }
108
109 impl Step for UnstableBook {
110     type Output = ();
111     const DEFAULT: bool = true;
112
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)
116     }
117
118     fn make_run(run: RunConfig) {
119         run.builder.ensure(UnstableBook {
120             target: run.target,
121         });
122     }
123
124     fn run(self, builder: &Builder) {
125         builder.ensure(UnstableBookGen {
126             target: self.target,
127         });
128         builder.ensure(RustbookSrc {
129             target: self.target,
130             name: INTERNER.intern_str("unstable-book"),
131             src: builder.build.md_doc_out(self.target),
132         })
133     }
134 }
135
136 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
137 pub struct CargoBook {
138     target: Interned<String>,
139     name: Interned<String>,
140 }
141
142 impl Step for CargoBook {
143     type Output = ();
144     const DEFAULT: bool = true;
145
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)
149     }
150
151     fn make_run(run: RunConfig) {
152         run.builder.ensure(CargoBook {
153             target: run.target,
154             name: INTERNER.intern_str("cargo"),
155         });
156     }
157
158     fn run(self, builder: &Builder) {
159         let build = builder.build;
160
161         let target = self.target;
162         let name = self.name;
163         let src = build.src.join("src/tools/cargo/src/doc");
164
165         let out = build.doc_out(target);
166         t!(fs::create_dir_all(&out));
167
168         let out = out.join(name);
169
170         println!("Cargo Book ({}) - {}", target, name);
171
172         let _ = fs::remove_dir_all(&out);
173
174         build.run(builder.tool_cmd(Tool::Rustbook)
175                        .arg("build")
176                        .arg(&src)
177                        .arg("-d")
178                        .arg(out));
179     }
180 }
181
182 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
183 struct RustbookSrc {
184     target: Interned<String>,
185     name: Interned<String>,
186     src: Interned<PathBuf>,
187 }
188
189 impl Step for RustbookSrc {
190     type Output = ();
191
192     fn should_run(run: ShouldRun) -> ShouldRun {
193         run.never()
194     }
195
196     /// Invoke `rustbook` for `target` for the doc book `name` from the `src` path.
197     ///
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;
204         let src = self.src;
205         let out = build.doc_out(target);
206         t!(fs::create_dir_all(&out));
207
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) {
213             return
214         }
215         println!("Rustbook ({}) - {}", target, name);
216         let _ = fs::remove_dir_all(&out);
217         build.run(builder.tool_cmd(Tool::Rustbook)
218                        .arg("build")
219                        .arg(&src)
220                        .arg("-d")
221                        .arg(out));
222     }
223 }
224
225 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
226 pub struct TheBook {
227     compiler: Compiler,
228     target: Interned<String>,
229     name: &'static str,
230 }
231
232 impl Step for TheBook {
233     type Output = ();
234     const DEFAULT: bool = true;
235
236     fn should_run(run: ShouldRun) -> ShouldRun {
237         let builder = run.builder;
238         run.path("src/doc/book").default_condition(builder.build.config.docs)
239     }
240
241     fn make_run(run: RunConfig) {
242         run.builder.ensure(TheBook {
243             compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
244             target: run.target,
245             name: "book",
246         });
247     }
248
249     /// Build the book and associated stuff.
250     ///
251     /// We need to build:
252     ///
253     /// * Book (first edition)
254     /// * Book (second edition)
255     /// * Version info and CSS
256     /// * Index page
257     /// * Redirect pages
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 {
265             target,
266             name: INTERNER.intern_string(format!("{}/first-edition", name)),
267         });
268
269         // build book second edition
270         builder.ensure(Rustbook {
271             target,
272             name: INTERNER.intern_string(format!("{}/second-edition", name)),
273         });
274
275         // build the version info page and CSS
276         builder.ensure(Standalone {
277             compiler,
278             target,
279         });
280
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);
285
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"))) {
289             let file = t!(file);
290             let path = file.path();
291             let path = path.to_str().unwrap();
292
293             invoke_rustdoc(builder, compiler, target, path);
294         }
295     }
296 }
297
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);
301
302     let path = build.src.join("src/doc").join(markdown);
303
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");
307
308     let mut cmd = builder.rustdoc_cmd(compiler.host);
309
310     let out = out.join("book");
311
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/")
318         .arg("-o").arg(&out)
319         .arg(&path)
320         .arg("--markdown-css")
321         .arg("../rust.css");
322
323     build.run(&mut cmd);
324 }
325
326 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
327 pub struct Standalone {
328     compiler: Compiler,
329     target: Interned<String>,
330 }
331
332 impl Step for Standalone {
333     type Output = ();
334     const DEFAULT: bool = true;
335
336     fn should_run(run: ShouldRun) -> ShouldRun {
337         let builder = run.builder;
338         run.path("src/doc").default_condition(builder.build.config.docs)
339     }
340
341     fn make_run(run: RunConfig) {
342         run.builder.ensure(Standalone {
343             compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
344             target: run.target,
345         });
346     }
347
348     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
349     /// for the `target` into `out`.
350     ///
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.
354     ///
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));
363
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")));
368
369         let version_input = build.src.join("src/doc/version_info.html.template");
370         let version_info = out.join("version_info.html");
371
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()));
379         }
380
381         for file in t!(fs::read_dir(build.src.join("src/doc"))) {
382             let file = t!(file);
383             let path = file.path();
384             let filename = path.file_name().unwrap().to_str().unwrap();
385             if !filename.ends_with(".md") || filename == "README.md" {
386                 continue
387             }
388
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) {
397                 continue
398             }
399
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/")
406                .arg("-o").arg(&out)
407                .arg(&path);
408
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");
413             } else {
414                 cmd.arg("--markdown-css").arg("rust.css");
415             }
416             build.run(&mut cmd);
417         }
418     }
419 }
420
421 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
422 pub struct Std {
423     pub stage: u32,
424     pub target: Interned<String>,
425 }
426
427 impl Step for Std {
428     type Output = ();
429     const DEFAULT: bool = true;
430
431     fn should_run(run: ShouldRun) -> ShouldRun {
432         let builder = run.builder;
433         run.all_krates("std").default_condition(builder.build.config.docs)
434     }
435
436     fn make_run(run: RunConfig) {
437         run.builder.ensure(Std {
438             stage: run.builder.top_stage,
439             target: run.target
440         });
441     }
442
443     /// Compile all standard library documentation.
444     ///
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)
458         } else {
459             compiler
460         };
461
462         builder.ensure(compile::Std { compiler, target });
463         let out_dir = build.stage_out(compiler, Mode::Libstd)
464                            .join(target).join("doc");
465
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.
471         //
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
475         // all of them.
476         //
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));
482
483         let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
484         compile::std_cargo(build, &compiler, target, &mut cargo);
485
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
494                 // relative links.
495                 // FIXME: Cargo should probably do this itself.
496                 t!(fs::create_dir_all(out_dir.join(krate)));
497             }
498         }
499
500
501         build.run(&mut cargo);
502         cp_r(&my_out, &out);
503     }
504 }
505
506 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
507 pub struct Test {
508     stage: u32,
509     target: Interned<String>,
510 }
511
512 impl Step for Test {
513     type Output = ();
514     const DEFAULT: bool = true;
515
516     fn should_run(run: ShouldRun) -> ShouldRun {
517         let builder = run.builder;
518         run.krate("test").default_condition(builder.config.compiler_docs)
519     }
520
521     fn make_run(run: RunConfig) {
522         run.builder.ensure(Test {
523             stage: run.builder.top_stage,
524             target: run.target,
525         });
526     }
527
528     /// Compile all libtest documentation.
529     ///
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)
543         } else {
544             compiler
545         };
546
547         // Build libstd docs so that we generate relative links
548         builder.ensure(Std { stage, target });
549
550         builder.ensure(compile::Test { compiler, target });
551         let out_dir = build.stage_out(compiler, Mode::Libtest)
552                            .join(target).join("doc");
553
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));
558
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);
562         cp_r(&my_out, &out);
563     }
564 }
565
566 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
567 pub struct Rustc {
568     stage: u32,
569     target: Interned<String>,
570 }
571
572 impl Step for Rustc {
573     type Output = ();
574     const DEFAULT: bool = true;
575     const ONLY_HOSTS: bool = true;
576
577     fn should_run(run: ShouldRun) -> ShouldRun {
578         let builder = run.builder;
579         run.krate("rustc-main").default_condition(builder.build.config.docs)
580     }
581
582     fn make_run(run: RunConfig) {
583         run.builder.ensure(Rustc {
584             stage: run.builder.top_stage,
585             target: run.target,
586         });
587     }
588
589     /// Generate all compiler documentation.
590     ///
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)
604         } else {
605             compiler
606         };
607
608         // Build libstd docs so that we generate relative links
609         builder.ensure(Std { stage, target });
610
611         builder.ensure(compile::Rustc { compiler, target });
612         let out_dir = build.stage_out(compiler, Mode::Librustc)
613                            .join(target).join("doc");
614
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));
619
620         let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
621         compile::rustc_cargo(build, &mut cargo);
622
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");
627         } else {
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);
633             }
634         }
635
636         build.run(&mut cargo);
637         cp_r(&my_out, &out);
638     }
639 }
640
641 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
642 pub struct ErrorIndex {
643     target: Interned<String>,
644 }
645
646 impl Step for ErrorIndex {
647     type Output = ();
648     const DEFAULT: bool = true;
649     const ONLY_HOSTS: bool = true;
650
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)
654     }
655
656     fn make_run(run: RunConfig) {
657         run.builder.ensure(ErrorIndex {
658             target: run.target,
659         });
660     }
661
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;
667
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);
672         index.arg("html");
673         index.arg(out.join("error-index.html"));
674
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());
678
679         build.run(&mut index);
680     }
681 }
682
683 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
684 pub struct UnstableBookGen {
685     target: Interned<String>,
686 }
687
688 impl Step for UnstableBookGen {
689     type Output = ();
690     const DEFAULT: bool = true;
691     const ONLY_HOSTS: bool = true;
692
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)
696     }
697
698     fn make_run(run: RunConfig) {
699         run.builder.ensure(UnstableBookGen {
700             target: run.target,
701         });
702     }
703
704     fn run(self, builder: &Builder) {
705         let build = builder.build;
706         let target = self.target;
707
708         builder.ensure(compile::Std {
709             compiler: builder.compiler(builder.top_stage, build.build),
710             target,
711         });
712
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"));
719         cmd.arg(out);
720
721         build.run(&mut cmd);
722     }
723 }
724
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));
729         } else {
730             // handle directory junctions on windows by falling back to
731             // `remove_dir`.
732             try!(fs::remove_file(dst).or_else(|_| {
733                 fs::remove_dir(dst)
734             }));
735         }
736     }
737
738     symlink_dir(src, dst)
739 }