]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/doc.rs
Refactor away `inferred_obligations` from the trait selector
[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-playground-url")
316         .arg("https://play.rust-lang.org/")
317         .arg("-o").arg(&out)
318         .arg(&path)
319         .arg("--markdown-css")
320         .arg("../rust.css");
321
322     build.run(&mut cmd);
323 }
324
325 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
326 pub struct Standalone {
327     compiler: Compiler,
328     target: Interned<String>,
329 }
330
331 impl Step for Standalone {
332     type Output = ();
333     const DEFAULT: bool = true;
334
335     fn should_run(run: ShouldRun) -> ShouldRun {
336         let builder = run.builder;
337         run.path("src/doc").default_condition(builder.build.config.docs)
338     }
339
340     fn make_run(run: RunConfig) {
341         run.builder.ensure(Standalone {
342             compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
343             target: run.target,
344         });
345     }
346
347     /// Generates all standalone documentation as compiled by the rustdoc in `stage`
348     /// for the `target` into `out`.
349     ///
350     /// This will list all of `src/doc` looking for markdown files and appropriately
351     /// perform transformations like substituting `VERSION`, `SHORT_HASH`, and
352     /// `STAMP` along with providing the various header/footer HTML we've customized.
353     ///
354     /// In the end, this is just a glorified wrapper around rustdoc!
355     fn run(self, builder: &Builder) {
356         let build = builder.build;
357         let target = self.target;
358         let compiler = self.compiler;
359         println!("Documenting standalone ({})", target);
360         let out = build.doc_out(target);
361         t!(fs::create_dir_all(&out));
362
363         let favicon = build.src.join("src/doc/favicon.inc");
364         let footer = build.src.join("src/doc/footer.inc");
365         let full_toc = build.src.join("src/doc/full-toc.inc");
366         t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
367
368         let version_input = build.src.join("src/doc/version_info.html.template");
369         let version_info = out.join("version_info.html");
370
371         if !up_to_date(&version_input, &version_info) {
372             let mut info = String::new();
373             t!(t!(File::open(&version_input)).read_to_string(&mut info));
374             let info = info.replace("VERSION", &build.rust_release())
375                            .replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
376                            .replace("STAMP", build.rust_info.sha().unwrap_or(""));
377             t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
378         }
379
380         for file in t!(fs::read_dir(build.src.join("src/doc"))) {
381             let file = t!(file);
382             let path = file.path();
383             let filename = path.file_name().unwrap().to_str().unwrap();
384             if !filename.ends_with(".md") || filename == "README.md" {
385                 continue
386             }
387
388             let html = out.join(filename).with_extension("html");
389             let rustdoc = builder.rustdoc(compiler.host);
390             if up_to_date(&path, &html) &&
391                up_to_date(&footer, &html) &&
392                up_to_date(&favicon, &html) &&
393                up_to_date(&full_toc, &html) &&
394                up_to_date(&version_info, &html) &&
395                up_to_date(&rustdoc, &html) {
396                 continue
397             }
398
399             let mut cmd = builder.rustdoc_cmd(compiler.host);
400             cmd.arg("--html-after-content").arg(&footer)
401                .arg("--html-before-content").arg(&version_info)
402                .arg("--html-in-header").arg(&favicon)
403                .arg("--markdown-playground-url")
404                .arg("https://play.rust-lang.org/")
405                .arg("-o").arg(&out)
406                .arg(&path);
407
408             if filename == "not_found.md" {
409                 cmd.arg("--markdown-no-toc")
410                    .arg("--markdown-css")
411                    .arg("https://doc.rust-lang.org/rust.css");
412             } else {
413                 cmd.arg("--markdown-css").arg("rust.css");
414             }
415             build.run(&mut cmd);
416         }
417     }
418 }
419
420 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
421 pub struct Std {
422     pub stage: u32,
423     pub target: Interned<String>,
424 }
425
426 impl Step for Std {
427     type Output = ();
428     const DEFAULT: bool = true;
429
430     fn should_run(run: ShouldRun) -> ShouldRun {
431         let builder = run.builder;
432         run.all_krates("std").default_condition(builder.build.config.docs)
433     }
434
435     fn make_run(run: RunConfig) {
436         run.builder.ensure(Std {
437             stage: run.builder.top_stage,
438             target: run.target
439         });
440     }
441
442     /// Compile all standard library documentation.
443     ///
444     /// This will generate all documentation for the standard library and its
445     /// dependencies. This is largely just a wrapper around `cargo doc`.
446     fn run(self, builder: &Builder) {
447         let build = builder.build;
448         let stage = self.stage;
449         let target = self.target;
450         println!("Documenting stage{} std ({})", stage, target);
451         let out = build.doc_out(target);
452         t!(fs::create_dir_all(&out));
453         let compiler = builder.compiler(stage, build.build);
454         let rustdoc = builder.rustdoc(compiler.host);
455         let compiler = if build.force_use_stage1(compiler, target) {
456             builder.compiler(1, compiler.host)
457         } else {
458             compiler
459         };
460
461         builder.ensure(compile::Std { compiler, target });
462         let out_dir = build.stage_out(compiler, Mode::Libstd)
463                            .join(target).join("doc");
464
465         // Here what we're doing is creating a *symlink* (directory junction on
466         // Windows) to the final output location. This is not done as an
467         // optimization but rather for correctness. We've got three trees of
468         // documentation, one for std, one for test, and one for rustc. It's then
469         // our job to merge them all together.
470         //
471         // Unfortunately rustbuild doesn't know nearly as well how to merge doc
472         // trees as rustdoc does itself, so instead of actually having three
473         // separate trees we just have rustdoc output to the same location across
474         // all of them.
475         //
476         // This way rustdoc generates output directly into the output, and rustdoc
477         // will also directly handle merging.
478         let my_out = build.crate_doc_out(target);
479         build.clear_if_dirty(&my_out, &rustdoc);
480         t!(symlink_dir_force(&my_out, &out_dir));
481
482         let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
483         compile::std_cargo(build, &compiler, target, &mut cargo);
484
485         // We don't want to build docs for internal std dependencies unless
486         // in compiler-docs mode. When not in that mode, we whitelist the crates
487         // for which docs must be built.
488         if !build.config.compiler_docs {
489             cargo.arg("--no-deps");
490             for krate in &["alloc", "core", "std", "std_unicode"] {
491                 cargo.arg("-p").arg(krate);
492                 // Create all crate output directories first to make sure rustdoc uses
493                 // relative links.
494                 // FIXME: Cargo should probably do this itself.
495                 t!(fs::create_dir_all(out_dir.join(krate)));
496             }
497         }
498
499
500         build.run(&mut cargo);
501         cp_r(&my_out, &out);
502     }
503 }
504
505 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
506 pub struct Test {
507     stage: u32,
508     target: Interned<String>,
509 }
510
511 impl Step for Test {
512     type Output = ();
513     const DEFAULT: bool = true;
514
515     fn should_run(run: ShouldRun) -> ShouldRun {
516         let builder = run.builder;
517         run.krate("test").default_condition(builder.config.compiler_docs)
518     }
519
520     fn make_run(run: RunConfig) {
521         run.builder.ensure(Test {
522             stage: run.builder.top_stage,
523             target: run.target,
524         });
525     }
526
527     /// Compile all libtest documentation.
528     ///
529     /// This will generate all documentation for libtest and its dependencies. This
530     /// is largely just a wrapper around `cargo doc`.
531     fn run(self, builder: &Builder) {
532         let build = builder.build;
533         let stage = self.stage;
534         let target = self.target;
535         println!("Documenting stage{} test ({})", stage, target);
536         let out = build.doc_out(target);
537         t!(fs::create_dir_all(&out));
538         let compiler = builder.compiler(stage, build.build);
539         let rustdoc = builder.rustdoc(compiler.host);
540         let compiler = if build.force_use_stage1(compiler, target) {
541             builder.compiler(1, compiler.host)
542         } else {
543             compiler
544         };
545
546         // Build libstd docs so that we generate relative links
547         builder.ensure(Std { stage, target });
548
549         builder.ensure(compile::Test { compiler, target });
550         let out_dir = build.stage_out(compiler, Mode::Libtest)
551                            .join(target).join("doc");
552
553         // See docs in std above for why we symlink
554         let my_out = build.crate_doc_out(target);
555         build.clear_if_dirty(&my_out, &rustdoc);
556         t!(symlink_dir_force(&my_out, &out_dir));
557
558         let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc");
559         compile::test_cargo(build, &compiler, target, &mut cargo);
560         build.run(&mut cargo);
561         cp_r(&my_out, &out);
562     }
563 }
564
565 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
566 pub struct Rustc {
567     stage: u32,
568     target: Interned<String>,
569 }
570
571 impl Step for Rustc {
572     type Output = ();
573     const DEFAULT: bool = true;
574     const ONLY_HOSTS: bool = true;
575
576     fn should_run(run: ShouldRun) -> ShouldRun {
577         let builder = run.builder;
578         run.krate("rustc-main").default_condition(builder.build.config.docs)
579     }
580
581     fn make_run(run: RunConfig) {
582         run.builder.ensure(Rustc {
583             stage: run.builder.top_stage,
584             target: run.target,
585         });
586     }
587
588     /// Generate all compiler documentation.
589     ///
590     /// This will generate all documentation for the compiler libraries and their
591     /// dependencies. This is largely just a wrapper around `cargo doc`.
592     fn run(self, builder: &Builder) {
593         let build = builder.build;
594         let stage = self.stage;
595         let target = self.target;
596         println!("Documenting stage{} compiler ({})", stage, target);
597         let out = build.doc_out(target);
598         t!(fs::create_dir_all(&out));
599         let compiler = builder.compiler(stage, build.build);
600         let rustdoc = builder.rustdoc(compiler.host);
601         let compiler = if build.force_use_stage1(compiler, target) {
602             builder.compiler(1, compiler.host)
603         } else {
604             compiler
605         };
606
607         // Build libstd docs so that we generate relative links
608         builder.ensure(Std { stage, target });
609
610         builder.ensure(compile::Rustc { compiler, target });
611         let out_dir = build.stage_out(compiler, Mode::Librustc)
612                            .join(target).join("doc");
613
614         // See docs in std above for why we symlink
615         let my_out = build.crate_doc_out(target);
616         build.clear_if_dirty(&my_out, &rustdoc);
617         t!(symlink_dir_force(&my_out, &out_dir));
618
619         let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
620         compile::rustc_cargo(build, &mut cargo);
621
622         if build.config.compiler_docs {
623             // src/rustc/Cargo.toml contains a bin crate called rustc which
624             // would otherwise overwrite the docs for the real rustc lib crate.
625             cargo.arg("-p").arg("rustc_driver");
626         } else {
627             // Like with libstd above if compiler docs aren't enabled then we're not
628             // documenting internal dependencies, so we have a whitelist.
629             cargo.arg("--no-deps");
630             for krate in &["proc_macro"] {
631                 cargo.arg("-p").arg(krate);
632             }
633         }
634
635         build.run(&mut cargo);
636         cp_r(&my_out, &out);
637     }
638 }
639
640 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
641 pub struct ErrorIndex {
642     target: Interned<String>,
643 }
644
645 impl Step for ErrorIndex {
646     type Output = ();
647     const DEFAULT: bool = true;
648     const ONLY_HOSTS: bool = true;
649
650     fn should_run(run: ShouldRun) -> ShouldRun {
651         let builder = run.builder;
652         run.path("src/tools/error_index_generator").default_condition(builder.build.config.docs)
653     }
654
655     fn make_run(run: RunConfig) {
656         run.builder.ensure(ErrorIndex {
657             target: run.target,
658         });
659     }
660
661     /// Generates the HTML rendered error-index by running the
662     /// `error_index_generator` tool.
663     fn run(self, builder: &Builder) {
664         let build = builder.build;
665         let target = self.target;
666
667         println!("Documenting error index ({})", target);
668         let out = build.doc_out(target);
669         t!(fs::create_dir_all(&out));
670         let mut index = builder.tool_cmd(Tool::ErrorIndex);
671         index.arg("html");
672         index.arg(out.join("error-index.html"));
673
674         // FIXME: shouldn't have to pass this env var
675         index.env("CFG_BUILD", &build.build)
676              .env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir());
677
678         build.run(&mut index);
679     }
680 }
681
682 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
683 pub struct UnstableBookGen {
684     target: Interned<String>,
685 }
686
687 impl Step for UnstableBookGen {
688     type Output = ();
689     const DEFAULT: bool = true;
690     const ONLY_HOSTS: bool = true;
691
692     fn should_run(run: ShouldRun) -> ShouldRun {
693         let builder = run.builder;
694         run.path("src/tools/unstable-book-gen").default_condition(builder.build.config.docs)
695     }
696
697     fn make_run(run: RunConfig) {
698         run.builder.ensure(UnstableBookGen {
699             target: run.target,
700         });
701     }
702
703     fn run(self, builder: &Builder) {
704         let build = builder.build;
705         let target = self.target;
706
707         builder.ensure(compile::Std {
708             compiler: builder.compiler(builder.top_stage, build.build),
709             target,
710         });
711
712         println!("Generating unstable book md files ({})", target);
713         let out = build.md_doc_out(target).join("unstable-book");
714         t!(fs::create_dir_all(&out));
715         t!(fs::remove_dir_all(&out));
716         let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
717         cmd.arg(build.src.join("src"));
718         cmd.arg(out);
719
720         build.run(&mut cmd);
721     }
722 }
723
724 fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
725     if let Ok(m) = fs::symlink_metadata(dst) {
726         if m.file_type().is_dir() {
727             try!(fs::remove_dir_all(dst));
728         } else {
729             // handle directory junctions on windows by falling back to
730             // `remove_dir`.
731             try!(fs::remove_file(dst).or_else(|_| {
732                 fs::remove_dir(dst)
733             }));
734         }
735     }
736
737     symlink_dir(src, dst)
738 }