Skip to content

Commit

Permalink
Fix passing --features when testing multiple packages
Browse files Browse the repository at this point in the history
The wrong method was being passed to resolution accidentally. Features specified
via `--features` and `--no-default-features` are spec'd as only applying to the
*current* package, not all packages.

Closes #3279
  • Loading branch information
alexcrichton committed Nov 11, 2016
1 parent be0873c commit d21cb9a
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 22 deletions.
19 changes: 12 additions & 7 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>,
features: &[String],
all_features: bool,
no_default_features: bool,
spec: &'a [String])
specs: &[PackageIdSpec])
-> CargoResult<(PackageSet<'a>, Resolve)> {
let features = features.iter().flat_map(|s| {
s.split_whitespace()
Expand Down Expand Up @@ -137,13 +137,10 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>,
}
};

let specs = try!(spec.iter().map(|p| PackageIdSpec::parse(p))
.collect::<CargoResult<Vec<_>>>());

let resolved_with_overrides =
try!(ops::resolve_with_previous(&mut registry, ws,
method, Some(&resolve), None,
&specs));
specs));

let packages = ops::get_resolved_packages(&resolved_with_overrides,
registry);
Expand Down Expand Up @@ -174,8 +171,16 @@ 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, spec));
let specs = try!(spec.iter().map(|p| PackageIdSpec::parse(p))
.collect::<CargoResult<Vec<_>>>());

let pair = try!(resolve_dependencies(ws,
source,
features,
all_features,
no_default_features,
&specs));
let (packages, resolve_with_overrides) = pair;

let mut pkgids = Vec::new();
if spec.len() > 0 {
Expand Down
7 changes: 5 additions & 2 deletions src/cargo/ops/cargo_output_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc_serialize::{Encodable, Encoder};

use core::resolver::Resolve;
use core::{Package, PackageId, Workspace};
use core::{Package, PackageId, PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

Expand Down Expand Up @@ -43,12 +43,15 @@ fn metadata_no_deps(ws: &Workspace,

fn metadata_full(ws: &Workspace,
opt: &OutputMetadataOptions) -> CargoResult<ExportInfo> {
let specs = ws.members().map(|pkg| {
PackageIdSpec::from_package_id(pkg.package_id())
}).collect::<Vec<_>>();
let deps = try!(ops::resolve_dependencies(ws,
None,
&opt.features,
opt.all_features,
opt.no_default_features,
&[]));
&specs));
let (packages, resolve) = deps;

let packages = try!(packages.package_ids()
Expand Down
52 changes: 39 additions & 13 deletions src/cargo/ops/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,50 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry,
for member in ws.members() {
try!(registry.add_sources(&[member.package_id().source_id()
.clone()]));
let method_to_resolve = match method {
// When everything for a workspace we want to be sure to resolve all
// members in the workspace, so propagate the `Method::Everything`.
Method::Everything => Method::Everything,

// If we're resolving everything then we include all members of the
// workspace. If we want a specific set of requirements and we're
// compiling only a single workspace crate then resolve only it. This
// case should only happen after we have a previous resolution, however,
// so assert that the previous exists.
if let Method::Required { .. } = method {
assert!(previous.is_some());
if let Some(current) = ws.current_opt() {
if member.package_id() != current.package_id() &&
!specs.iter().any(|spec| spec.matches(member.package_id())) {
continue;
// If we're not resolving everything though then the workspace is
// already resolved and now we're drilling down from that to the
// exact crate graph we're going to build. Here we don't necessarily
// want to keep around all workspace crates as they may not all be
// built/tested.
//
// Additionally, the `method` specified represents command line
// flags, which really only matters for the current package
// (determined by the cwd). If other packages are specified (via
// `-p`) then the command line flags like features don't apply to
// them.
//
// As a result, if this `member` is the current member of the
// workspace, then we use `method` specified. Otherwise we use a
// base method with no features specified but using default features
// for any other packages specified with `-p`.
Method::Required { dev_deps, .. } => {
assert!(previous.is_some());
let base = Method::Required {
dev_deps: dev_deps,
features: &[],
uses_default_features: true,
};
let member_id = member.package_id();
match ws.current_opt() {
Some(current) if member_id == current.package_id() => method,
_ => {
if specs.iter().any(|spec| spec.matches(member_id)) {
base
} else {
continue
}
}
}
}
}
};

let summary = registry.lock(member.summary().clone());
summaries.push((summary, method));
summaries.push((summary, method_to_resolve));
}

let root_replace = ws.root_replace();
Expand Down
34 changes: 34 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2364,3 +2364,37 @@ fn test_release_ignore_panic() {
println!("bench");
assert_that(p.cargo("bench").arg("-v"), execs().with_status(0));
}

#[test]
fn test_many_with_features() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
a = { path = "a" }
[features]
foo = []
[workspace]
"#)
.file("src/lib.rs", "")
.file("a/Cargo.toml", r#"
[package]
name = "a"
version = "0.0.1"
authors = []
"#)
.file("a/src/lib.rs", "");
p.build();

assert_that(p.cargo("test").arg("-v")
.arg("-p").arg("a")
.arg("-p").arg("foo")
.arg("--features").arg("foo"),
execs().with_status(0));
}

0 comments on commit d21cb9a

Please sign in to comment.