diff --git a/src/bin/test.rs b/src/bin/test.rs index e10684dd9b0..59bf0a09d1f 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -105,7 +105,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { let empty = Vec::new(); let (mode, filter); if options.flag_doc { - mode = ops::CompileMode::Build; + mode = ops::CompileMode::Doctest; filter = ops::CompileFilter::new(true, &empty, &empty, &empty, &empty); } else { mode = ops::CompileMode::Test; diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 86cd6eebd1e..530fffc7f8a 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -170,6 +170,7 @@ pub struct Profiles { pub doc: Profile, pub custom_build: Profile, pub check: Profile, + pub doctest: Profile, } /// Information about a binary, a library, an example, etc. that is part of the @@ -535,6 +536,14 @@ impl Profile { ..Profile::default_dev() } } + + pub fn default_doctest() -> Profile { + Profile { + doc: true, + test: true, + ..Profile::default_dev() + } + } } impl Default for Profile { diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 870733f96d9..afdb047ca99 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -461,6 +461,7 @@ impl<'cfg> Workspace<'cfg> { doc: Profile::default_doc(), custom_build: Profile::default_custom_build(), check: Profile::default_check(), + doctest: Profile::default_doctest(), }; for pkg in self.members().filter(|p| p.manifest_path() != root_manifest) { diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 9627c902154..b7c214a75d9 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -51,10 +51,11 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { for kind in [Kind::Host, Kind::Target].iter() { let Profiles { ref release, ref dev, ref test, ref bench, ref doc, - ref custom_build, ref test_deps, ref bench_deps, ref check + ref custom_build, ref test_deps, ref bench_deps, ref check, + ref doctest, } = *profiles; let profiles = [release, dev, test, bench, doc, custom_build, - test_deps, bench_deps, check]; + test_deps, bench_deps, check, doctest]; for profile in profiles.iter() { units.push(Unit { pkg: &pkg, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 1be87408ab4..3ca2315129c 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -69,6 +69,7 @@ pub enum CompileMode { Check, Bench, Doc { deps: bool }, + Doctest, } #[derive(Clone, Copy, PartialEq, Eq, RustcDecodable)] @@ -290,6 +291,7 @@ fn generate_targets<'a>(pkg: &'a Package, CompileMode::Build => build, CompileMode::Check => &profiles.check, CompileMode::Doc { .. } => &profiles.doc, + CompileMode::Doctest => &profiles.doctest, }; match *filter { CompileFilter::Everything => { @@ -329,6 +331,15 @@ fn generate_targets<'a>(pkg: &'a Package, Ok(pkg.targets().iter().filter(|t| t.documented()) .map(|t| (t, profile)).collect()) } + CompileMode::Doctest => { + if let Some(t) = pkg.targets().iter().find(|t| t.is_lib()) { + if t.doctested() { + return Ok(vec![(t, profile)]) + } + } + + Ok(Vec::new()) + } } } CompileFilter::Only { lib, bins, examples, tests, benches } => { diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs index a5a16b4791d..f2f5ef39e37 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -224,7 +224,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { map.insert(crate_type.to_string(), Some((prefix.to_string(), suffix.to_string()))); } - + let cfg = if has_cfg { Some(try!(lines.map(Cfg::from_str).collect())) } else { @@ -554,7 +554,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { pub fn dep_targets(&self, unit: &Unit<'a>) -> CargoResult>> { if unit.profile.run_custom_build { return self.dep_run_custom_build(unit) - } else if unit.profile.doc { + } else if unit.profile.doc && !unit.profile.test { return self.doc_deps(unit); } @@ -626,7 +626,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { // the library of the same package. The call to `resolve.deps` above // didn't include `pkg` in the return values, so we need to special case // it here and see if we need to push `(pkg, pkg_lib_target)`. - if unit.target.is_lib() { + if unit.target.is_lib() && !unit.profile.doc { return Ok(ret) } ret.extend(self.maybe_lib(unit)); diff --git a/src/cargo/ops/cargo_rustc/job_queue.rs b/src/cargo/ops/cargo_rustc/job_queue.rs index f06a0bbebb6..0188330b994 100644 --- a/src/cargo/ops/cargo_rustc/job_queue.rs +++ b/src/cargo/ops/cargo_rustc/job_queue.rs @@ -292,8 +292,10 @@ impl<'a> JobQueue<'a> { // being a compiled package Dirty => { if key.profile.doc { - self.documented.insert(key.pkg); - config.shell().status("Documenting", key.pkg)?; + if !key.profile.test { + self.documented.insert(key.pkg); + config.shell().status("Documenting", key.pkg)?; + } } else { self.compiled.insert(key.pkg); config.shell().status("Compiling", key.pkg)?; diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 24c3de65697..0c9ea9decb2 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -13,6 +13,7 @@ use core::{Profile, Profiles, Workspace}; use core::shell::ColorConfig; use util::{self, CargoResult, ProcessBuilder, human, machine_message}; use util::{Config, internal, ChainError, profile, join_paths, short_hash}; +use util::Freshness; use self::job::{Job, Work}; use self::job_queue::JobQueue; @@ -183,6 +184,9 @@ fn compile<'a, 'cfg: 'a>(cx: &mut Context<'a, 'cfg>, let (dirty, fresh, freshness) = if unit.profile.run_custom_build { custom_build::prepare(cx, unit)? + } else if unit.profile.doc && unit.profile.test { + // we run these targets later, so this is just a noop for now + (Work::new(|_| Ok(())), Work::new(|_| Ok(())), Freshness::Fresh) } else { let (freshness, dirty, fresh) = fingerprint::prepare_target(cx, unit)?; let work = if unit.profile.doc { diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index b708d18cf9d..ecfcd0fcf83 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -1253,6 +1253,7 @@ fn build_profiles(profiles: &Option) -> Profiles { custom_build: Profile::default_custom_build(), check: merge(Profile::default_check(), profiles.and_then(|p| p.dev.as_ref())), + doctest: Profile::default_doctest(), }; // The test/bench targets cannot have panic=abort because they'll all get // compiled with --test which requires the unwind runtime currently diff --git a/tests/test.rs b/tests/test.rs index 1ed25b6f0b6..37fafddb64c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2143,6 +2143,7 @@ fn only_test_docs() { } /// ``` + /// foo::bar(); /// println!("ok"); /// ``` pub fn bar() { @@ -2524,3 +2525,35 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured ")); } + +#[test] +fn doctest_only_with_dev_dep() { + let p = project("workspace") + .file("Cargo.toml", r#" + [project] + name = "a" + version = "0.1.0" + + [dev-dependencies] + b = { path = "b" } + "#) + .file("src/lib.rs", r#" + /// ``` + /// extern crate b; + /// + /// b::b(); + /// ``` + pub fn a() {} + "#) + .file("b/Cargo.toml", r#" + [project] + name = "b" + version = "0.1.0" + "#) + .file("b/src/lib.rs", r#" + pub fn b() {} + "#); + + assert_that(p.cargo_process("test").arg("--doc").arg("-v"), + execs().with_status(0)); +}