1 // Copyright 2012-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.
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.
11 #![crate_name = "rustdoc"]
12 #![unstable(feature = "rustdoc", issue = "27812")]
13 #![crate_type = "dylib"]
14 #![crate_type = "rlib"]
15 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
16 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
17 html_root_url
= "https://doc.rust-lang.org/nightly/",
18 html_playground_url
= "https://play.rust-lang.org/")]
19 #![cfg_attr(not(stage0), deny(warnings))]
21 #![feature(box_patterns)]
22 #![feature(box_syntax)]
24 #![feature(rustc_private)]
25 #![feature(set_stdio)]
26 #![feature(slice_patterns)]
27 #![feature(staged_api)]
30 #![feature(question_mark)]
36 extern crate rustc_const_eval
;
37 extern crate rustc_trans
;
38 extern crate rustc_driver
;
39 extern crate rustc_resolve
;
40 extern crate rustc_lint
;
41 extern crate rustc_back
;
42 extern crate rustc_metadata
;
43 extern crate serialize
;
44 #[macro_use] extern crate syntax;
45 extern crate test
as testing
;
46 extern crate rustc_unicode
;
47 #[macro_use] extern crate log;
49 extern crate serialize
as rustc_serialize
; // used by deriving
51 use std
::cell
::RefCell
;
52 use std
::collections
::HashMap
;
53 use std
::default::Default
;
56 use std
::path
::PathBuf
;
59 use std
::sync
::mpsc
::channel
;
61 use externalfiles
::ExternalHtml
;
62 use rustc
::session
::search_paths
::SearchPaths
;
63 use rustc
::session
::config
::{ErrorOutputType, RustcOptGroup, nightly_options}
;
66 pub mod externalfiles
;
89 use clean
::Attributes
;
91 type Pass
= (&'
static str, // name
92 fn(clean
::Crate
) -> plugins
::PluginResult
, // fn
93 &'
static str); // description
95 const PASSES
: &'
static [Pass
] = &[
96 ("strip-hidden", passes
::strip_hidden
,
97 "strips all doc(hidden) items from the output"),
98 ("unindent-comments", passes
::unindent_comments
,
99 "removes excess indentation on comments in order for markdown to like it"),
100 ("collapse-docs", passes
::collapse_docs
,
101 "concatenates all document attributes into one document attribute"),
102 ("strip-private", passes
::strip_private
,
103 "strips all private items from a crate which cannot be seen externally, \
104 implies strip-priv-imports"),
105 ("strip-priv-imports", passes
::strip_priv_imports
,
106 "strips all private import statements (`use`, `extern crate`) from a crate"),
109 const DEFAULT_PASSES
: &'
static [&'
static str] = &[
116 thread_local
!(pub static ANALYSISKEY
: Rc
<RefCell
<Option
<core
::CrateAnalysis
>>> = {
117 Rc
::new(RefCell
::new(None
))
126 const STACK_SIZE
: usize = 32000000; // 32MB
127 let res
= std
::thread
::Builder
::new().stack_size(STACK_SIZE
).spawn(move || {
128 let s
= env
::args().collect
::<Vec
<_
>>();
130 }).unwrap().join().unwrap_or(101);
131 process
::exit(res
as i32);
134 fn stable(g
: getopts
::OptGroup
) -> RustcOptGroup { RustcOptGroup::stable(g) }
135 fn unstable(g
: getopts
::OptGroup
) -> RustcOptGroup { RustcOptGroup::unstable(g) }
137 pub fn opts() -> Vec
<RustcOptGroup
> {
140 stable(optflag("h", "help", "show this help message")),
141 stable(optflag("V", "version", "print rustdoc's version")),
142 stable(optflag("v", "verbose", "use verbose output")),
143 stable(optopt("r", "input-format", "the input type of the specified file",
145 stable(optopt("w", "output-format", "the output type to write",
147 stable(optopt("o", "output", "where to place the output", "PATH")),
148 stable(optopt("", "crate-name", "specify the name of this crate", "NAME")),
149 stable(optmulti("L", "library-path", "directory to add to crate search path",
151 stable(optmulti("", "cfg", "pass a --cfg to rustc", "")),
152 stable(optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH")),
153 stable(optmulti("", "plugin-path", "directory to load plugins from", "DIR")),
154 stable(optmulti("", "passes",
155 "list of passes to also run, you might want \
156 to pass it multiple times; a value of `list` \
157 will print available passes",
159 stable(optmulti("", "plugins", "space separated list of plugins to also load",
161 stable(optflag("", "no-defaults", "don't run the default passes")),
162 stable(optflag("", "test", "run code examples as tests")),
163 stable(optmulti("", "test-args", "arguments to pass to the test runner",
165 stable(optopt("", "target", "target triple to document", "TRIPLE")),
166 stable(optmulti("", "markdown-css",
167 "CSS files to include via <link> in a rendered Markdown file",
169 stable(optmulti("", "html-in-header",
170 "files to include inline in the <head> section of a rendered Markdown file \
171 or generated documentation",
173 stable(optmulti("", "html-before-content",
174 "files to include inline between <body> and the content of a rendered \
175 Markdown file or generated documentation",
177 stable(optmulti("", "html-after-content",
178 "files to include inline between the content and </body> of a rendered \
179 Markdown file or generated documentation",
181 stable(optopt("", "markdown-playground-url",
182 "URL to send code snippets to", "URL")),
183 stable(optflag("", "markdown-no-toc", "don't include table of contents")),
184 unstable(optopt("e", "extend-css",
185 "to redefine some css rules with a given file to generate doc with your \
186 own theme", "PATH")),
187 unstable(optmulti("Z", "",
188 "internal and debugging options (only on nightly build)", "FLAG")),
192 pub fn usage(argv0
: &str) {
194 getopts
::usage(&format
!("{} [options] <input>", argv0
),
196 .map(|x
| x
.opt_group
)
197 .collect
::<Vec
<getopts
::OptGroup
>>()));
200 pub fn main_args(args
: &[String
]) -> isize {
201 let all_groups
: Vec
<getopts
::OptGroup
> = opts()
203 .map(|x
| x
.opt_group
)
205 let matches
= match getopts
::getopts(&args
[1..], &all_groups
) {
212 // Check for unstable options.
213 nightly_options
::check_nightly_options(&matches
, &opts());
215 if matches
.opt_present("h") || matches
.opt_present("help") {
218 } else if matches
.opt_present("version") {
219 rustc_driver
::version("rustdoc", &matches
);
223 if matches
.opt_strs("passes") == ["list"] {
224 println
!("Available passes for running rustdoc:");
225 for &(name
, _
, description
) in PASSES
{
226 println
!("{:>20} - {}", name
, description
);
228 println
!("\nDefault passes for rustdoc:");
229 for &name
in DEFAULT_PASSES
{
230 println
!("{:>20}", name
);
235 if matches
.free
.is_empty() {
236 println
!("expected an input file to act on");
238 } if matches
.free
.len() > 1 {
239 println
!("only one input file may be specified");
242 let input
= &matches
.free
[0];
244 let mut libs
= SearchPaths
::new();
245 for s
in &matches
.opt_strs("L") {
246 libs
.add_path(s
, ErrorOutputType
::default());
248 let externs
= match parse_externs(&matches
) {
256 let test_args
= matches
.opt_strs("test-args");
257 let test_args
: Vec
<String
> = test_args
.iter()
258 .flat_map(|s
| s
.split_whitespace())
259 .map(|s
| s
.to_string())
262 let should_test
= matches
.opt_present("test");
263 let markdown_input
= input
.ends_with(".md") || input
.ends_with(".markdown");
265 let output
= matches
.opt_str("o").map(|s
| PathBuf
::from(&s
));
266 let css_file_extension
= matches
.opt_str("e").map(|s
| PathBuf
::from(&s
));
267 let cfgs
= matches
.opt_strs("cfg");
269 if let Some(ref p
) = css_file_extension
{
271 println
!("{}", "--extend-css option must take a css file as input");
276 let external_html
= match ExternalHtml
::load(
277 &matches
.opt_strs("html-in-header"),
278 &matches
.opt_strs("html-before-content"),
279 &matches
.opt_strs("html-after-content")) {
283 let crate_name
= matches
.opt_str("crate-name");
285 match (should_test
, markdown_input
) {
287 return markdown
::test(input
, cfgs
, libs
, externs
, test_args
)
290 return test
::run(input
, cfgs
, libs
, externs
, test_args
, crate_name
)
292 (false, true) => return markdown
::render(input
,
293 output
.unwrap_or(PathBuf
::from("doc")),
294 &matches
, &external_html
,
295 !matches
.opt_present("markdown-no-toc")),
298 let out
= match acquire_input(input
, externs
, &matches
) {
301 println
!("input error: {}", s
);
305 let Output { krate, passes, }
= out
;
306 info
!("going to format");
307 match matches
.opt_str("w").as_ref().map(|s
| &**s
) {
308 Some("html") | None
=> {
309 html
::render
::run(krate
, &external_html
,
310 output
.unwrap_or(PathBuf
::from("doc")),
311 passes
.into_iter().collect(),
313 .expect("failed to generate documentation")
316 println
!("unknown output format: {}", s
);
324 /// Looks inside the command line arguments to extract the relevant input format
325 /// and files and then generates the necessary rustdoc output for formatting.
326 fn acquire_input(input
: &str,
327 externs
: core
::Externs
,
328 matches
: &getopts
::Matches
) -> Result
<Output
, String
> {
329 match matches
.opt_str("r").as_ref().map(|s
| &**s
) {
330 Some("rust") => Ok(rust_input(input
, externs
, matches
)),
331 Some(s
) => Err(format
!("unknown input format: {}", s
)),
333 Ok(rust_input(input
, externs
, matches
))
338 /// Extracts `--extern CRATE=PATH` arguments from `matches` and
339 /// returns a `HashMap` mapping crate names to their paths or else an
341 fn parse_externs(matches
: &getopts
::Matches
) -> Result
<core
::Externs
, String
> {
342 let mut externs
= HashMap
::new();
343 for arg
in &matches
.opt_strs("extern") {
344 let mut parts
= arg
.splitn(2, '
='
);
345 let name
= parts
.next().ok_or("--extern value must not be empty".to_string())?
;
346 let location
= parts
.next()
347 .ok_or("--extern value must be of the format `foo=bar`"
349 let name
= name
.to_string();
350 externs
.entry(name
).or_insert(vec
![]).push(location
.to_string());
355 /// Interprets the input file as a rust source file, passing it through the
356 /// compiler all the way through the analysis passes. The rustdoc output is then
357 /// generated from the cleaned AST of the crate.
359 /// This form of input will run all of the plug/cleaning passes
360 fn rust_input(cratefile
: &str, externs
: core
::Externs
, matches
: &getopts
::Matches
) -> Output
{
361 let mut default_passes
= !matches
.opt_present("no-defaults");
362 let mut passes
= matches
.opt_strs("passes");
363 let mut plugins
= matches
.opt_strs("plugins");
365 // First, parse the crate and extract all relevant information.
366 let mut paths
= SearchPaths
::new();
367 for s
in &matches
.opt_strs("L") {
368 paths
.add_path(s
, ErrorOutputType
::default());
370 let cfgs
= matches
.opt_strs("cfg");
371 let triple
= matches
.opt_str("target");
373 let cr
= PathBuf
::from(cratefile
);
374 info
!("starting to run rustc");
376 let (tx
, rx
) = channel();
377 rustc_driver
::monitor(move || {
378 use rustc
::session
::config
::Input
;
380 tx
.send(core
::run_core(paths
, cfgs
, externs
, Input
::File(cr
),
383 let (mut krate
, analysis
) = rx
.recv().unwrap();
384 info
!("finished with rustc");
385 let mut analysis
= Some(analysis
);
386 ANALYSISKEY
.with(|s
| {
387 *s
.borrow_mut() = analysis
.take();
390 if let Some(name
) = matches
.opt_str("crate-name") {
394 // Process all of the crate attributes, extracting plugin metadata along
395 // with the passes which we are supposed to run.
396 for attr
in krate
.module
.as_ref().unwrap().attrs
.list("doc") {
398 clean
::Word(ref w
) if "no_default_passes" == *w
=> {
399 default_passes
= false;
401 clean
::NameValue(ref name
, ref value
) => {
402 let sink
= match &name
[..] {
403 "passes" => &mut passes
,
404 "plugins" => &mut plugins
,
407 for p
in value
.split_whitespace() {
408 sink
.push(p
.to_string());
416 for name
in DEFAULT_PASSES
.iter().rev() {
417 passes
.insert(0, name
.to_string());
421 // Load all plugins/passes into a PluginManager
422 let path
= matches
.opt_str("plugin-path")
423 .unwrap_or("/tmp/rustdoc/plugins".to_string());
424 let mut pm
= plugins
::PluginManager
::new(PathBuf
::from(path
));
425 for pass
in &passes
{
426 let plugin
= match PASSES
.iter()
427 .position(|&(p
, _
, _
)| {
430 Some(i
) => PASSES
[i
].1,
432 error
!("unknown pass {}, skipping", *pass
);
436 pm
.add_plugin(plugin
);
438 info
!("loading plugins...");
439 for pname
in plugins
{
440 pm
.load_plugin(pname
);
444 info
!("Executing passes/plugins");
445 let krate
= pm
.run_plugins(krate
);
446 Output { krate: krate, passes: passes }