From bb058d9c59961ee663904f31625ba0a5020a8e1b Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Mon, 26 Sep 2016 14:13:49 -0700 Subject: [PATCH] Build transitive dev-dependencies when needed When running `cargo test -p foo` where `foo` is a crate in the current workspace, build and link `foo`'s dev-dependencies. Fixes #860. --- src/cargo/ops/cargo_compile.rs | 16 +++++--- src/cargo/ops/cargo_generate_lockfile.rs | 5 ++- src/cargo/ops/cargo_output_metadata.rs | 3 +- src/cargo/ops/resolve.rs | 10 +++-- tests/workspaces.rs | 48 ++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 25b96891787..512117ed8ba 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -28,7 +28,7 @@ use std::sync::Arc; use core::registry::PackageRegistry; use core::{Source, SourceId, PackageSet, Package, Target}; -use core::{Profile, TargetKind, Profiles, Workspace}; +use core::{Profile, TargetKind, Profiles, Workspace, PackageIdSpec}; use core::resolver::{Method, Resolve}; use ops::{self, BuildOutput, ExecEngine}; use sources::PathSource; @@ -97,7 +97,8 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>, source: Option>, features: Vec, all_features: bool, - no_default_features: bool) + no_default_features: bool, + spec: &'a [String]) -> CargoResult<(PackageSet<'a>, Resolve)> { let mut registry = try!(PackageRegistry::new(ws.config())); @@ -128,9 +129,13 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>, } }; + let specs = try!(spec.iter().map(|p| PackageIdSpec::parse(p)) + .collect::>>()); + let resolved_with_overrides = try!(ops::resolve_with_previous(&mut registry, ws, - method, Some(&resolve), None)); + method, Some(&resolve), None, + &specs)); let packages = ops::get_resolved_packages(&resolved_with_overrides, registry); @@ -164,9 +169,8 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, try!(generate_targets(root_package, profiles, mode, filter, release)); } - let (packages, resolve_with_overrides) = { - try!(resolve_dependencies(ws, source, features, all_features, no_default_features)) - }; + let (packages, resolve_with_overrides) = + try!(resolve_dependencies(ws, source, features, all_features, no_default_features, spec)); let mut pkgids = Vec::new(); if spec.len() > 0 { diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index 6a6d198b35b..25e2204ba9e 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -19,7 +19,7 @@ pub fn generate_lockfile(ws: &Workspace) -> CargoResult<()> { let mut registry = try!(PackageRegistry::new(ws.config())); let resolve = try!(ops::resolve_with_previous(&mut registry, ws, Method::Everything, - None, None)); + None, None, &[])); try!(ops::write_pkg_lockfile(ws, &resolve)); Ok(()) } @@ -78,7 +78,8 @@ pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions) ws, Method::Everything, Some(&previous_resolve), - Some(&to_avoid))); + Some(&to_avoid), + &[])); // Summarize what is changing for the user. let print_change = |status: &str, msg: String| { diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index 035c7350fe9..131b93825db 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -47,7 +47,8 @@ fn metadata_full(ws: &Workspace, None, opt.features.clone(), opt.all_features, - opt.no_default_features)); + opt.no_default_features, + &[])); let (packages, resolve) = deps; let packages = try!(packages.package_ids() diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index 7ab3d1b4c12..5c8b0d89a4a 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use core::{PackageId, SourceId, Workspace}; +use core::{PackageId, PackageIdSpec, SourceId, Workspace}; use core::registry::PackageRegistry; use core::resolver::{self, Resolve, Method}; use ops; @@ -16,7 +16,7 @@ pub fn resolve_ws(registry: &mut PackageRegistry, ws: &Workspace) let prev = try!(ops::load_pkg_lockfile(ws)); let resolve = try!(resolve_with_previous(registry, ws, Method::Everything, - prev.as_ref(), None)); + prev.as_ref(), None, &[])); // Avoid writing a lockfile if we are `cargo install`ing a non local package. if ws.current_opt().map(|pkg| pkg.package_id().source_id().is_path()).unwrap_or(true) { @@ -38,7 +38,8 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry, ws: &Workspace, method: Method, previous: Option<&'a Resolve>, - to_avoid: Option<&HashSet<&'a PackageId>>) + to_avoid: Option<&HashSet<&'a PackageId>>, + specs: &[PackageIdSpec]) -> CargoResult { // Here we place an artificial limitation that all non-registry sources // cannot be locked at more than one revision. This means that if a git @@ -67,7 +68,8 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry, if let Method::Required { .. } = method { assert!(previous.is_some()); if let Some(current) = ws.current_opt() { - if member.package_id() != current.package_id() { + if member.package_id() != current.package_id() && + !specs.iter().any(|spec| spec.matches(member.package_id())) { continue; } } diff --git a/tests/workspaces.rs b/tests/workspaces.rs index 08be87bf4cf..42ac608da46 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -962,3 +962,51 @@ fn you_cannot_generate_lockfile_for_empty_workspaces() { error: you can't generate a lockfile for an empty workspace. ")); } + +#[test] +fn workspace_with_transitive_dev_deps() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.0" + authors = ["mbrubeck@example.com"] + + [dependencies.bar] + path = "bar" + + [workspace] + "#) + .file("src/main.rs", r#"fn main() {}"#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.5.0" + authors = ["mbrubeck@example.com"] + + [dev-dependencies.baz] + path = "../baz" + "#) + .file("bar/src/lib.rs", r#" + pub fn init() {} + + #[cfg(test)] + + #[test] + fn test() { + extern crate baz; + baz::do_stuff(); + } + "#) + .file("baz/Cargo.toml", r#" + [project] + name = "baz" + version = "0.5.0" + authors = ["mbrubeck@example.com"] + "#) + .file("baz/src/lib.rs", r#"pub fn do_stuff() {}"#); + p.build(); + + assert_that(p.cargo("test").args(&["-p", "bar"]), + execs().with_status(0)); +}