diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 2354eadfe34..6c71cd9b94b 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -185,6 +185,7 @@ pub struct Target { kind: TargetKind, name: String, src_path: PathBuf, + required_features: Option>, tested: bool, benched: bool, doc: bool, @@ -305,6 +306,7 @@ impl Target { kind: TargetKind::Bin, name: String::new(), src_path: src_path, + required_features: None, doc: false, doctest: false, harness: true, @@ -326,10 +328,12 @@ impl Target { } } - pub fn bin_target(name: &str, src_path: PathBuf) -> Target { + pub fn bin_target(name: &str, src_path: PathBuf, + required_features: Option>) -> Target { Target { kind: TargetKind::Bin, name: name.to_string(), + required_features: required_features, doc: true, ..Target::with_path(src_path) } @@ -349,7 +353,8 @@ impl Target { pub fn example_target(name: &str, crate_targets: Vec, - src_path: PathBuf) -> Target { + src_path: PathBuf, + required_features: Option>) -> Target { let kind = if crate_targets.is_empty() { TargetKind::ExampleBin } else { @@ -359,24 +364,29 @@ impl Target { Target { kind: kind, name: name.to_string(), + required_features: required_features, benched: false, ..Target::with_path(src_path) } } - pub fn test_target(name: &str, src_path: PathBuf) -> Target { + pub fn test_target(name: &str, src_path: PathBuf, + required_features: Option>) -> Target { Target { kind: TargetKind::Test, name: name.to_string(), + required_features: required_features, benched: false, ..Target::with_path(src_path) } } - pub fn bench_target(name: &str, src_path: PathBuf) -> Target { + pub fn bench_target(name: &str, src_path: PathBuf, + required_features: Option>) -> Target { Target { kind: TargetKind::Bench, name: name.to_string(), + required_features: required_features, tested: false, ..Target::with_path(src_path) } @@ -385,6 +395,7 @@ impl Target { pub fn name(&self) -> &str { &self.name } pub fn crate_name(&self) -> String { self.name.replace("-", "_") } pub fn src_path(&self) -> &Path { &self.src_path } + pub fn required_features(&self) -> Option<&Vec> { self.required_features.as_ref() } pub fn kind(&self) -> &TargetKind { &self.kind } pub fn tested(&self) -> bool { self.tested } pub fn harness(&self) -> bool { self.harness } diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index ec13b2a2e8a..fdacc1e446e 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -22,7 +22,8 @@ //! previously compiled dependency //! -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::default::Default; use std::path::PathBuf; use std::sync::Arc; @@ -187,7 +188,8 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } } else { let root_package = ws.current()?; - generate_targets(root_package, profiles, mode, filter, release)?; + let all_features = resolve_with_overrides.features(root_package.package_id()); + generate_targets(root_package, profiles, mode, filter, all_features, release)?; pkgids.push(root_package.package_id()); }; @@ -204,8 +206,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags") } (Some(args), _) => { + let all_features = resolve_with_overrides.features(to_builds[0].package_id()); let targets = generate_targets(to_builds[0], profiles, - mode, filter, release)?; + mode, filter, all_features, release)?; if targets.len() == 1 { let (target, profile) = targets[0]; let mut profile = profile.clone(); @@ -218,8 +221,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } } (None, Some(args)) => { + let all_features = resolve_with_overrides.features(to_builds[0].package_id()); let targets = generate_targets(to_builds[0], profiles, - mode, filter, release)?; + mode, filter, all_features, release)?; if targets.len() == 1 { let (target, profile) = targets[0]; let mut profile = profile.clone(); @@ -233,8 +237,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } (None, None) => { for &to_build in to_builds.iter() { + let all_features = resolve_with_overrides.features(to_build.package_id()); let targets = generate_targets(to_build, profiles, mode, - filter, release)?; + filter, all_features, release)?; package_targets.push((to_build, targets)); } } @@ -313,6 +318,7 @@ fn generate_targets<'a>(pkg: &'a Package, profiles: &'a Profiles, mode: CompileMode, filter: &CompileFilter, + features: Option<&HashSet>, release: bool) -> CargoResult> { let build = if release {&profiles.release} else {&profiles.dev}; @@ -325,13 +331,13 @@ fn generate_targets<'a>(pkg: &'a Package, CompileMode::Doc { .. } => &profiles.doc, CompileMode::Doctest => &profiles.doctest, }; - match *filter { + let mut targets = match *filter { CompileFilter::Everything => { match mode { CompileMode::Bench => { - Ok(pkg.targets().iter().filter(|t| t.benched()).map(|t| { + pkg.targets().iter().filter(|t| t.benched()).map(|t| { (t, profile) - }).collect::>()) + }).collect::>() } CompileMode::Test => { let deps = if release { @@ -352,16 +358,16 @@ fn generate_targets<'a>(pkg: &'a Package, base.push((t, deps)); } } - Ok(base) + base } CompileMode::Build | CompileMode::Check => { - Ok(pkg.targets().iter().filter(|t| { + pkg.targets().iter().filter(|t| { t.is_bin() || t.is_lib() - }).map(|t| (t, profile)).collect()) + }).map(|t| (t, profile)).collect() } CompileMode::Doc { .. } => { - Ok(pkg.targets().iter().filter(|t| t.documented()) - .map(|t| (t, profile)).collect()) + 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()) { @@ -370,7 +376,7 @@ fn generate_targets<'a>(pkg: &'a Package, } } - Ok(Vec::new()) + Vec::new() } } } @@ -409,6 +415,7 @@ fn generate_targets<'a>(pkg: &'a Package, } }; debug!("found {} `{}`", desc, name); + targets.push((t, profile)); } Ok(()) @@ -418,9 +425,37 @@ fn generate_targets<'a>(pkg: &'a Package, find(tests, "test", Target::is_test, test)?; find(benches, "bench", Target::is_bench, &profiles.bench)?; } - Ok(targets) + targets + } + }; + + //Collect the targets that are libraries or have all required features available. + let no_features = HashSet::new(); + let features = features.unwrap_or(&no_features); + let mut compatible_targets = Vec::with_capacity(targets.len()); + for (target, profile) in targets.drain(0..) { + if target.is_lib() || match target.required_features() { + Some(f) => f.iter().all(|f| features.contains(f)), + None => true, + } { + compatible_targets.push((target, profile)); + continue; + } + + if let CompileFilter::Only { .. } = *filter { + let required_features = target.required_features().unwrap(); + let quoted_required_features: Vec = required_features.iter() + .map(|s| format!("`{}`",s)) + .collect(); + bail!("target `{}` requires the features: {}\n\ + Consider enabling them by passing e.g. `--features=\"{}\"`", + target.name(), + quoted_required_features.join(", "), + required_features.join(" ")); } } + + Ok(compatible_targets) } /// Parse all config files to learn about build configuration. Currently diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index 9e1548de353..02daac2ab6d 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -129,6 +129,10 @@ pub fn install(root: Option<&str>, bail!("Binary `{:?}` name can't be serialized into string", name) } }).collect::>()?; + if binaries.is_empty() { + bail!("no binaries are available for install using the selected \ + features"); + } let metadata = metadata(config, &root)?; let mut list = read_crate_list(metadata.file())?; diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index d12c73c679e..c9ee9b2459d 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -932,6 +932,7 @@ struct TomlTarget { plugin: Option, proc_macro: Option, harness: Option, + required_features: Option>, } #[derive(RustcDecodable, Clone)] @@ -961,6 +962,7 @@ impl TomlTarget { plugin: None, proc_macro: None, harness: None, + required_features: None } } @@ -1125,7 +1127,8 @@ fn normalize(package_root: &Path, false => PathValue::Path(Path::new("src").join("main.rs")) } }); - let mut target = Target::bin_target(&bin.name(), package_root.join(path.to_path())); + let mut target = Target::bin_target(&bin.name(), package_root.join(path.to_path()), + bin.required_features.clone()); configure(bin, &mut target); dst.push(target); } @@ -1154,7 +1157,8 @@ fn normalize(package_root: &Path, let mut target = Target::example_target( &ex.name(), crate_types, - package_root.join(path.to_path()) + package_root.join(path.to_path()), + ex.required_features.clone() ); configure(ex, &mut target); dst.push(target); @@ -1169,7 +1173,8 @@ fn normalize(package_root: &Path, PathValue::Path(default(test)) }); - let mut target = Target::test_target(&test.name(), package_root.join(path.to_path())); + let mut target = Target::test_target(&test.name(), package_root.join(path.to_path()), + test.required_features.clone()); configure(test, &mut target); dst.push(target); } @@ -1183,7 +1188,8 @@ fn normalize(package_root: &Path, PathValue::Path(default(bench)) }); - let mut target = Target::bench_target(&bench.name(), package_root.join(path.to_path())); + let mut target = Target::bench_target(&bench.name(), package_root.join(path.to_path()), + bench.required_features.clone()); configure(bench, &mut target); dst.push(target); } diff --git a/src/doc/manifest.md b/src/doc/manifest.md index 8ee88efe2a3..d6e1aa12a5d 100644 --- a/src/doc/manifest.md +++ b/src/doc/manifest.md @@ -551,6 +551,24 @@ proc-macro = false harness = true ``` +## The `required-features` field (optional) + +The `required-features` field specifies which features must be selected for the +target to be built. This is only relevant for the `[[bin]]`, `[[bench]]`, +`[[test]]`, and `[[example]]` sections, it has no effect on `[lib]`. + +```toml +[features] +# ... +postgres = [] +sqlite = [] +tools = [] + +[[bin]] +# ... +required-features = ["postgres", "tools"] +``` + # Building dynamic or static libraries If your project produces a library, you can specify which kind of library to diff --git a/tests/required-features.rs b/tests/required-features.rs new file mode 100644 index 00000000000..2b18e03dd6e --- /dev/null +++ b/tests/required-features.rs @@ -0,0 +1,730 @@ +extern crate cargotest; +extern crate hamcrest; + +use cargotest::is_nightly; +use cargotest::install::{cargo_home, has_installed_exe}; +use cargotest::support::{project, execs}; +use hamcrest::{assert_that, existing_file, not}; + +#[test] +fn build_bin_default_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#) + .file("src/main.rs", "fn main() {}"); + + assert_that(p.cargo_process("build"), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + assert_that(p.cargo_process("build").arg("--no-default-features"), + execs().with_status(0)); + assert_that(&p.bin("foo"), not(existing_file())); + + assert_that(p.cargo_process("build").arg("--bin=foo"), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + assert_that(p.cargo_process("build").arg("--bin=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +")); + assert_that(&p.bin("foo"), not(existing_file())); +} + +#[test] +fn build_bin_arg_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#) + .file("src/main.rs", "fn main() {}"); + + assert_that(p.cargo_process("build").arg("--features").arg("a"), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); +} + +#[test] +fn build_bin_multiple_required_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bin]] + name = "foo_1" + path = "src/foo_1.rs" + required-features = ["b", "c"] + + [[bin]] + name = "foo_2" + path = "src/foo_2.rs" + required-features = ["a"] + "#) + .file("src/foo_1.rs", "fn main() {}") + .file("src/foo_2.rs", "fn main() {}"); + + assert_that(p.cargo_process("build"), + execs().with_status(0)); + + assert_that(&p.bin("foo_1"), not(existing_file())); + assert_that(&p.bin("foo_2"), existing_file()); + + assert_that(p.cargo_process("build").arg("--features").arg("c"), + execs().with_status(0)); + + assert_that(&p.bin("foo_1"), existing_file()); + assert_that(&p.bin("foo_2"), existing_file()); + + assert_that(p.cargo_process("build").arg("--no-default-features"), + execs().with_status(0)); + + assert_that(&p.bin("foo_1"), not(existing_file())); + assert_that(&p.bin("foo_2"), not(existing_file())); +} + +#[test] +fn build_example_default_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[example]] + name = "foo" + required-features = ["a"] + "#) + .file("examples/foo.rs", "fn main() {}"); + + assert_that(p.cargo_process("build").arg("--example=foo"), + execs().with_status(0)); + assert_that(&p.bin("examples/foo"), existing_file()); + + assert_that(p.cargo_process("build").arg("--example=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +")); + assert_that(&p.bin("examples/foo"), not(existing_file())); +} + +#[test] +fn build_example_arg_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[example]] + name = "foo" + required-features = ["a"] + "#) + .file("examples/foo.rs", "fn main() {}"); + + assert_that(p.cargo_process("build").arg("--example=foo").arg("--features").arg("a"), + execs().with_status(0)); + assert_that(&p.bin("examples/foo"), existing_file()); +} + +#[test] +fn build_example_multiple_required_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[example]] + name = "foo_1" + required-features = ["b", "c"] + + [[example]] + name = "foo_2" + required-features = ["a"] + "#) + .file("examples/foo_1.rs", "fn main() {}") + .file("examples/foo_2.rs", "fn main() {}"); + + assert_that(p.cargo_process("build").arg("--example=foo_1"), + execs().with_status(101).with_stderr("\ +error: target `foo_1` requires the features: `b`, `c` +Consider enabling them by passing e.g. `--features=\"b c\"` +")); + assert_that(p.cargo("build").arg("--example=foo_2"), + execs().with_status(0)); + + assert_that(&p.bin("examples/foo_1"), not(existing_file())); + assert_that(&p.bin("examples/foo_2"), existing_file()); + + assert_that(p.cargo_process("build").arg("--example=foo_1") + .arg("--features").arg("c"), + execs().with_status(0)); + assert_that(p.cargo("build").arg("--example=foo_2") + .arg("--features").arg("c"), + execs().with_status(0)); + + assert_that(&p.bin("examples/foo_1"), existing_file()); + assert_that(&p.bin("examples/foo_2"), existing_file()); + + assert_that(p.cargo_process("build").arg("--example=foo_1") + .arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo_1` requires the features: `b`, `c` +Consider enabling them by passing e.g. `--features=\"b c\"` +")); + assert_that(p.cargo("build").arg("--example=foo_2") + .arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo_2` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +")); + + assert_that(&p.bin("examples/foo_1"), not(existing_file())); + assert_that(&p.bin("examples/foo_2"), not(existing_file())); +} + +#[test] +fn test_default_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[test]] + name = "foo" + required-features = ["a"] + "#) + .file("tests/foo.rs", "#[test]\nfn test() {}"); + + assert_that(p.cargo_process("test"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + assert_that(p.cargo_process("test").arg("--no-default-features"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")) + .with_stdout("")); + + assert_that(p.cargo_process("test").arg("--test=foo"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + assert_that(p.cargo_process("test").arg("--test=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +")); +} + +#[test] +fn test_arg_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[test]] + name = "foo" + required-features = ["a"] + "#) + .file("tests/foo.rs", "#[test]\nfn test() {}"); + + assert_that(p.cargo_process("test").arg("--features").arg("a"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); +} + +#[test] +fn test_multiple_required_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[test]] + name = "foo_1" + required-features = ["b", "c"] + + [[test]] + name = "foo_2" + required-features = ["a"] + "#) + .file("tests/foo_1.rs", "#[test]\nfn test() {}") + .file("tests/foo_2.rs", "#[test]\nfn test() {}"); + + assert_that(p.cargo_process("test"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo_2-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + assert_that(p.cargo_process("test").arg("--features").arg("c"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo_1-[..][EXE] +[RUNNING] target[/]debug[/]deps[/]foo_2-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + + +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + assert_that(p.cargo_process("test").arg("--no-default-features"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")) + .with_stdout("")); +} + +#[test] +fn bench_default_features() { + if !is_nightly() { + return; + } + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bench]] + name = "foo" + required-features = ["a"] + "#) + .file("benches/foo.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#); + + assert_that(p.cargo_process("bench"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + + assert_that(p.cargo_process("bench").arg("--no-default-features"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] release [optimized] target(s) in [..]")) + .with_stdout("")); + + assert_that(p.cargo_process("bench").arg("--bench=foo"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + + assert_that(p.cargo_process("bench").arg("--bench=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +")); +} + +#[test] +fn bench_arg_features() { + if !is_nightly() { + return; + } + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bench]] + name = "foo" + required-features = ["a"] + "#) + .file("benches/foo.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#); + + assert_that(p.cargo_process("bench").arg("--features").arg("a"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); +} + +#[test] +fn bench_multiple_required_features() { + if !is_nightly() { + return; + } + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bench]] + name = "foo_1" + required-features = ["b", "c"] + + [[bench]] + name = "foo_2" + required-features = ["a"] + "#) + .file("benches/foo_1.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#) + .file("benches/foo_2.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#); + + assert_that(p.cargo_process("bench"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo_2-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + + assert_that(p.cargo_process("bench").arg("--features").arg("c"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo_1-[..][EXE] +[RUNNING] target[/]release[/]deps[/]foo_2-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + + +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + + assert_that(p.cargo_process("bench").arg("--no-default-features"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] release [optimized] target(s) in [..]")) + .with_stdout("")); +} + +#[test] +fn install_default_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + + [[example]] + name = "foo" + required-features = ["a"] + "#) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}"); + + assert_that(p.cargo_process("install"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); + + assert_that(p.cargo_process("install").arg("--no-default-features"), + execs().with_status(101).with_stderr(format!("\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[ERROR] no binaries are available for install using the selected features +"))); + assert_that(cargo_home(), not(has_installed_exe("foo"))); + + assert_that(p.cargo_process("install").arg("--bin=foo"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); + + assert_that(p.cargo_process("install").arg("--bin=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr(format!("\ +[INSTALLING] foo v0.0.1 ([..]) +[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ + `[..]target` + +Caused by: + target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +"))); + assert_that(cargo_home(), not(has_installed_exe("foo"))); + + assert_that(p.cargo_process("install").arg("--example=foo"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); + + assert_that(p.cargo_process("install").arg("--example=foo").arg("--no-default-features"), + execs().with_status(101).with_stderr(format!("\ +[INSTALLING] foo v0.0.1 ([..]) +[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ + `[..]target` + +Caused by: + target `foo` requires the features: `a` +Consider enabling them by passing e.g. `--features=\"a\"` +"))); + assert_that(cargo_home(), not(has_installed_exe("foo"))); +} + +#[test] +fn install_arg_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#) + .file("src/main.rs", "fn main() {}"); + + assert_that(p.cargo_process("install").arg("--features").arg("a"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); +} + +#[test] +fn install_multiple_required_features() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bin]] + name = "foo_1" + path = "src/foo_1.rs" + required-features = ["b", "c"] + + [[bin]] + name = "foo_2" + path = "src/foo_2.rs" + required-features = ["a"] + "#) + .file("src/foo_1.rs", "fn main() {}") + .file("src/foo_2.rs", "fn main() {}"); + + assert_that(p.cargo_process("install"), + execs().with_status(0)); + assert_that(cargo_home(), not(has_installed_exe("foo_1"))); + assert_that(cargo_home(), has_installed_exe("foo_2")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); + + assert_that(p.cargo_process("install").arg("--features").arg("c"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo_1")); + assert_that(cargo_home(), has_installed_exe("foo_2")); + assert_that(p.cargo_process("uninstall").arg("foo"), + execs().with_status(0)); + + assert_that(p.cargo_process("install").arg("--no-default-features"), + execs().with_status(101).with_stderr("\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[ERROR] no binaries are available for install using the selected features +")); + assert_that(cargo_home(), not(has_installed_exe("foo_1"))); + assert_that(cargo_home(), not(has_installed_exe("foo_2"))); +}