rust/src/librustdoc/rustdoc.rs

249 lines
8.0 KiB
Rust
Raw Normal View History

2013-02-28 14:15:32 +01:00
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
2012-01-15 23:28:10 +01:00
#[link(name = "rustdoc",
2013-09-22 01:25:08 +02:00
vers = "0.8",
uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6",
url = "https://github.com/mozilla/rust/tree/master/src/librustdoc")];
2012-01-15 23:28:10 +01:00
#[desc = "rustdoc, the Rust documentation extractor"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
extern mod syntax;
extern mod rustc;
extern mod extra;
2012-04-06 02:30:26 +02:00
use extra::serialize::Encodable;
use extra::time;
use extra::getopts::groups;
use std::cell::Cell;
use std::rt::io;
use std::rt::io::Writer;
use std::rt::io::file::FileInfo;
pub mod clean;
pub mod core;
pub mod doctree;
pub mod fold;
pub mod html {
pub mod render;
pub mod layout;
pub mod markdown;
pub mod format;
}
pub mod passes;
pub mod plugins;
pub mod visit_ast;
pub static SCHEMA_VERSION: &'static str = "0.8.0";
type Pass = (&'static str, // name
extern fn(clean::Crate) -> plugins::PluginResult, // fn
&'static str); // description
static PASSES: &'static [Pass] = &[
("strip-hidden", passes::strip_hidden,
"strips all doc(hidden) items from the output"),
("unindent-comments", passes::unindent_comments,
"removes excess indentation on comments in order for markdown to like it"),
("collapse-docs", passes::collapse_docs,
"concatenates all document attributes into one document attribute"),
("strip-private", passes::strip_private,
"strips all private items from a crate which cannot be seen externally"),
];
static DEFAULT_PASSES: &'static [&'static str] = &[
"unindent-comments",
"collapse-docs",
"strip-private",
];
local_data_key!(pub ctxtkey: @core::DocContext)
enum OutputFormat {
HTML, JSON
}
2012-11-19 02:56:50 +01:00
pub fn main() {
std::os::set_exit_status(main_args(std::os::args()));
}
pub fn opts() -> ~[groups::OptGroup] {
use extra::getopts::groups::*;
~[
optmulti("L", "library-path", "directory to add to crate search path",
"DIR"),
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
optmulti("", "passes", "space separated list of passes to also run, a \
value of `list` will print available passes",
"PASSES"),
optmulti("", "plugins", "space separated list of plugins to also load",
"PLUGINS"),
optflag("h", "help", "show this help message"),
optflag("", "nodefaults", "don't run the default passes"),
optopt("o", "output", "where to place the output", "PATH"),
]
}
pub fn usage(argv0: &str) {
println(groups::usage(format!("{} [options] [html|json] <crate>",
argv0), opts()));
}
2012-11-19 02:56:50 +01:00
pub fn main_args(args: &[~str]) -> int {
//use extra::getopts::groups::*;
let matches = groups::getopts(args.tail(), opts()).unwrap();
if matches.opt_present("h") || matches.opt_present("help") {
usage(args[0]);
return 0;
2012-11-19 02:56:50 +01:00
}
let mut default_passes = !matches.opt_present("nodefaults");
let mut passes = matches.opt_strs("passes");
let mut plugins = matches.opt_strs("plugins");
if passes == ~[~"list"] {
println("Available passes for running rustdoc:");
for &(name, _, description) in PASSES.iter() {
println!("{:>20s} - {}", name, description);
}
println("\nDefault passes for rustdoc:");
for &name in DEFAULT_PASSES.iter() {
println!("{:>20s}", name);
}
return;
}
let (format, cratefile) = match matches.free.clone() {
[~"json", crate] => (JSON, crate),
[~"html", crate] => (HTML, crate),
[s, _] => {
println!("Unknown output format: `{}`", s);
usage(args[0]);
return 1;
}
[_, .._] => {
println!("Expected exactly one crate to process");
usage(args[0]);
return 1;
}
_ => {
println!("Expected an output format and then one crate");
usage(args[0]);
return 1;
}
2012-11-19 02:56:50 +01:00
};
// First, parse the crate and extract all relevant information.
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
let cr = Cell::new(Path(cratefile));
info2!("starting to run rustc");
let crate = do std::task::try {
let cr = cr.take();
core::run_core(libs.take(), &cr)
}.unwrap();
info2!("finished with rustc");
// Process all of the crate attributes, extracting plugin metadata along
// with the passes which we are supposed to run.
match crate.module.get_ref().doc_list() {
Some(nested) => {
for inner in nested.iter() {
match *inner {
clean::Word(~"no_default_passes") => {
default_passes = false;
}
clean::NameValue(~"passes", ref value) => {
for pass in value.word_iter() {
passes.push(pass.to_owned());
}
}
clean::NameValue(~"plugins", ref value) => {
for p in value.word_iter() {
plugins.push(p.to_owned());
}
}
_ => {}
}
}
}
None => {}
}
if default_passes {
for name in DEFAULT_PASSES.rev_iter() {
passes.unshift(name.to_owned());
}
}
2012-11-19 02:56:50 +01:00
// Load all plugins/passes into a PluginManager
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
for pass in passes.iter() {
let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) {
Some(i) => PASSES[i].n1(),
None => {
error2!("unknown pass {}, skipping", *pass);
loop
},
};
pm.add_plugin(plugin);
}
info2!("loading plugins...");
for pname in plugins.move_iter() {
pm.load_plugin(pname);
}
2012-11-19 02:56:50 +01:00
// Run everything!
info2!("Executing passes/plugins");
let (crate, res) = pm.run_plugins(crate);
2012-11-19 02:56:50 +01:00
info2!("going to format");
let started = time::precise_time_ns();
let output = matches.opt_str("o").map(|s| Path(*s));
match format {
HTML => { html::render::run(crate, output.unwrap_or(Path("doc"))) }
JSON => { jsonify(crate, res, output.unwrap_or(Path("doc.json"))) }
}
let ended = time::precise_time_ns();
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1000000000f64);
return 0;
}
2012-11-19 02:56:50 +01:00
fn jsonify(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
// {
// "schema": version,
// "crate": { parsed crate ... },
// "plugins": { output of plugins ... }
// }
let mut json = ~extra::treemap::TreeMap::new();
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
// FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
// straight to the Rust JSON representation.
let crate_json_str = do std::io::with_str_writer |w| {
crate.encode(&mut extra::json::Encoder(w));
};
let crate_json = match extra::json::from_str(crate_json_str) {
Ok(j) => j,
Err(_) => fail!("Rust generated JSON is invalid??")
};
2012-11-19 02:56:50 +01:00
json.insert(~"crate", crate_json);
json.insert(~"plugins", extra::json::Object(plugins_json));
let mut file = dst.open_writer(io::Create).unwrap();
let output = extra::json::Object(json).to_str();
file.write(output.as_bytes());
2012-11-19 02:56:50 +01:00
}