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