]> git.lizzy.rs Git - rust.git/blob - src/rustdoc_ng/rustdoc_ng.rs
4bff60c71b73236973c4c56bb1d1633692725624
[rust.git] / src / rustdoc_ng / rustdoc_ng.rs
1 // Copyright 2012-2013 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 #[link(name = "rustdoc_ng",
12        vers = "0.8-pre",
13        uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6",
14        url = "https://github.com/mozilla/rust/tree/master/src/rustdoc_ng")];
15
16 #[desc = "rustdoc, the Rust documentation extractor"];
17 #[license = "MIT/ASL2"];
18 #[crate_type = "lib"];
19
20 extern mod syntax;
21 extern mod rustc;
22 extern mod extra;
23
24 use extra::serialize::Encodable;
25 use extra::time;
26 use std::cell::Cell;
27 use std::rt::io;
28 use std::rt::io::Writer;
29 use std::rt::io::file::FileInfo;
30
31 pub mod clean;
32 pub mod core;
33 pub mod doctree;
34 pub mod fold;
35 pub mod html {
36     pub mod render;
37     pub mod layout;
38     pub mod markdown;
39     pub mod format;
40 }
41 pub mod passes;
42 pub mod plugins;
43 pub mod visit_ast;
44
45 pub static SCHEMA_VERSION: &'static str = "0.8.0";
46
47 local_data_key!(pub ctxtkey: @core::DocContext)
48
49 enum OutputFormat {
50     HTML, JSON
51 }
52
53 pub fn main() {
54     main_args(std::os::args());
55 }
56
57 pub fn main_args(args: &[~str]) {
58     use extra::getopts::groups::*;
59
60     let opts = ~[
61         optmulti("L", "library-path", "directory to add to crate search path",
62                  "DIR"),
63         optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
64         optmulti("", "passes", "space separated list of passes to also run",
65                  "PASSES"),
66         optmulti("", "plugins", "space separated list of plugins to also load",
67                  "PLUGINS"),
68         optflag("h", "help", "show this help message"),
69         optflag("", "nodefaults", "don't run the default passes"),
70         optopt("o", "output", "where to place the output", "PATH"),
71     ];
72
73     let matches = getopts(args.tail(), opts).unwrap();
74
75     let myusage = || {
76         println(usage(format!("{} [options] [html|json] <crate>", args[0]), opts));
77     };
78
79     if matches.opt_present("h") || matches.opt_present("help") {
80         myusage();
81         return;
82     }
83
84     let (format, cratefile) = match matches.free.clone() {
85         [~"json", crate] => (JSON, crate),
86         [~"html", crate] => (HTML, crate),
87         [s, _] => {
88             println!("Unknown output format: `{}`", s);
89             myusage();
90             exit(1);
91         }
92         [_, .._] => {
93             println!("Expected exactly one crate to process");
94             myusage();
95             exit(1);
96         }
97         _ => {
98             println!("Expected an output format and then one crate");
99             myusage();
100             exit(1);
101         }
102     };
103
104     // First, parse the crate and extract all relevant information.
105     let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
106     let cr = Cell::new(Path(cratefile));
107     info2!("starting to run rustc");
108     let crate = do std::task::try {
109         let cr = cr.take();
110         core::run_core(libs.take(), &cr)
111     }.unwrap();
112     info2!("finished with rustc");
113
114     // Process all of the crate attributes, extracting plugin metadata along
115     // with the passes which we are supposed to run.
116     let mut default_passes = !matches.opt_present("nodefaults");
117     let mut passes = matches.opt_strs("passes");
118     let mut plugins = matches.opt_strs("plugins");
119     match crate.module.get_ref().doc_list() {
120         Some(nested) => {
121             for inner in nested.iter() {
122                 match *inner {
123                     clean::Word(~"no_default_passes") => {
124                         default_passes = false;
125                     }
126                     clean::NameValue(~"passes", ref value) => {
127                         for pass in value.word_iter() {
128                             passes.push(pass.to_owned());
129                         }
130                     }
131                     clean::NameValue(~"plugins", ref value) => {
132                         for p in value.word_iter() {
133                             plugins.push(p.to_owned());
134                         }
135                     }
136                     _ => {}
137                 }
138             }
139         }
140         None => {}
141     }
142     if default_passes {
143         passes.unshift(~"collapse-docs");
144         passes.unshift(~"unindent-comments");
145     }
146
147     // Load all plugins/passes into a PluginManager
148     let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
149     for pass in passes.iter() {
150         let plugin = match pass.as_slice() {
151             "strip-hidden" => passes::strip_hidden,
152             "unindent-comments" => passes::unindent_comments,
153             "collapse-docs" => passes::collapse_docs,
154             "collapse-privacy" => passes::collapse_privacy,
155             s => { error!("unknown pass %s, skipping", s); loop },
156         };
157         pm.add_plugin(plugin);
158     }
159     info2!("loading plugins...");
160     for pname in plugins.move_iter() {
161         pm.load_plugin(pname);
162     }
163
164     // Run everything!
165     info2!("Executing passes/plugins");
166     let (crate, res) = pm.run_plugins(crate);
167
168     info2!("going to format");
169     let started = time::precise_time_ns();
170     let output = matches.opt_str("o").map(|s| Path(*s));
171     match format {
172         HTML => { html::render::run(crate, output.unwrap_or(Path("doc"))) }
173         JSON => { jsonify(crate, res, output.unwrap_or(Path("doc.json"))) }
174     }
175     let ended = time::precise_time_ns();
176     info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1000000000f64);
177 }
178
179 fn jsonify(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
180     // {
181     //   "schema": version,
182     //   "crate": { parsed crate ... },
183     //   "plugins": { output of plugins ... }
184     // }
185     let mut json = ~extra::treemap::TreeMap::new();
186     json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
187     let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
188
189     // FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
190     // straight to the Rust JSON representation.
191     let crate_json_str = do std::io::with_str_writer |w| {
192         crate.encode(&mut extra::json::Encoder(w));
193     };
194     let crate_json = match extra::json::from_str(crate_json_str) {
195         Ok(j) => j,
196         Err(_) => fail!("Rust generated JSON is invalid??")
197     };
198
199     json.insert(~"crate", crate_json);
200     json.insert(~"plugins", extra::json::Object(plugins_json));
201
202     let mut file = dst.open_writer(io::Create).unwrap();
203     let output = extra::json::Object(json).to_str();
204     file.write(output.as_bytes());
205 }
206
207 fn exit(status: int) -> ! {
208     #[fixed_stack_segment]; #[inline(never)];
209     use std::libc;
210     unsafe { libc::exit(status as libc::c_int) }
211 }