]>
Commit | Line | Data |
---|---|---|
62682a34 | 1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | //! Implementation of the `build` subcommand, used to compile a book. | |
12 | ||
85aaf69f | 13 | use std::env; |
c34b1796 AL |
14 | use std::fs::{self, File}; |
15 | use std::io::prelude::*; | |
16 | use std::io::{self, BufWriter}; | |
17 | use std::path::{Path, PathBuf}; | |
18 | use rustc_back::tempdir::TempDir; | |
1a4d82fc JJ |
19 | |
20 | use subcommand::Subcommand; | |
21 | use term::Term; | |
c34b1796 | 22 | use error::{err, CliResult, CommandResult}; |
1a4d82fc JJ |
23 | use book; |
24 | use book::{Book, BookItem}; | |
25 | use css; | |
85aaf69f | 26 | use javascript; |
1a4d82fc JJ |
27 | |
28 | use rustdoc; | |
29 | ||
30 | struct Build; | |
31 | ||
32 | pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> { | |
33 | if name == "build" { | |
c34b1796 | 34 | Some(Box::new(Build)) |
1a4d82fc JJ |
35 | } else { |
36 | None | |
37 | } | |
38 | } | |
39 | ||
62682a34 | 40 | fn write_toc(book: &Book, current_page: &BookItem, out: &mut Write) -> io::Result<()> { |
1a4d82fc JJ |
41 | fn walk_items(items: &[BookItem], |
42 | section: &str, | |
62682a34 | 43 | current_page: &BookItem, |
c34b1796 | 44 | out: &mut Write) -> io::Result<()> { |
1a4d82fc | 45 | for (i, item) in items.iter().enumerate() { |
62682a34 | 46 | try!(walk_item(item, &format!("{}{}.", section, i + 1)[..], current_page, out)); |
1a4d82fc JJ |
47 | } |
48 | Ok(()) | |
49 | } | |
50 | fn walk_item(item: &BookItem, | |
51 | section: &str, | |
62682a34 | 52 | current_page: &BookItem, |
c34b1796 | 53 | out: &mut Write) -> io::Result<()> { |
62682a34 SL |
54 | let class_string = if item.path == current_page.path { |
55 | "class='active'" | |
56 | } else { | |
57 | "" | |
58 | }; | |
59 | ||
60 | try!(writeln!(out, "<li><a {} href='{}'><b>{}</b> {}</a>", | |
61 | class_string, | |
62 | current_page.path_to_root.join(&item.path).with_extension("html").display(), | |
1a4d82fc JJ |
63 | section, |
64 | item.title)); | |
65 | if !item.children.is_empty() { | |
66 | try!(writeln!(out, "<ul class='section'>")); | |
62682a34 | 67 | let _ = walk_items(&item.children[..], section, current_page, out); |
1a4d82fc JJ |
68 | try!(writeln!(out, "</ul>")); |
69 | } | |
70 | try!(writeln!(out, "</li>")); | |
71 | ||
72 | Ok(()) | |
73 | } | |
74 | ||
85aaf69f | 75 | try!(writeln!(out, "<div id='toc' class='mobile-hidden'>")); |
1a4d82fc | 76 | try!(writeln!(out, "<ul class='chapter'>")); |
62682a34 | 77 | try!(walk_items(&book.chapters[..], "", ¤t_page, out)); |
1a4d82fc JJ |
78 | try!(writeln!(out, "</ul>")); |
79 | try!(writeln!(out, "</div>")); | |
80 | ||
81 | Ok(()) | |
82 | } | |
83 | ||
84 | fn render(book: &Book, tgt: &Path) -> CliResult<()> { | |
85aaf69f | 85 | let tmp = try!(TempDir::new("rust-book")); |
1a4d82fc | 86 | |
c34b1796 AL |
87 | for (_section, item) in book.iter() { |
88 | let out_path = match item.path.parent() { | |
89 | Some(p) => tgt.join(p), | |
90 | None => tgt.to_path_buf(), | |
91 | }; | |
1a4d82fc | 92 | |
1a4d82fc | 93 | let src; |
85aaf69f | 94 | if env::args().len() < 3 { |
c34b1796 | 95 | src = env::current_dir().unwrap().clone(); |
1a4d82fc | 96 | } else { |
c34b1796 | 97 | src = PathBuf::from(&env::args().nth(2).unwrap()); |
1a4d82fc | 98 | } |
9346a6ac AL |
99 | // preprocess the markdown, rerouting markdown references to html |
100 | // references | |
c34b1796 AL |
101 | let mut markdown_data = String::new(); |
102 | try!(File::open(&src.join(&item.path)).and_then(|mut f| { | |
103 | f.read_to_string(&mut markdown_data) | |
104 | })); | |
105 | let preprocessed_path = tmp.path().join(item.path.file_name().unwrap()); | |
1a4d82fc | 106 | { |
85aaf69f | 107 | let urls = markdown_data.replace(".md)", ".html)"); |
c34b1796 AL |
108 | try!(File::create(&preprocessed_path).and_then(|mut f| { |
109 | f.write_all(urls.as_bytes()) | |
110 | })); | |
1a4d82fc JJ |
111 | } |
112 | ||
113 | // write the prelude to a temporary HTML file for rustdoc inclusion | |
114 | let prelude = tmp.path().join("prelude.html"); | |
115 | { | |
c34b1796 | 116 | let mut toc = BufWriter::new(try!(File::create(&prelude))); |
85aaf69f SL |
117 | try!(writeln!(&mut toc, r#"<div id="nav"> |
118 | <button id="toggle-nav"> | |
119 | <span class="sr-only">Toggle navigation</span> | |
120 | <span class="bar"></span> | |
121 | <span class="bar"></span> | |
122 | <span class="bar"></span> | |
123 | </button> | |
124 | </div>"#)); | |
62682a34 | 125 | let _ = write_toc(book, &item, &mut toc); |
1a4d82fc JJ |
126 | try!(writeln!(&mut toc, "<div id='page-wrapper'>")); |
127 | try!(writeln!(&mut toc, "<div id='page'>")); | |
128 | } | |
129 | ||
130 | // write the postlude to a temporary HTML file for rustdoc inclusion | |
131 | let postlude = tmp.path().join("postlude.html"); | |
132 | { | |
c34b1796 AL |
133 | let mut toc = BufWriter::new(try!(File::create(&postlude))); |
134 | try!(toc.write_all(javascript::JAVASCRIPT.as_bytes())); | |
1a4d82fc JJ |
135 | try!(writeln!(&mut toc, "</div></div>")); |
136 | } | |
137 | ||
c34b1796 | 138 | try!(fs::create_dir_all(&out_path)); |
1a4d82fc JJ |
139 | |
140 | let rustdoc_args: &[String] = &[ | |
141 | "".to_string(), | |
142 | preprocessed_path.display().to_string(), | |
143 | format!("-o{}", out_path.display()), | |
144 | format!("--html-before-content={}", prelude.display()), | |
145 | format!("--html-after-content={}", postlude.display()), | |
9346a6ac | 146 | format!("--markdown-playground-url=http://play.rust-lang.org"), |
1a4d82fc JJ |
147 | format!("--markdown-css={}", item.path_to_root.join("rust-book.css").display()), |
148 | "--markdown-no-toc".to_string(), | |
149 | ]; | |
150 | let output_result = rustdoc::main_args(rustdoc_args); | |
151 | if output_result != 0 { | |
152 | let message = format!("Could not execute `rustdoc` with {:?}: {}", | |
153 | rustdoc_args, output_result); | |
c34b1796 | 154 | return Err(err(&message)); |
1a4d82fc JJ |
155 | } |
156 | } | |
157 | ||
158 | // create index.html from the root README | |
159 | try!(fs::copy(&tgt.join("README.html"), &tgt.join("index.html"))); | |
9346a6ac | 160 | |
62682a34 | 161 | // Copy js for playpen |
9346a6ac AL |
162 | let mut playpen = try!(File::create(tgt.join("playpen.js"))); |
163 | let js = include_bytes!("../librustdoc/html/static/playpen.js"); | |
164 | try!(playpen.write_all(js)); | |
1a4d82fc JJ |
165 | Ok(()) |
166 | } | |
167 | ||
168 | impl Subcommand for Build { | |
169 | fn parse_args(&mut self, _: &[String]) -> CliResult<()> { | |
170 | Ok(()) | |
171 | } | |
172 | fn usage(&self) {} | |
173 | fn execute(&mut self, term: &mut Term) -> CommandResult<()> { | |
c34b1796 | 174 | let cwd = env::current_dir().unwrap(); |
1a4d82fc JJ |
175 | let src; |
176 | let tgt; | |
177 | ||
85aaf69f | 178 | if env::args().len() < 3 { |
1a4d82fc JJ |
179 | src = cwd.clone(); |
180 | } else { | |
c34b1796 | 181 | src = PathBuf::from(&env::args().nth(2).unwrap()); |
1a4d82fc JJ |
182 | } |
183 | ||
85aaf69f | 184 | if env::args().len() < 4 { |
1a4d82fc JJ |
185 | tgt = cwd.join("_book"); |
186 | } else { | |
c34b1796 | 187 | tgt = PathBuf::from(&env::args().nth(3).unwrap()); |
1a4d82fc JJ |
188 | } |
189 | ||
d9579d0f AL |
190 | // `_book` directory may already exist from previous runs. Check and |
191 | // delete it if it exists. | |
192 | for entry in try!(fs::read_dir(&cwd)) { | |
193 | let path = try!(entry).path(); | |
194 | if path == tgt { try!(fs::remove_dir_all(&tgt)) } | |
195 | } | |
c34b1796 | 196 | try!(fs::create_dir(&tgt)); |
1a4d82fc | 197 | |
c34b1796 AL |
198 | try!(File::create(&tgt.join("rust-book.css")).and_then(|mut f| { |
199 | f.write_all(css::STYLE.as_bytes()) | |
200 | })); | |
1a4d82fc | 201 | |
c34b1796 AL |
202 | let mut summary = try!(File::open(&src.join("SUMMARY.md"))); |
203 | match book::parse_summary(&mut summary, &src) { | |
1a4d82fc JJ |
204 | Ok(book) => { |
205 | // execute rustdoc on the whole book | |
85aaf69f | 206 | render(&book, &tgt) |
1a4d82fc JJ |
207 | } |
208 | Err(errors) => { | |
85aaf69f SL |
209 | let n = errors.len(); |
210 | for err in errors { | |
c34b1796 | 211 | term.err(&format!("error: {}", err)[..]); |
1a4d82fc | 212 | } |
85aaf69f | 213 | |
c34b1796 | 214 | Err(err(&format!("{} errors occurred", n))) |
1a4d82fc JJ |
215 | } |
216 | } | |
1a4d82fc JJ |
217 | } |
218 | } |