diff --git a/src/bin/bench.rs b/src/bin/bench.rs index 7e658997d28..224f5ab3cc4 100644 --- a/src/bin/bench.rs +++ b/src/bin/bench.rs @@ -84,6 +84,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustdoc_args: None, target_rustc_args: None, }, }; diff --git a/src/bin/build.rs b/src/bin/build.rs index 27bcf57b122..5db738b18f0 100644 --- a/src/bin/build.rs +++ b/src/bin/build.rs @@ -81,6 +81,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustdoc_args: None, target_rustc_args: None, }; diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 144e020a028..3c31678284d 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -81,6 +81,7 @@ macro_rules! each_subcommand{ ($mac:ident) => ({ $mac!(read_manifest); $mac!(run); $mac!(rustc); + $mac!(rustdoc); $mac!(search); $mac!(test); $mac!(uninstall); diff --git a/src/bin/doc.rs b/src/bin/doc.rs index 16fd12430ec..df110f93bc1 100644 --- a/src/bin/doc.rs +++ b/src/bin/doc.rs @@ -70,6 +70,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { deps: !options.flag_no_deps, }, target_rustc_args: None, + target_rustdoc_args: None, }, }; diff --git a/src/bin/install.rs b/src/bin/install.rs index db96c81b41f..141956179f2 100644 --- a/src/bin/install.rs +++ b/src/bin/install.rs @@ -95,6 +95,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { filter: ops::CompileFilter::new(false, &options.flag_bin, &[], &options.flag_example, &[]), target_rustc_args: None, + target_rustdoc_args: None, }; let source = if let Some(url) = options.flag_git { diff --git a/src/bin/run.rs b/src/bin/run.rs index d765967ddda..797b120a589 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -80,6 +80,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { bins: &bins, examples: &examples, } }, + target_rustdoc_args: None, target_rustc_args: None, }; diff --git a/src/bin/rustc.rs b/src/bin/rustc.rs index 924cfb6365e..c4ae2326835 100644 --- a/src/bin/rustc.rs +++ b/src/bin/rustc.rs @@ -84,6 +84,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustdoc_args: None, target_rustc_args: options.arg_opts.as_ref().map(|a| &a[..]), }; diff --git a/src/bin/rustdoc.rs b/src/bin/rustdoc.rs new file mode 100644 index 00000000000..e62adc87fc5 --- /dev/null +++ b/src/bin/rustdoc.rs @@ -0,0 +1,96 @@ +use cargo::ops; +use cargo::util::{CliResult, Config}; +use cargo::util::important_paths::{find_root_manifest_for_cwd}; + +#[derive(RustcDecodable)] +struct Options { + arg_opts: Vec, + flag_target: Option, + flag_features: Vec, + flag_jobs: Option, + flag_manifest_path: Option, + flag_no_default_features: bool, + flag_open: bool, + flag_verbose: bool, + flag_release: bool, + flag_quiet: bool, + flag_color: Option, + flag_package: Option, + flag_lib: bool, + flag_bin: Vec, + flag_example: Vec, + flag_test: Vec, + flag_bench: Vec, +} + +pub const USAGE: &'static str = " +Build a package's documentation, using specified custom flags. + +Usage: + cargo rustdoc [options] [--] [...] + +Options: + -h, --help Print this message + --open Opens the docs in a browser after the operation + -p SPEC, --package SPEC Package to document + -j N, --jobs N The number of jobs to run in parallel + --lib Build only this package's library + --bin NAME Build only the specified binary + --example NAME Build only the specified example + --test NAME Build only the specified test target + --bench NAME Build only the specified benchmark target + --release Build artifacts in release mode, with optimizations + --features FEATURES Space-separated list of features to also build + --no-default-features Do not build the `default` feature + --target TRIPLE Build for the target triple + --manifest-path PATH Path to the manifest to document + -v, --verbose Use verbose output + -q, --quiet No output printed to stdout + --color WHEN Coloring: auto, always, never + +The specified target for the current package (or package specified by SPEC if +provided) will be documented with the specified ... being passed to the +final rustdoc invocation. Dependencies will not be documented as part of this +command. Note that rustdoc will still unconditionally receive arguments such +as -L, --extern, and --crate-type, and the specified ... will simply be +added to the rustdoc invocation. + +If the --package argument is given, then SPEC is a package id specification +which indicates which package should be documented. If it is not given, then the +current package is documented. For more information on SPEC and its format, see +the `cargo help pkgid` command. +"; + +pub fn execute(options: Options, config: &Config) -> CliResult> { + try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet)); + try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..]))); + + let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path)); + + let mut doc_opts = ops::DocOptions { + open_result: options.flag_open, + compile_opts: ops::CompileOptions { + config: config, + jobs: options.flag_jobs, + target: options.flag_target.as_ref().map(|t| &t[..]), + features: &options.flag_features, + no_default_features: options.flag_no_default_features, + spec: &options.flag_package.map_or(Vec::new(), |s| vec![s]), + exec_engine: None, + release: options.flag_release, + filter: ops::CompileFilter::new(options.flag_lib, + &options.flag_bin, + &options.flag_test, + &options.flag_example, + &options.flag_bench), + mode: ops::CompileMode::Doc { deps: false }, + target_rustdoc_args: Some(&options.arg_opts), + target_rustc_args: None, + }, + }; + + try!(ops::doc(&root, &mut doc_opts)); + + Ok(None) +} + diff --git a/src/bin/test.rs b/src/bin/test.rs index 09981e0bed8..5afb9b8e8bd 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -90,6 +90,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustdoc_args: None, target_rustc_args: None, }, }; diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 68f3d2d4c1d..ef9fa5433fc 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -112,6 +112,7 @@ pub struct Profile { pub lto: bool, pub codegen_units: Option, // None = use rustc default pub rustc_args: Option>, + pub rustdoc_args: Option>, pub debuginfo: bool, pub debug_assertions: bool, pub rpath: bool, @@ -474,6 +475,7 @@ impl Default for Profile { lto: false, codegen_units: None, rustc_args: None, + rustdoc_args: None, debuginfo: false, debug_assertions: false, rpath: false, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 547aa8d3790..073d5929335 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -57,6 +57,8 @@ pub struct CompileOptions<'a> { pub release: bool, /// Mode for this compile. pub mode: CompileMode, + /// Extra arguments to be passed to rustdoc (for main crate and dependencies) + pub target_rustdoc_args: Option<&'a [String]>, /// The specified target will be compiled with all the available arguments, /// note that this only accounts for the *final* invocation of rustc pub target_rustc_args: Option<&'a [String]>, @@ -145,6 +147,7 @@ pub fn compile_pkg<'a>(root_package: &Package, let CompileOptions { config, jobs, target, spec, features, no_default_features, release, mode, ref filter, ref exec_engine, + ref target_rustdoc_args, ref target_rustc_args } = *options; let target = target.map(|s| s.to_string()); @@ -185,29 +188,44 @@ pub fn compile_pkg<'a>(root_package: &Package, let mut package_targets = Vec::new(); let profiles = root_package.manifest().profiles(); - match *target_rustc_args { - Some(args) => { - if to_builds.len() == 1 { - let targets = try!(generate_targets(to_builds[0], profiles, - mode, filter, release)); - if targets.len() == 1 { - let (target, profile) = targets[0]; - let mut profile = profile.clone(); - profile.rustc_args = Some(args.to_vec()); - general_targets.push((target, profile)); - } else { - return Err(human("extra arguments to `rustc` can only be \ - passed to one target, consider \ - filtering\nthe package by passing e.g. \ - `--lib` or `--bin NAME` to specify \ - a single target")) - - } + match (*target_rustc_args, *target_rustdoc_args) { + (Some(..), _) | + (_, Some(..)) if to_builds.len() != 1 => { + panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags") + } + (Some(args), _) => { + let targets = try!(generate_targets(to_builds[0], profiles, + mode, filter, release)); + if targets.len() == 1 { + let (target, profile) = targets[0]; + let mut profile = profile.clone(); + profile.rustc_args = Some(args.to_vec()); + general_targets.push((target, profile)); + } else { + return Err(human("extra arguments to `rustc` can only be \ + passed to one target, consider \ + filtering\nthe package by passing e.g. \ + `--lib` or `--bin NAME` to specify \ + a single target")) + } + } + (None, Some(args)) => { + let targets = try!(generate_targets(to_builds[0], profiles, + mode, filter, release)); + if targets.len() == 1 { + let (target, profile) = targets[0]; + let mut profile = profile.clone(); + profile.rustdoc_args = Some(args.to_vec()); + general_targets.push((target, profile)); } else { - panic!("`rustc` should not accept multiple `-p` flags") + return Err(human("extra arguments to `rustdoc` can only be \ + passed to one target, consider \ + filtering\nthe package by passing e.g. \ + `--lib` or `--bin NAME` to specify \ + a single target")) } } - None => { + (None, None) => { for &to_build in to_builds.iter() { let targets = try!(generate_targets(to_build, profiles, mode, filter, release)); diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 635aaf5c34e..76eab5396b4 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -220,6 +220,7 @@ fn run_verify(config: &Config, pkg: &Package, tar: &Path) exec_engine: None, release: false, mode: ops::CompileMode::Build, + target_rustdoc_args: None, target_rustc_args: None, })); diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 5feb3359195..d62f8cbf1f7 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -395,6 +395,10 @@ fn rustdoc(cx: &mut Context, unit: &Unit) -> CargoResult { } } + if let Some(ref args) = unit.profile.rustdoc_args { + rustdoc.args(args); + } + try!(build_deps_args(&mut rustdoc, cx, unit)); if unit.pkg.has_custom_build() { @@ -448,6 +452,7 @@ fn build_base_args(cx: &Context, let Profile { opt_level, lto, codegen_units, ref rustc_args, debuginfo, debug_assertions, rpath, test, doc: _doc, run_custom_build, + rustdoc_args: _, } = *unit.profile; assert!(!run_custom_build); diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 430bdde9a9d..85a7befc0b8 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -941,6 +941,7 @@ fn build_profiles(profiles: &Option) -> Profiles { lto: lto.unwrap_or(profile.lto), codegen_units: codegen_units, rustc_args: None, + rustdoc_args: None, debuginfo: debug.unwrap_or(profile.debuginfo), debug_assertions: debug_assertions.unwrap_or(profile.debug_assertions), rpath: rpath.unwrap_or(profile.rpath), diff --git a/tests/test_cargo_rustdoc.rs b/tests/test_cargo_rustdoc.rs new file mode 100644 index 00000000000..83084fc4ff2 --- /dev/null +++ b/tests/test_cargo_rustdoc.rs @@ -0,0 +1,175 @@ +use std::path::MAIN_SEPARATOR as SEP; +use support::{execs, project}; +use support::{COMPILING, RUNNING, DOCUMENTING}; +use hamcrest::{assert_that}; + +fn setup() { +} + + +test!(rustdoc_simple { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustdoc").arg("-v"), + execs() + .with_status(0) + .with_stdout(format!("\ +{documenting} foo v0.0.1 ({url}) +{running} `rustdoc src{sep}lib.rs --crate-name foo \ + -o {dir}{sep}target{sep}doc \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +", + running = RUNNING, documenting = DOCUMENTING, sep = SEP, + dir = p.root().display(), url = p.url()))); +}); + +test!(rustdoc_args { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustdoc").arg("-v").arg("--").arg("--no-defaults"), + execs() + .with_status(0) + .with_stdout(format!("\ +{documenting} foo v0.0.1 ({url}) +{running} `rustdoc src{sep}lib.rs --crate-name foo \ + -o {dir}{sep}target{sep}doc \ + --no-defaults \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +", + running = RUNNING, documenting = DOCUMENTING, sep = SEP, + dir = p.root().display(), url = p.url()))); +}); + + + +test!(rustdoc_foo_with_bar_dependency { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#) + .file("src/lib.rs", r#" + extern crate bar; + pub fn foo() {} + "#); + let bar = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn baz() {} + "#); + bar.build(); + + assert_that(foo.cargo_process("rustdoc").arg("-v").arg("--").arg("--no-defaults"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} bar v0.0.1 ({url}) +{running} `rustc {bar_dir}{sep}src{sep}lib.rs [..]` +{documenting} foo v0.0.1 ({url}) +{running} `rustdoc src{sep}lib.rs --crate-name foo \ + -o {dir}{sep}target{sep}doc \ + --no-defaults \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps \ + --extern [..]` +", + running = RUNNING, compiling = COMPILING, sep = SEP, + documenting = DOCUMENTING, + dir = foo.root().display(), url = foo.url(), + bar_dir = bar.root().display()))); +}); + +test!(rustdoc_only_bar_dependency { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#) + .file("src/main.rs", r#" + extern crate bar; + fn main() { + bar::baz() + } + "#); + let bar = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn baz() {} + "#); + bar.build(); + + assert_that(foo.cargo_process("rustdoc").arg("-v").arg("-p").arg("bar") + .arg("--").arg("--no-defaults"), + execs() + .with_status(0) + .with_stdout(format!("\ +{documenting} bar v0.0.1 ({url}) +{running} `rustdoc {bar_dir}{sep}src{sep}lib.rs --crate-name bar \ + -o {dir}{sep}target{sep}doc \ + --no-defaults \ + -L dependency={dir}{sep}target{sep}debug{sep}deps \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +", + running = RUNNING, documenting = DOCUMENTING, sep = SEP, + dir = foo.root().display(), url = foo.url(), + bar_dir = bar.root().display()))); +}); + + +test!(rustdoc_same_name_err { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustdoc").arg("-v") + .arg("--").arg("--no-defaults"), + execs() + .with_status(101) + .with_stderr("Cannot document a package where a library and a \ + binary have the same name. Consider renaming one \ + or marking the target as `doc = false`")); +}); diff --git a/tests/tests.rs b/tests/tests.rs index 612d11e0344..19aaf0afff0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -57,6 +57,7 @@ mod test_cargo_read_manifest; mod test_cargo_registry; mod test_cargo_run; mod test_cargo_rustc; +mod test_cargo_rustdoc; mod test_cargo_search; mod test_cargo_test; mod test_cargo_tool_paths;