1 // Copyright 2018 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 use std::collections::{BTreeMap, BTreeSet};
12 use std::path::PathBuf;
15 use errors::emitter::ColorConfig;
17 use rustc::lint::Level;
18 use rustc::session::early_error;
19 use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
20 use rustc::session::config::{nightly_options, build_codegen_options, build_debugging_options,
21 get_cmd_lint_options};
22 use rustc::session::search_paths::SearchPaths;
24 use rustc_target::spec::TargetTriple;
25 use syntax::edition::Edition;
27 use core::new_handler;
28 use externalfiles::ExternalHtml;
30 use html::markdown::IdMap;
32 use passes::{self, DefaultPassOption};
36 // Basic options / Options passed directly to rustc
38 /// The crate root or Markdown file to load.
40 /// Output directory to generate docs into. Defaults to `doc`.
42 /// The name of the crate being documented.
43 pub crate_name: Option<String>,
44 /// How to format errors and warnings.
45 pub error_format: ErrorOutputType,
46 /// Library search paths to hand to the compiler.
47 pub libs: SearchPaths,
48 /// The list of external crates to link against.
50 /// List of `cfg` flags to hand to the compiler. Always includes `rustdoc`.
51 pub cfgs: Vec<String>,
52 /// Codegen options to hand to the compiler.
53 pub codegen_options: CodegenOptions,
54 /// Debugging (`-Z`) options to pass to the compiler.
55 pub debugging_options: DebuggingOptions,
56 /// The target used to compile the crate against.
57 pub target: Option<TargetTriple>,
58 /// Edition used when reading the crate. Defaults to "2015". Also used by default when
59 /// compiling doctests from the crate.
61 /// The path to the sysroot. Used during the compilation process.
62 pub maybe_sysroot: Option<PathBuf>,
63 /// Linker to use when building doctests.
64 pub linker: Option<PathBuf>,
65 /// Lint information passed over the command-line.
66 pub lint_opts: Vec<(String, Level)>,
67 /// Whether to ask rustc to describe the lints it knows. Practically speaking, this will not be
68 /// used, since we abort if we have no input file, but it's included for completeness.
69 pub describe_lints: bool,
70 /// What level to cap lints at.
71 pub lint_cap: Option<Level>,
73 // Options specific to running doctests
75 /// Whether we should run doctests instead of generating docs.
76 pub should_test: bool,
77 /// List of arguments to pass to the test harness, if running tests.
78 pub test_args: Vec<String>,
80 // Options that affect the documentation process
82 /// The selected default set of passes to use.
84 /// Be aware: This option can come both from the CLI and from crate attributes!
85 pub default_passes: DefaultPassOption,
86 /// Any passes manually selected by the user.
88 /// Be aware: This option can come both from the CLI and from crate attributes!
89 pub manual_passes: Vec<String>,
90 /// Whether to display warnings during doc generation or while gathering doctests. By default,
91 /// all non-rustdoc-specific lints are allowed when generating docs.
92 pub display_warnings: bool,
93 /// A pre-populated `IdMap` with the default headings and any headings added by Markdown files
94 /// processed by `external_html`.
97 // Options that alter generated documentation pages
99 /// External files to insert into generated pages.
100 pub external_html: ExternalHtml,
101 /// If present, playground URL to use in the "Run" button added to code samples.
103 /// Be aware: This option can come both from the CLI and from crate attributes!
104 pub playground_url: Option<String>,
105 /// Crate version to note on the sidebar of generated docs.
106 pub crate_version: Option<String>,
107 /// Whether to sort modules alphabetically on a module page instead of using declaration order.
108 /// `true` by default.
110 /// FIXME(misdreavus): the flag name is `--sort-modules-by-appearance` but the meaning is
111 /// inverted once read
112 pub sort_modules_alphabetically: bool,
113 /// List of themes to extend the docs with. Original argument name is included to assist in
114 /// displaying errors if it fails a theme check.
115 pub themes: Vec<PathBuf>,
116 /// If present, CSS file that contains rules to add to the default CSS.
117 pub extension_css: Option<PathBuf>,
118 /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`.
119 pub extern_html_root_urls: BTreeMap<String, String>,
120 /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
121 pub resource_suffix: String,
122 /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
125 /// FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted
127 pub enable_minification: bool,
128 /// Whether to create an index page in the root of the output directory. If this is true but
129 /// `enable_index_page` is None, generate a static listing of crates instead.
130 pub enable_index_page: bool,
131 /// A file to use as the index page at the root of the output directory. Overrides
132 /// `enable_index_page` to be true if set.
133 pub index_page: Option<PathBuf>,
135 // Options specific to reading standalone Markdown files
137 /// Whether to generate a table of contents on the output file when reading a standalone
139 pub markdown_no_toc: bool,
140 /// Additional CSS files to link in pages generated from standlone Markdown files.
141 pub markdown_css: Vec<String>,
142 /// If present, playground URL to use in the "Run" button added to code samples generated from
143 /// standalone Markdown files. If not present, `playground_url` is used.
144 pub markdown_playground_url: Option<String>,
148 /// Parses the given command-line for options. If an error message or other early-return has
149 /// been printed, returns `Err` with the exit code.
150 pub fn from_matches(matches: &getopts::Matches) -> Result<Options, isize> {
151 // Check for unstable options.
152 nightly_options::check_nightly_options(&matches, &opts());
154 if matches.opt_present("h") || matches.opt_present("help") {
157 } else if matches.opt_present("version") {
158 rustc_driver::version("rustdoc", &matches);
162 if matches.opt_strs("passes") == ["list"] {
163 println!("Available passes for running rustdoc:");
164 for pass in passes::PASSES {
165 println!("{:>20} - {}", pass.name(), pass.description());
167 println!("\nDefault passes for rustdoc:");
168 for &name in passes::DEFAULT_PASSES {
169 println!("{:>20}", name);
171 println!("\nPasses run with `--document-private-items`:");
172 for &name in passes::DEFAULT_PRIVATE_PASSES {
173 println!("{:>20}", name);
178 let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
179 Some("auto") => ColorConfig::Auto,
180 Some("always") => ColorConfig::Always,
181 Some("never") => ColorConfig::Never,
182 None => ColorConfig::Auto,
184 early_error(ErrorOutputType::default(),
185 &format!("argument for --color must be `auto`, `always` or `never` \
186 (instead was `{}`)", arg));
189 let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
190 Some("human") => ErrorOutputType::HumanReadable(color),
191 Some("json") => ErrorOutputType::Json(false),
192 Some("pretty-json") => ErrorOutputType::Json(true),
193 Some("short") => ErrorOutputType::Short(color),
194 None => ErrorOutputType::HumanReadable(color),
196 early_error(ErrorOutputType::default(),
197 &format!("argument for --error-format must be `human`, `json` or \
198 `short` (instead was `{}`)", arg));
202 let codegen_options = build_codegen_options(matches, error_format);
203 let debugging_options = build_debugging_options(matches, error_format);
205 let diag = new_handler(error_format,
207 debugging_options.treat_err_as_bug,
208 debugging_options.ui_testing);
210 // check for deprecated options
211 check_deprecated_options(&matches, &diag);
213 let to_check = matches.opt_strs("theme-checker");
214 if !to_check.is_empty() {
215 let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
218 println!("rustdoc: [theme-checker] Starting tests!");
219 for theme_file in to_check.iter() {
220 print!(" - Checking \"{}\"...", theme_file);
221 let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag);
222 if !differences.is_empty() || !success {
225 if !differences.is_empty() {
226 println!("{}", differences.join("\n"));
238 if matches.free.is_empty() {
239 diag.struct_err("missing file operand").emit();
242 if matches.free.len() > 1 {
243 diag.struct_err("too many file operands").emit();
246 let input = PathBuf::from(&matches.free[0]);
248 let mut libs = SearchPaths::new();
249 for s in &matches.opt_strs("L") {
250 libs.add_path(s, error_format);
252 let externs = match parse_externs(&matches) {
255 diag.struct_err(&err).emit();
259 let extern_html_root_urls = match parse_extern_html_roots(&matches) {
262 diag.struct_err(err).emit();
267 let test_args = matches.opt_strs("test-args");
268 let test_args: Vec<String> = test_args.iter()
269 .flat_map(|s| s.split_whitespace())
270 .map(|s| s.to_string())
273 let should_test = matches.opt_present("test");
275 let output = matches.opt_str("o")
276 .map(|s| PathBuf::from(&s))
277 .unwrap_or_else(|| PathBuf::from("doc"));
278 let mut cfgs = matches.opt_strs("cfg");
279 cfgs.push("rustdoc".to_string());
281 let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s));
283 if let Some(ref p) = extension_css {
285 diag.struct_err("option --extend-css argument must be a file").emit();
290 let mut themes = Vec::new();
291 if matches.opt_present("themes") {
292 let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css"));
294 for (theme_file, theme_s) in matches.opt_strs("themes")
296 .map(|s| (PathBuf::from(&s), s.to_owned())) {
297 if !theme_file.is_file() {
298 diag.struct_err("option --themes arguments must all be files").emit();
301 let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag);
302 if !success || !ret.is_empty() {
303 diag.struct_err(&format!("invalid theme: \"{}\"", theme_s))
304 .help("check what's wrong with the --theme-checker option")
308 themes.push(theme_file);
312 let mut id_map = html::markdown::IdMap::new();
313 id_map.populate(html::render::initial_ids());
314 let external_html = match ExternalHtml::load(
315 &matches.opt_strs("html-in-header"),
316 &matches.opt_strs("html-before-content"),
317 &matches.opt_strs("html-after-content"),
318 &matches.opt_strs("markdown-before-content"),
319 &matches.opt_strs("markdown-after-content"), &diag, &mut id_map) {
321 None => return Err(3),
324 let edition = matches.opt_str("edition").unwrap_or("2015".to_string());
325 let edition = match edition.parse() {
328 diag.struct_err("could not parse edition").emit();
333 match matches.opt_str("w").as_ref().map(|s| &**s) {
334 Some("html") | None => {}
336 diag.struct_err(&format!("unknown output format: {}", s)).emit();
341 let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s));
342 if let Some(ref index_page) = index_page {
343 if !index_page.is_file() {
344 diag.struct_err("option `--index-page` argument must be a file").emit();
349 let target = matches.opt_str("target").map(|target| {
350 if target.ends_with(".json") {
351 TargetTriple::TargetPath(PathBuf::from(target))
353 TargetTriple::TargetTriple(target)
357 let default_passes = if matches.opt_present("no-defaults") {
358 passes::DefaultPassOption::None
359 } else if matches.opt_present("document-private-items") {
360 passes::DefaultPassOption::Private
362 passes::DefaultPassOption::Default
364 let manual_passes = matches.opt_strs("passes");
366 let crate_name = matches.opt_str("crate-name");
367 let playground_url = matches.opt_str("playground-url");
368 let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
369 let display_warnings = matches.opt_present("display-warnings");
370 let linker = matches.opt_str("linker").map(PathBuf::from);
371 let sort_modules_alphabetically = !matches.opt_present("sort-modules-by-appearance");
372 let resource_suffix = matches.opt_str("resource-suffix").unwrap_or_default();
373 let enable_minification = !matches.opt_present("disable-minification");
374 let markdown_no_toc = matches.opt_present("markdown-no-toc");
375 let markdown_css = matches.opt_strs("markdown-css");
376 let markdown_playground_url = matches.opt_str("markdown-playground-url");
377 let crate_version = matches.opt_str("crate-version");
378 let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some();
380 let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
408 sort_modules_alphabetically,
411 extern_html_root_urls,
418 markdown_playground_url,
422 /// Returns whether the file given as `self.input` is a Markdown file.
423 pub fn markdown_input(&self) -> bool {
424 self.input.extension()
425 .map_or(false, |e| e == "md" || e == "markdown")
429 /// Prints deprecation warnings for deprecated options
430 fn check_deprecated_options(matches: &getopts::Matches, diag: &errors::Handler) {
431 let deprecated_flags = [
438 for flag in deprecated_flags.into_iter() {
439 if matches.opt_present(flag) {
440 let mut err = diag.struct_warn(&format!("the '{}' flag is considered deprecated",
442 err.warn("please see https://github.com/rust-lang/rust/issues/44136");
444 if *flag == "no-defaults" {
445 err.help("you may want to use --document-private-items");
453 /// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
454 /// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
455 /// describing the issue.
456 fn parse_extern_html_roots(matches: &getopts::Matches)
457 -> Result<BTreeMap<String, String>, &'static str>
459 let mut externs = BTreeMap::new();
460 for arg in &matches.opt_strs("extern-html-root-url") {
461 let mut parts = arg.splitn(2, '=');
462 let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
463 let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
464 externs.insert(name.to_string(), url.to_string());
470 /// Extracts `--extern CRATE=PATH` arguments from `matches` and
471 /// returns a map mapping crate names to their paths or else an
473 // FIXME(eddyb) This shouldn't be duplicated with `rustc::session`.
474 fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
475 let mut externs: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
476 for arg in &matches.opt_strs("extern") {
477 let mut parts = arg.splitn(2, '=');
478 let name = parts.next().ok_or("--extern value must not be empty".to_string())?;
479 let location = parts.next().map(|s| s.to_string());
480 if location.is_none() && !nightly_options::is_unstable_enabled(matches) {
481 return Err("the `-Z unstable-options` flag must also be passed to \
482 enable `--extern crate_name` without `=path`".to_string());
484 let name = name.to_string();
485 externs.entry(name).or_default().insert(location);
487 Ok(Externs::new(externs))