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)]
23 #![feature(dotdot_in_tuple_patterns)]
25 #![feature(rustc_private)]
26 #![feature(set_stdio)]
27 #![feature(slice_patterns)]
28 #![feature(staged_api)]
31 #![cfg_attr(stage0, feature(question_mark))]
37 extern crate rustc_const_eval
;
38 extern crate rustc_const_math
;
39 extern crate rustc_data_structures
;
40 extern crate rustc_trans
;
41 extern crate rustc_driver
;
42 extern crate rustc_resolve
;
43 extern crate rustc_lint
;
44 extern crate rustc_back
;
45 extern crate rustc_metadata
;
46 extern crate serialize
;
47 #[macro_use] extern crate syntax;
48 extern crate syntax_pos
;
49 extern crate test
as testing
;
50 extern crate rustc_unicode
;
51 #[macro_use] extern crate log;
52 extern crate rustc_errors
as errors
;
54 extern crate serialize
as rustc_serialize
; // used by deriving
56 use std
::collections
::{BTreeMap, BTreeSet}
;
57 use std
::default::Default
;
59 use std
::path
::PathBuf
;
61 use std
::sync
::mpsc
::channel
;
63 use externalfiles
::ExternalHtml
;
64 use rustc
::session
::search_paths
::SearchPaths
;
65 use rustc
::session
::config
::{ErrorOutputType
, RustcOptGroup
, nightly_options
,
69 pub mod externalfiles
;
92 use clean
::Attributes
;
96 renderinfo
: html
::render
::RenderInfo
,
101 const STACK_SIZE
: usize = 32_000_000; // 32MB
102 let res
= std
::thread
::Builder
::new().stack_size(STACK_SIZE
).spawn(move || {
103 let s
= env
::args().collect
::<Vec
<_
>>();
105 }).unwrap().join().unwrap_or(101);
106 process
::exit(res
as i32);
109 fn stable(g
: getopts
::OptGroup
) -> RustcOptGroup { RustcOptGroup::stable(g) }
110 fn unstable(g
: getopts
::OptGroup
) -> RustcOptGroup { RustcOptGroup::unstable(g) }
112 pub fn opts() -> Vec
<RustcOptGroup
> {
115 stable(optflag("h", "help", "show this help message")),
116 stable(optflag("V", "version", "print rustdoc's version")),
117 stable(optflag("v", "verbose", "use verbose output")),
118 stable(optopt("r", "input-format", "the input type of the specified file",
120 stable(optopt("w", "output-format", "the output type to write",
122 stable(optopt("o", "output", "where to place the output", "PATH")),
123 stable(optopt("", "crate-name", "specify the name of this crate", "NAME")),
124 stable(optmulti("L", "library-path", "directory to add to crate search path",
126 stable(optmulti("", "cfg", "pass a --cfg to rustc", "")),
127 stable(optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH")),
128 stable(optmulti("", "plugin-path", "directory to load plugins from", "DIR")),
129 stable(optmulti("", "passes",
130 "list of passes to also run, you might want \
131 to pass it multiple times; a value of `list` \
132 will print available passes",
134 stable(optmulti("", "plugins", "space separated list of plugins to also load",
136 stable(optflag("", "no-defaults", "don't run the default passes")),
137 stable(optflag("", "test", "run code examples as tests")),
138 stable(optmulti("", "test-args", "arguments to pass to the test runner",
140 stable(optopt("", "target", "target triple to document", "TRIPLE")),
141 stable(optmulti("", "markdown-css",
142 "CSS files to include via <link> in a rendered Markdown file",
144 stable(optmulti("", "html-in-header",
145 "files to include inline in the <head> section of a rendered Markdown file \
146 or generated documentation",
148 stable(optmulti("", "html-before-content",
149 "files to include inline between <body> and the content of a rendered \
150 Markdown file or generated documentation",
152 stable(optmulti("", "html-after-content",
153 "files to include inline between the content and </body> of a rendered \
154 Markdown file or generated documentation",
156 stable(optopt("", "markdown-playground-url",
157 "URL to send code snippets to", "URL")),
158 stable(optflag("", "markdown-no-toc", "don't include table of contents")),
159 unstable(optopt("e", "extend-css",
160 "to redefine some css rules with a given file to generate doc with your \
161 own theme", "PATH")),
162 unstable(optmulti("Z", "",
163 "internal and debugging options (only on nightly build)", "FLAG")),
164 stable(optopt("", "sysroot", "Override the system root", "PATH")),
168 pub fn usage(argv0
: &str) {
170 getopts
::usage(&format
!("{} [options] <input>", argv0
),
172 .map(|x
| x
.opt_group
)
173 .collect
::<Vec
<getopts
::OptGroup
>>()));
176 pub fn main_args(args
: &[String
]) -> isize {
177 let all_groups
: Vec
<getopts
::OptGroup
> = opts()
179 .map(|x
| x
.opt_group
)
181 let matches
= match getopts
::getopts(&args
[1..], &all_groups
) {
188 // Check for unstable options.
189 nightly_options
::check_nightly_options(&matches
, &opts());
191 if matches
.opt_present("h") || matches
.opt_present("help") {
194 } else if matches
.opt_present("version") {
195 rustc_driver
::version("rustdoc", &matches
);
199 if matches
.opt_strs("passes") == ["list"] {
200 println
!("Available passes for running rustdoc:");
201 for &(name
, _
, description
) in passes
::PASSES
{
202 println
!("{:>20} - {}", name
, description
);
204 println
!("\nDefault passes for rustdoc:");
205 for &name
in passes
::DEFAULT_PASSES
{
206 println
!("{:>20}", name
);
211 if matches
.free
.is_empty() {
212 println
!("expected an input file to act on");
215 if matches
.free
.len() > 1 {
216 println
!("only one input file may be specified");
219 let input
= &matches
.free
[0];
221 let mut libs
= SearchPaths
::new();
222 for s
in &matches
.opt_strs("L") {
223 libs
.add_path(s
, ErrorOutputType
::default());
225 let externs
= match parse_externs(&matches
) {
233 let test_args
= matches
.opt_strs("test-args");
234 let test_args
: Vec
<String
> = test_args
.iter()
235 .flat_map(|s
| s
.split_whitespace())
236 .map(|s
| s
.to_string())
239 let should_test
= matches
.opt_present("test");
240 let markdown_input
= input
.ends_with(".md") || input
.ends_with(".markdown");
242 let output
= matches
.opt_str("o").map(|s
| PathBuf
::from(&s
));
243 let css_file_extension
= matches
.opt_str("e").map(|s
| PathBuf
::from(&s
));
244 let cfgs
= matches
.opt_strs("cfg");
246 if let Some(ref p
) = css_file_extension
{
248 println
!("{}", "--extend-css option must take a css file as input");
253 let external_html
= match ExternalHtml
::load(
254 &matches
.opt_strs("html-in-header"),
255 &matches
.opt_strs("html-before-content"),
256 &matches
.opt_strs("html-after-content")) {
260 let crate_name
= matches
.opt_str("crate-name");
262 match (should_test
, markdown_input
) {
264 return markdown
::test(input
, cfgs
, libs
, externs
, test_args
)
267 return test
::run(input
, cfgs
, libs
, externs
, test_args
, crate_name
)
269 (false, true) => return markdown
::render(input
,
270 output
.unwrap_or(PathBuf
::from("doc")),
271 &matches
, &external_html
,
272 !matches
.opt_present("markdown-no-toc")),
275 let out
= match acquire_input(input
, externs
, &matches
) {
278 println
!("input error: {}", s
);
282 let Output { krate, passes, renderinfo }
= out
;
283 info
!("going to format");
284 match matches
.opt_str("w").as_ref().map(|s
| &**s
) {
285 Some("html") | None
=> {
286 html
::render
::run(krate
, &external_html
,
287 output
.unwrap_or(PathBuf
::from("doc")),
288 passes
.into_iter().collect(),
291 .expect("failed to generate documentation")
294 println
!("unknown output format: {}", s
);
302 /// Looks inside the command line arguments to extract the relevant input format
303 /// and files and then generates the necessary rustdoc output for formatting.
304 fn acquire_input(input
: &str,
306 matches
: &getopts
::Matches
) -> Result
<Output
, String
> {
307 match matches
.opt_str("r").as_ref().map(|s
| &**s
) {
308 Some("rust") => Ok(rust_input(input
, externs
, matches
)),
309 Some(s
) => Err(format
!("unknown input format: {}", s
)),
311 Ok(rust_input(input
, externs
, matches
))
316 /// Extracts `--extern CRATE=PATH` arguments from `matches` and
317 /// returns a map mapping crate names to their paths or else an
319 fn parse_externs(matches
: &getopts
::Matches
) -> Result
<Externs
, String
> {
320 let mut externs
= BTreeMap
::new();
321 for arg
in &matches
.opt_strs("extern") {
322 let mut parts
= arg
.splitn(2, '
='
);
323 let name
= parts
.next().ok_or("--extern value must not be empty".to_string())?
;
324 let location
= parts
.next()
325 .ok_or("--extern value must be of the format `foo=bar`"
327 let name
= name
.to_string();
328 externs
.entry(name
).or_insert_with(BTreeSet
::new
).insert(location
.to_string());
330 Ok(Externs
::new(externs
))
333 /// Interprets the input file as a rust source file, passing it through the
334 /// compiler all the way through the analysis passes. The rustdoc output is then
335 /// generated from the cleaned AST of the crate.
337 /// This form of input will run all of the plug/cleaning passes
338 fn rust_input(cratefile
: &str, externs
: Externs
, matches
: &getopts
::Matches
) -> Output
{
339 let mut default_passes
= !matches
.opt_present("no-defaults");
340 let mut passes
= matches
.opt_strs("passes");
341 let mut plugins
= matches
.opt_strs("plugins");
343 // First, parse the crate and extract all relevant information.
344 let mut paths
= SearchPaths
::new();
345 for s
in &matches
.opt_strs("L") {
346 paths
.add_path(s
, ErrorOutputType
::default());
348 let cfgs
= matches
.opt_strs("cfg");
349 let triple
= matches
.opt_str("target");
350 let maybe_sysroot
= matches
.opt_str("sysroot").map(PathBuf
::from
);
352 let cr
= PathBuf
::from(cratefile
);
353 info
!("starting to run rustc");
355 let (tx
, rx
) = channel();
356 rustc_driver
::monitor(move || {
357 use rustc
::session
::config
::Input
;
359 tx
.send(core
::run_core(paths
, cfgs
, externs
, Input
::File(cr
),
360 triple
, maybe_sysroot
)).unwrap();
362 let (mut krate
, renderinfo
) = rx
.recv().unwrap();
363 info
!("finished with rustc");
365 if let Some(name
) = matches
.opt_str("crate-name") {
369 // Process all of the crate attributes, extracting plugin metadata along
370 // with the passes which we are supposed to run.
371 for attr
in krate
.module
.as_ref().unwrap().attrs
.list("doc") {
373 clean
::Word(ref w
) if "no_default_passes" == *w
=> {
374 default_passes
= false;
376 clean
::NameValue(ref name
, ref value
) => {
377 let sink
= match &name
[..] {
378 "passes" => &mut passes
,
379 "plugins" => &mut plugins
,
382 for p
in value
.split_whitespace() {
383 sink
.push(p
.to_string());
391 for name
in passes
::DEFAULT_PASSES
.iter().rev() {
392 passes
.insert(0, name
.to_string());
396 // Load all plugins/passes into a PluginManager
397 let path
= matches
.opt_str("plugin-path")
398 .unwrap_or("/tmp/rustdoc/plugins".to_string());
399 let mut pm
= plugins
::PluginManager
::new(PathBuf
::from(path
));
400 for pass
in &passes
{
401 let plugin
= match passes
::PASSES
.iter()
402 .position(|&(p
, ..)| {
405 Some(i
) => passes
::PASSES
[i
].1,
407 error
!("unknown pass {}, skipping", *pass
);
411 pm
.add_plugin(plugin
);
413 info
!("loading plugins...");
414 for pname
in plugins
{
415 pm
.load_plugin(pname
);
419 info
!("Executing passes/plugins");
420 let krate
= pm
.run_plugins(krate
);
421 Output { krate: krate, renderinfo: renderinfo, passes: passes }