mirror of
https://github.com/rust-lang/cargo
synced 2024-08-25 02:25:46 +00:00
709 lines
17 KiB
Rust
709 lines
17 KiB
Rust
//! Tests for feature selection on the command-line.
|
|
|
|
use super::features2::switch_to_resolver_2;
|
|
use cargo_test_support::registry::{Dependency, Package};
|
|
use cargo_test_support::{basic_manifest, project};
|
|
use std::fmt::Write;
|
|
|
|
#[cargo_test]
|
|
fn virtual_no_default_features() {
|
|
// --no-default-features in root of virtual workspace.
|
|
Package::new("dep1", "1.0.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["a", "b"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
dep1 = {version = "1.0", optional = true}
|
|
|
|
[features]
|
|
default = ["dep1"]
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", "")
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "f1")]
|
|
compile_error!{"expected f1 off"}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check --no-default-features")
|
|
.with_stderr_unordered(
|
|
"\
|
|
[UPDATING] [..]
|
|
[CHECKING] a v0.1.0 [..]
|
|
[CHECKING] b v0.1.0 [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
|
|
p.cargo("check --features foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"[ERROR] none of the selected packages contains these features: foo, did you mean: f1?",
|
|
)
|
|
.run();
|
|
|
|
p.cargo("check --features a/dep1,b/f1,b/f2,f2")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] none of the selected packages contains these features: b/f2, f2, did you mean: f1?")
|
|
.run();
|
|
|
|
p.cargo("check --features a/dep,b/f1,b/f2,f2")
|
|
.masquerade_as_nightly_cargo()
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] none of the selected packages contains these features: a/dep, b/f2, f2, did you mean: a/dep1, f1?")
|
|
.run();
|
|
|
|
p.cargo("check --features a/dep,a/dep1")
|
|
.masquerade_as_nightly_cargo()
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] none of the selected packages contains these features: a/dep, did you mean: b/f1?")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn virtual_typo_member_feature() {
|
|
project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
resolver = "2"
|
|
|
|
[features]
|
|
deny-warnings = []
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build()
|
|
.cargo("check --features a/deny-warning")
|
|
.masquerade_as_nightly_cargo()
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"[ERROR] none of the selected packages contains these features: a/deny-warning, did you mean: a/deny-warnings?",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn virtual_features() {
|
|
// --features in root of virtual workspace.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["a", "b"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/src/lib.rs",
|
|
r#"
|
|
#[cfg(not(feature = "f1"))]
|
|
compile_error!{"f1 is missing"}
|
|
"#,
|
|
)
|
|
.file("b/Cargo.toml", &basic_manifest("b", "0.1.0"))
|
|
.file("b/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check --features f1")
|
|
.with_stderr_unordered(
|
|
"\
|
|
[CHECKING] a [..]
|
|
[CHECKING] b [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn virtual_with_specific() {
|
|
// -p flags with --features in root of virtual.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["a", "b"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
f1 = []
|
|
f2 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/src/lib.rs",
|
|
r#"
|
|
#[cfg(not_feature = "f1")]
|
|
compile_error!{"f1 is missing"}
|
|
#[cfg(not_feature = "f2")]
|
|
compile_error!{"f2 is missing"}
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
f2 = []
|
|
f3 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/src/lib.rs",
|
|
r#"
|
|
#[cfg(not_feature = "f2")]
|
|
compile_error!{"f2 is missing"}
|
|
#[cfg(not_feature = "f3")]
|
|
compile_error!{"f3 is missing"}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check -p a -p b --features f1,f2,f3")
|
|
.with_stderr_unordered(
|
|
"\
|
|
[CHECKING] a [..]
|
|
[CHECKING] b [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn other_member_from_current() {
|
|
// -p for another member while in the current directory.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["bar"]
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bar = { path="bar", features=["f3"] }
|
|
|
|
[features]
|
|
f1 = ["bar/f4"]
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
f1 = []
|
|
f2 = []
|
|
f3 = []
|
|
f4 = []
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "")
|
|
.file(
|
|
"bar/src/main.rs",
|
|
r#"
|
|
fn main() {
|
|
if cfg!(feature = "f1") {
|
|
print!("f1");
|
|
}
|
|
if cfg!(feature = "f2") {
|
|
print!("f2");
|
|
}
|
|
if cfg!(feature = "f3") {
|
|
print!("f3");
|
|
}
|
|
if cfg!(feature = "f4") {
|
|
print!("f4");
|
|
}
|
|
println!();
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
// Old behavior.
|
|
p.cargo("run -p bar --features f1")
|
|
.with_stdout("f3f4")
|
|
.run();
|
|
|
|
p.cargo("run -p bar --features f1,f2")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] Package `foo[..]` does not have the feature `f2`")
|
|
.run();
|
|
|
|
p.cargo("run -p bar --features bar/f1")
|
|
.with_stdout("f1f3")
|
|
.run();
|
|
|
|
// New behavior.
|
|
switch_to_resolver_2(&p);
|
|
p.cargo("run -p bar --features f1").with_stdout("f1").run();
|
|
|
|
p.cargo("run -p bar --features f1,f2")
|
|
.with_stdout("f1f2")
|
|
.run();
|
|
|
|
p.cargo("run -p bar --features bar/f1")
|
|
.with_stdout("f1")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn feature_default_resolver() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
test = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
fn main() {
|
|
if cfg!(feature = "test") {
|
|
println!("feature set");
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check --features testt")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] Package `a[..]` does not have the feature `testt`")
|
|
.run();
|
|
|
|
p.cargo("run --features test")
|
|
.with_status(0)
|
|
.with_stdout("feature set")
|
|
.run();
|
|
|
|
p.cargo("run --features a/test")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] package `a[..]` does not have a dependency named `a`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn virtual_member_slash() {
|
|
// member slash feature syntax
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["a"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
b = {path="../b", optional=true}
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
f2 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "f1")]
|
|
compile_error!{"f1 is set"}
|
|
|
|
#[cfg(feature = "f2")]
|
|
compile_error!{"f2 is set"}
|
|
|
|
#[cfg(feature = "b")]
|
|
compile_error!{"b is set"}
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
bfeat = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "bfeat")]
|
|
compile_error!{"bfeat is set"}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check -p a")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]f1 is set[..]")
|
|
.with_stderr_does_not_contain("[..]f2 is set[..]")
|
|
.with_stderr_does_not_contain("[..]b is set[..]")
|
|
.run();
|
|
|
|
p.cargo("check -p a --features a/f1")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]f1 is set[..]")
|
|
.with_stderr_does_not_contain("[..]f2 is set[..]")
|
|
.with_stderr_does_not_contain("[..]b is set[..]")
|
|
.run();
|
|
|
|
p.cargo("check -p a --features a/f2")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]f1 is set[..]")
|
|
.with_stderr_contains("[..]f2 is set[..]")
|
|
.with_stderr_does_not_contain("[..]b is set[..]")
|
|
.run();
|
|
|
|
p.cargo("check -p a --features b/bfeat")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]bfeat is set[..]")
|
|
.run();
|
|
|
|
p.cargo("check -p a --no-default-features").run();
|
|
|
|
p.cargo("check -p a --no-default-features --features b")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]b is set[..]")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn non_member() {
|
|
// -p for a non-member
|
|
Package::new("dep", "1.0.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
resolver = "2"
|
|
|
|
[dependencies]
|
|
dep = "1.0"
|
|
|
|
[features]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("build -p dep --features f1")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"[UPDATING][..]\n[ERROR] cannot specify features for packages outside of workspace",
|
|
)
|
|
.run();
|
|
|
|
p.cargo("build -p dep --all-features")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] cannot specify features for packages outside of workspace")
|
|
.run();
|
|
|
|
p.cargo("build -p dep --no-default-features")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] cannot specify features for packages outside of workspace")
|
|
.run();
|
|
|
|
p.cargo("build -p dep")
|
|
.with_stderr(
|
|
"\
|
|
[DOWNLOADING] [..]
|
|
[DOWNLOADED] [..]
|
|
[COMPILING] dep [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn resolver1_member_features() {
|
|
// --features member-name/feature-name with resolver="1"
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["member1", "member2"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"member1/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "member1"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
m1-feature = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"member1/src/main.rs",
|
|
r#"
|
|
fn main() {
|
|
if cfg!(feature = "m1-feature") {
|
|
println!("m1-feature set");
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
.file("member2/Cargo.toml", &basic_manifest("member2", "0.1.0"))
|
|
.file("member2/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("run -p member1 --features member1/m1-feature")
|
|
.cwd("member2")
|
|
.with_stdout("m1-feature set")
|
|
.run();
|
|
|
|
p.cargo("check -p member1 --features member1/m2-feature")
|
|
.cwd("member2")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] Package `member1[..]` does not have the feature `m2-feature`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn non_member_feature() {
|
|
// --features for a non-member
|
|
Package::new("jazz", "1.0.0").publish();
|
|
Package::new("bar", "1.0.0")
|
|
.add_dep(Dependency::new("jazz", "1.0").optional(true))
|
|
.publish();
|
|
let make_toml = |resolver, optional| {
|
|
let mut s = String::new();
|
|
write!(
|
|
s,
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
resolver = "{}"
|
|
|
|
[dependencies]
|
|
"#,
|
|
resolver
|
|
)
|
|
.unwrap();
|
|
if optional {
|
|
s.push_str(r#"bar = { version = "1.0", optional = true } "#);
|
|
} else {
|
|
s.push_str(r#"bar = "1.0""#)
|
|
}
|
|
s.push('\n');
|
|
s
|
|
};
|
|
let p = project()
|
|
.file("Cargo.toml", &make_toml("1", false))
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
p.cargo("fetch").run();
|
|
///////////////////////// V1 non-optional
|
|
eprintln!("V1 non-optional");
|
|
p.cargo("check -p bar")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] bar v1.0.0
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
// TODO: This should not be allowed (future warning?)
|
|
p.cargo("check --features bar/jazz")
|
|
.with_stderr(
|
|
"\
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] jazz v1.0.0 [..]
|
|
[CHECKING] jazz v1.0.0
|
|
[CHECKING] bar v1.0.0
|
|
[CHECKING] foo v0.1.0 [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
// TODO: This should not be allowed (future warning?)
|
|
p.cargo("check -p bar --features bar/jazz -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] jazz v1.0.0
|
|
[FRESH] bar v1.0.0
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
|
|
///////////////////////// V1 optional
|
|
eprintln!("V1 optional");
|
|
p.change_file("Cargo.toml", &make_toml("1", true));
|
|
|
|
// This error isn't great, but is probably unlikely to be common in
|
|
// practice, so I'm not going to put much effort into improving it.
|
|
p.cargo("check -p bar")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
error: package ID specification `bar` did not match any packages
|
|
|
|
<tab>Did you mean `foo`?
|
|
",
|
|
)
|
|
.run();
|
|
|
|
p.cargo("check -p bar --features bar -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] bar v1.0.0
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
|
|
// TODO: This should not be allowed (future warning?)
|
|
p.cargo("check -p bar --features bar/jazz -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] jazz v1.0.0
|
|
[FRESH] bar v1.0.0
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
|
|
///////////////////////// V2 non-optional
|
|
eprintln!("V2 non-optional");
|
|
p.change_file("Cargo.toml", &make_toml("2", false));
|
|
// TODO: This should not be allowed (future warning?)
|
|
p.cargo("check --features bar/jazz -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] jazz v1.0.0
|
|
[FRESH] bar v1.0.0
|
|
[FRESH] foo v0.1.0 [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.cargo("check -p bar -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] bar v1.0.0
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.cargo("check -p bar --features bar/jazz")
|
|
.with_status(101)
|
|
.with_stderr("error: cannot specify features for packages outside of workspace")
|
|
.run();
|
|
|
|
///////////////////////// V2 optional
|
|
eprintln!("V2 optional");
|
|
p.change_file("Cargo.toml", &make_toml("2", true));
|
|
p.cargo("check -p bar")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
error: package ID specification `bar` did not match any packages
|
|
|
|
<tab>Did you mean `foo`?
|
|
",
|
|
)
|
|
.run();
|
|
// New --features behavior does not look at cwd.
|
|
p.cargo("check -p bar --features bar")
|
|
.with_status(101)
|
|
.with_stderr("error: cannot specify features for packages outside of workspace")
|
|
.run();
|
|
p.cargo("check -p bar --features bar/jazz")
|
|
.with_status(101)
|
|
.with_stderr("error: cannot specify features for packages outside of workspace")
|
|
.run();
|
|
p.cargo("check -p bar --features foo/bar")
|
|
.with_status(101)
|
|
.with_stderr("error: cannot specify features for packages outside of workspace")
|
|
.run();
|
|
}
|