mirror of
https://github.com/rust-lang/cargo
synced 2024-10-31 07:46:57 +00:00
2166 lines
52 KiB
Rust
2166 lines
52 KiB
Rust
//! Tests for `[features]` table.
|
|
|
|
use cargo_test_support::paths::CargoPathExt;
|
|
use cargo_test_support::registry::{Dependency, Package};
|
|
use cargo_test_support::{basic_manifest, project};
|
|
|
|
#[cargo_test]
|
|
fn invalid1() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
bar = ["baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse manifest at `[..]`
|
|
|
|
Caused by:
|
|
feature `bar` includes `baz` which is neither a dependency nor another feature
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn empty_feature_name() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
"" = []
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] feature name cannot be empty
|
|
--> Cargo.toml:8:17
|
|
|
|
|
8 | \"\" = []
|
|
| ^^
|
|
|
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn same_name() {
|
|
// Feature with the same name as a dependency.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
bar = ["baz"]
|
|
baz = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0"))
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("tree -f")
|
|
.arg("{p} [{f}]")
|
|
.with_stderr("")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.0.1 ([..]) []
|
|
└── bar v1.0.0 ([..]) []
|
|
",
|
|
)
|
|
.run();
|
|
|
|
p.cargo("tree --features bar -f")
|
|
.arg("{p} [{f}]")
|
|
.with_stderr("")
|
|
.with_stdout(
|
|
"\
|
|
foo v0.0.1 ([..]) [bar,baz]
|
|
└── bar v1.0.0 ([..]) []
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid3() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
bar = ["baz"]
|
|
|
|
[dependencies.baz]
|
|
path = "foo"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse manifest at `[..]`
|
|
|
|
Caused by:
|
|
feature `bar` includes `baz`, but `baz` is not an optional dependency
|
|
A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition.
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid4() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
features = ["bar"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
error: failed to select a version for `bar`.
|
|
... required by package `foo v0.0.1 ([..])`
|
|
versions that meet the requirements `*` are: 0.0.1
|
|
|
|
the package `foo` depends on `bar`, with features: `bar` but `bar` does not have these features.
|
|
|
|
|
|
failed to select a version for `bar` which could resolve this conflict",
|
|
)
|
|
.run();
|
|
|
|
p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1"));
|
|
|
|
p.cargo("check --features test")
|
|
.with_status(101)
|
|
.with_stderr("error: Package `foo v0.0.1 ([..])` does not have the feature `test`")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid5() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dev-dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse manifest at `[..]`
|
|
|
|
Caused by:
|
|
dev-dependencies are not allowed to be optional: `bar`
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid6() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = ["bar/baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check --features foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse manifest at `[..]`
|
|
|
|
Caused by:
|
|
feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid7() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = ["bar/baz"]
|
|
bar = []
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check --features foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] failed to parse manifest at `[..]`
|
|
|
|
Caused by:
|
|
feature `foo` includes `bar/baz`, but `bar` is not a dependency
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid8() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
features = ["foo/bar"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check --features foo")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
error: failed to parse manifest at `[CWD]/Cargo.toml`
|
|
|
|
Caused by:
|
|
feature `foo/bar` in dependency `bar` is not allowed to contain slashes
|
|
If you want to enable features [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid9() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check --features bar")
|
|
.with_stderr(
|
|
"\
|
|
error: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with that name, but only optional dependencies can be used as features.
|
|
",
|
|
).with_status(101).run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid10() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
features = ["baz"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "")
|
|
.file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("bar/baz/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check").with_stderr("\
|
|
error: failed to select a version for `bar`.
|
|
... required by package `foo v0.0.1 ([..])`
|
|
versions that meet the requirements `*` are: 0.0.1
|
|
|
|
the package `foo` depends on `bar`, with features: `baz` but `bar` does not have these features.
|
|
It has a required dependency with that name, but only optional dependencies can be used as features.
|
|
|
|
|
|
failed to select a version for `bar` which could resolve this conflict
|
|
").with_status(101)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_transitive_dep_feature_requirement() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.derived]
|
|
path = "derived"
|
|
|
|
[features]
|
|
default = ["derived/bar/qux"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
extern crate derived;
|
|
fn main() { derived::test(); }
|
|
"#,
|
|
)
|
|
.file(
|
|
"derived/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "derived"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "../bar"
|
|
"#,
|
|
)
|
|
.file("derived/src/lib.rs", "extern crate bar; pub use bar::test;")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
qux = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "qux")]
|
|
pub fn test() { print!("test"); }
|
|
"#,
|
|
)
|
|
.build();
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
error: failed to parse manifest at `[CWD]/Cargo.toml`
|
|
|
|
Caused by:
|
|
multiple slashes in feature `derived/bar/qux` (included by feature `default`) are not allowed
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_feature_doesnt_build() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(feature = "bar")]
|
|
extern crate bar;
|
|
#[cfg(feature = "bar")]
|
|
fn main() { bar::bar(); println!("bar") }
|
|
#[cfg(not(feature = "bar"))]
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.build();
|
|
|
|
p.cargo("build")
|
|
.with_stderr(
|
|
"\
|
|
[COMPILING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.process(&p.bin("foo")).with_stdout("").run();
|
|
|
|
p.cargo("build --features bar -v")
|
|
.with_stderr(
|
|
"\
|
|
[COMPILING] bar v0.0.1 ([CWD]/bar)
|
|
[RUNNING] `rustc --crate-name bar [..]
|
|
[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed
|
|
[COMPILING] foo v0.0.1 ([CWD])
|
|
[RUNNING] `rustc --crate-name foo [..]
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.process(&p.bin("foo")).with_stdout("bar\n").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn default_feature_pulled_in() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["bar"]
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(feature = "bar")]
|
|
extern crate bar;
|
|
#[cfg(feature = "bar")]
|
|
fn main() { bar::bar(); println!("bar") }
|
|
#[cfg(not(feature = "bar"))]
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.build();
|
|
|
|
p.cargo("build")
|
|
.with_stderr(
|
|
"\
|
|
[COMPILING] bar v0.0.1 ([CWD]/bar)
|
|
[COMPILING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.process(&p.bin("foo")).with_stdout("bar\n").run();
|
|
|
|
p.cargo("build --no-default-features -v")
|
|
.with_stderr(
|
|
"\
|
|
[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed
|
|
[COMPILING] foo v0.0.1 ([CWD])
|
|
[RUNNING] `rustc --crate-name foo [..]
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.process(&p.bin("foo")).with_stdout("").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn cyclic_feature() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["default"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] cyclic feature dependency: feature `default` depends on itself")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn cyclic_feature2() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = ["bar"]
|
|
bar = ["foo"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] foo [..]
|
|
[FINISHED] [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn groups_on_groups_on_groups() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = ["f2", "bar"]
|
|
f2 = ["f3", "f4"]
|
|
f3 = ["f5", "f6", "baz"]
|
|
f4 = ["f5", "f7"]
|
|
f5 = ["f6"]
|
|
f6 = ["f7"]
|
|
f7 = ["bar"]
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bar;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate baz;
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("baz/src/lib.rs", "pub fn baz() {}")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn many_cli_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bar;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate baz;
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("baz/src/lib.rs", "pub fn baz() {}")
|
|
.build();
|
|
|
|
p.cargo("check --features")
|
|
.arg("bar baz")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn union_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.d1]
|
|
path = "d1"
|
|
features = ["f1"]
|
|
[dependencies.d2]
|
|
path = "d2"
|
|
features = ["f2"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[allow(unused_extern_crates)]
|
|
extern crate d1;
|
|
extern crate d2;
|
|
fn main() {
|
|
d2::f1();
|
|
d2::f2();
|
|
}
|
|
"#,
|
|
)
|
|
.file(
|
|
"d1/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "d1"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
f1 = ["d2"]
|
|
|
|
[dependencies.d2]
|
|
path = "../d2"
|
|
features = ["f1"]
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file("d1/src/lib.rs", "")
|
|
.file(
|
|
"d2/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "d2"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
f1 = []
|
|
f2 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"d2/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "f1")] pub fn f1() {}
|
|
#[cfg(feature = "f2")] pub fn f2() {}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] d2 v0.0.1 ([CWD]/d2)
|
|
[CHECKING] d1 v0.0.1 ([CWD]/d1)
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn many_features_no_rebuilds() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies.a]
|
|
path = "a"
|
|
features = ["fall"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[features]
|
|
ftest = []
|
|
ftest2 = []
|
|
fall = ["ftest", "ftest2"]
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] a v0.1.0 ([CWD]/a)
|
|
[CHECKING] b v0.1.0 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
p.root().move_into_the_past();
|
|
|
|
p.cargo("check -v")
|
|
.with_stderr(
|
|
"\
|
|
[FRESH] a v0.1.0 ([..]/a)
|
|
[FRESH] b v0.1.0 ([..])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
// Tests that all cmd lines work with `--features ""`
|
|
#[cargo_test]
|
|
fn empty_features() {
|
|
let p = project().file("src/main.rs", "fn main() {}").build();
|
|
|
|
p.cargo("check --features").arg("").run();
|
|
}
|
|
|
|
// Tests that all cmd lines work with `--features ""`
|
|
#[cargo_test]
|
|
fn transitive_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = ["bar/baz"]
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "extern crate bar; fn main() { bar::baz(); }")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
baz = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/src/lib.rs",
|
|
r#"#[cfg(feature = "baz")] pub fn baz() {}"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check --features foo").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn everything_in_the_lockfile() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
f1 = ["d1/f1"]
|
|
f2 = ["d2"]
|
|
|
|
[dependencies.d1]
|
|
path = "d1"
|
|
[dependencies.d2]
|
|
path = "d2"
|
|
optional = true
|
|
[dependencies.d3]
|
|
path = "d3"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"d1/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "d1"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file("d1/src/lib.rs", "")
|
|
.file("d2/Cargo.toml", &basic_manifest("d2", "0.0.2"))
|
|
.file("d2/src/lib.rs", "")
|
|
.file(
|
|
"d3/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "d3"
|
|
version = "0.0.3"
|
|
authors = []
|
|
|
|
[features]
|
|
f3 = []
|
|
"#,
|
|
)
|
|
.file("d3/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("fetch").run();
|
|
let lockfile = p.read_lockfile();
|
|
assert!(
|
|
lockfile.contains(r#"name = "d1""#),
|
|
"d1 not found\n{}",
|
|
lockfile
|
|
);
|
|
assert!(
|
|
lockfile.contains(r#"name = "d2""#),
|
|
"d2 not found\n{}",
|
|
lockfile
|
|
);
|
|
assert!(
|
|
lockfile.contains(r#"name = "d3""#),
|
|
"d3 not found\n{}",
|
|
lockfile
|
|
);
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_rebuild_when_frobbing_default_feature() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
a = { path = "a" }
|
|
b = { path = "b" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
a = { path = "../a", features = ["f1"], default-features = false }
|
|
"#,
|
|
)
|
|
.file("b/src/lib.rs", "")
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check").run();
|
|
p.cargo("check").with_stderr("[FINISHED] [..]").run();
|
|
p.cargo("check").with_stderr("[FINISHED] [..]").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn unions_work_with_no_default_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
a = { path = "a" }
|
|
b = { path = "b" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "extern crate a; pub fn foo() { a::a(); }")
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
a = { path = "../a", features = [], default-features = false }
|
|
"#,
|
|
)
|
|
.file("b/src/lib.rs", "")
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", r#"#[cfg(feature = "f1")] pub fn a() {}"#)
|
|
.build();
|
|
|
|
p.cargo("check").run();
|
|
p.cargo("check").with_stderr("[FINISHED] [..]").run();
|
|
p.cargo("check").with_stderr("[FINISHED] [..]").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn optional_and_dev_dep() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "test"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
foo = { path = "foo", optional = true }
|
|
[dev-dependencies]
|
|
foo = { path = "foo" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0"))
|
|
.file("foo/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] test v0.1.0 ([..])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn activating_feature_activates_dep() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "test"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
foo = { path = "foo", optional = true }
|
|
|
|
[features]
|
|
a = ["foo/a"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/lib.rs",
|
|
"extern crate foo; pub fn bar() { foo::bar(); }",
|
|
)
|
|
.file(
|
|
"foo/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[features]
|
|
a = []
|
|
"#,
|
|
)
|
|
.file("foo/src/lib.rs", r#"#[cfg(feature = "a")] pub fn bar() {}"#)
|
|
.build();
|
|
|
|
p.cargo("check --features a -v").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn activating_feature_does_not_activate_transitive_dev_dependency() {
|
|
let p = project()
|
|
.no_manifest()
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.0.0"
|
|
edition = "2021"
|
|
|
|
[features]
|
|
f = ["b/f"]
|
|
|
|
[dependencies]
|
|
b = { path = "../b" }
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.0.0"
|
|
edition = "2021"
|
|
|
|
[features]
|
|
f = ["c/f"]
|
|
|
|
[dev-dependencies]
|
|
c = { path = "../c" }
|
|
"#,
|
|
)
|
|
.file(
|
|
"c/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "c"
|
|
version = "0.0.0"
|
|
edition = "2021"
|
|
|
|
[features]
|
|
f = []
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", "")
|
|
.file("b/src/lib.rs", "")
|
|
.file("c/src/lib.rs", "compile_error!")
|
|
.build();
|
|
|
|
p.cargo("check --manifest-path a/Cargo.toml --features f")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn dep_feature_in_cmd_line() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.derived]
|
|
path = "derived"
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
extern crate derived;
|
|
fn main() { derived::test(); }
|
|
"#,
|
|
)
|
|
.file(
|
|
"derived/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "derived"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "../bar"
|
|
|
|
[features]
|
|
default = []
|
|
derived-feat = ["bar/some-feat"]
|
|
"#,
|
|
)
|
|
.file("derived/src/lib.rs", "extern crate bar; pub use bar::test;")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
some-feat = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/src/lib.rs",
|
|
r#"
|
|
#[cfg(feature = "some-feat")]
|
|
pub fn test() { print!("test"); }
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
// The foo project requires that feature "some-feat" in "bar" is enabled.
|
|
// Building without any features enabled should fail:
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]unresolved import `bar::test`")
|
|
.run();
|
|
|
|
// We should be able to enable the feature "derived-feat", which enables "some-feat",
|
|
// on the command line. The feature is enabled, thus building should be successful:
|
|
p.cargo("check --features derived/derived-feat").run();
|
|
|
|
// Trying to enable features of transitive dependencies is an error
|
|
p.cargo("check --features bar/some-feat")
|
|
.with_status(101)
|
|
.with_stderr("error: package `foo v0.0.1 ([..])` does not have a dependency named `bar`")
|
|
.run();
|
|
|
|
// Hierarchical feature specification should still be disallowed
|
|
p.cargo("check --features derived/bar/some-feat")
|
|
.with_status(101)
|
|
.with_stderr("[ERROR] multiple slashes in feature `derived/bar/some-feat` is not allowed")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn all_features_flag_enables_all_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = []
|
|
bar = []
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(feature = "foo")]
|
|
pub fn foo() {}
|
|
|
|
#[cfg(feature = "bar")]
|
|
pub fn bar() {
|
|
extern crate baz;
|
|
baz::baz();
|
|
}
|
|
|
|
fn main() {
|
|
foo();
|
|
bar();
|
|
}
|
|
"#,
|
|
)
|
|
.file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("baz/src/lib.rs", "pub fn baz() {}")
|
|
.build();
|
|
|
|
p.cargo("check --all-features").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn many_cli_features_comma_delimited() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bar;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate baz;
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("baz/src/lib.rs", "pub fn baz() {}")
|
|
.build();
|
|
|
|
p.cargo("check --features bar,baz")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn many_cli_features_comma_and_space_delimited() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
|
|
[dependencies.baz]
|
|
path = "baz"
|
|
optional = true
|
|
|
|
[dependencies.bam]
|
|
path = "bam"
|
|
optional = true
|
|
|
|
[dependencies.bap]
|
|
path = "bap"
|
|
optional = true
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bar;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate baz;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bam;
|
|
#[allow(unused_extern_crates)]
|
|
extern crate bap;
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
|
|
.file("baz/src/lib.rs", "pub fn baz() {}")
|
|
.file("bam/Cargo.toml", &basic_manifest("bam", "0.0.1"))
|
|
.file("bam/src/lib.rs", "pub fn bam() {}")
|
|
.file("bap/Cargo.toml", &basic_manifest("bap", "0.0.1"))
|
|
.file("bap/src/lib.rs", "pub fn bap() {}")
|
|
.build();
|
|
|
|
p.cargo("check --features")
|
|
.arg("bar,baz bam bap")
|
|
.with_stderr(
|
|
"\
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..])
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn only_dep_is_optional() {
|
|
Package::new("bar", "0.1.0").publish();
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = ['bar']
|
|
|
|
[dependencies]
|
|
bar = { version = "0.1", optional = true }
|
|
|
|
[dev-dependencies]
|
|
bar = "0.1"
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("check").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn all_features_all_crates() {
|
|
Package::new("bar", "0.1.0").publish();
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[workspace]
|
|
members = ['bar']
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
foo = []
|
|
"#,
|
|
)
|
|
.file("bar/src/main.rs", "#[cfg(feature = \"foo\")] fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("check --all-features --workspace").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn feature_off_dylib() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["bar"]
|
|
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
|
|
[lib]
|
|
crate-type = ["dylib"]
|
|
|
|
[features]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/lib.rs",
|
|
r#"
|
|
pub fn hello() -> &'static str {
|
|
if cfg!(feature = "f1") {
|
|
"f1"
|
|
} else {
|
|
"no f1"
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
|
|
[dependencies]
|
|
foo = { path = ".." }
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/src/main.rs",
|
|
r#"
|
|
extern crate foo;
|
|
|
|
fn main() {
|
|
assert_eq!(foo::hello(), "no f1");
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
// Build the dylib with `f1` feature.
|
|
p.cargo("check --features f1").run();
|
|
// Check that building without `f1` uses a dylib without `f1`.
|
|
p.cargo("run -p bar").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn warn_if_default_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies.bar]
|
|
path = "bar"
|
|
optional = true
|
|
|
|
[features]
|
|
default-features = ["bar"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr(
|
|
r#"
|
|
[WARNING] `default-features = [".."]` was found in [features]. Did you mean to use `default = [".."]`?
|
|
[CHECKING] foo v0.0.1 ([CWD])
|
|
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
|
|
"#.trim(),
|
|
).run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn no_feature_for_non_optional_dep() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
bar = { path = "bar" }
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(not(feature = "bar"))]
|
|
fn main() {
|
|
}
|
|
"#,
|
|
)
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
a = []
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "pub fn bar() {}")
|
|
.build();
|
|
|
|
p.cargo("check --features bar/a").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn features_option_given_twice() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
a = []
|
|
b = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(all(feature = "a", feature = "b"))]
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check --features a --features b").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn multi_multi_features() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
a = []
|
|
b = []
|
|
c = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(all(feature = "a", feature = "b", feature = "c"))]
|
|
fn main() {}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check --features a --features").arg("b c").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn cli_parse_ok() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.0.1"
|
|
authors = []
|
|
|
|
[features]
|
|
a = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
#[cfg(feature = "a")]
|
|
fn main() {
|
|
assert_eq!(std::env::args().nth(1).unwrap(), "b");
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("run --features a b").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn all_features_virtual_ws() {
|
|
// What happens with `--all-features` in the root of a virtual workspace.
|
|
// Some of this behavior is a little strange (member dependencies also
|
|
// have all features enabled, one might expect `f4` to be disabled).
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[workspace]
|
|
members = ["a", "b"]
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
edition = "2018"
|
|
|
|
[dependencies]
|
|
b = {path="../b", optional=true}
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
f2 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"a/src/main.rs",
|
|
r#"
|
|
fn main() {
|
|
if cfg!(feature="f1") {
|
|
println!("f1");
|
|
}
|
|
if cfg!(feature="f2") {
|
|
println!("f2");
|
|
}
|
|
#[cfg(feature="b")]
|
|
b::f();
|
|
}
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "b"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
default = ["f3"]
|
|
f3 = []
|
|
f4 = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"b/src/lib.rs",
|
|
r#"
|
|
pub fn f() {
|
|
if cfg!(feature="f3") {
|
|
println!("f3");
|
|
}
|
|
if cfg!(feature="f4") {
|
|
println!("f4");
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("run").with_stdout("f1\n").run();
|
|
p.cargo("run --all-features")
|
|
.with_stdout("f1\nf2\nf3\nf4\n")
|
|
.run();
|
|
// In `a`, it behaves differently. :(
|
|
p.cargo("run --all-features")
|
|
.cwd("a")
|
|
.with_stdout("f1\nf2\nf3\n")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn slash_optional_enables() {
|
|
// --features dep/feat will enable `dep` and set its feature.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
dep = {path="dep", optional=true}
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/lib.rs",
|
|
r#"
|
|
#[cfg(not(feature="dep"))]
|
|
compile_error!("dep not set");
|
|
"#,
|
|
)
|
|
.file(
|
|
"dep/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "dep"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
feat = []
|
|
"#,
|
|
)
|
|
.file(
|
|
"dep/src/lib.rs",
|
|
r#"
|
|
#[cfg(not(feature="feat"))]
|
|
compile_error!("feat not set");
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr_contains("[..]dep not set[..]")
|
|
.run();
|
|
|
|
p.cargo("check --features dep/feat").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn registry_summary_order_doesnt_matter() {
|
|
// Checks for an issue where the resolver depended on the order of entries
|
|
// in the registry summary. If there was a non-optional dev-dependency
|
|
// that appeared before an optional normal dependency, then the resolver
|
|
// would not activate the optional dependency with a pkg/featname feature
|
|
// syntax.
|
|
Package::new("dep", "0.1.0")
|
|
.feature("feat1", &[])
|
|
.file(
|
|
"src/lib.rs",
|
|
r#"
|
|
#[cfg(feature="feat1")]
|
|
pub fn work() {
|
|
println!("it works");
|
|
}
|
|
"#,
|
|
)
|
|
.publish();
|
|
Package::new("bar", "0.1.0")
|
|
.feature("bar_feat", &["dep/feat1"])
|
|
.add_dep(Dependency::new("dep", "0.1.0").dev())
|
|
.add_dep(Dependency::new("dep", "0.1.0").optional(true))
|
|
.file(
|
|
"src/lib.rs",
|
|
r#"
|
|
// This will fail to compile without `dep` optional dep activated.
|
|
extern crate dep;
|
|
|
|
pub fn doit() {
|
|
dep::work();
|
|
}
|
|
"#,
|
|
)
|
|
.publish();
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
edition = "2018"
|
|
|
|
[dependencies]
|
|
bar = { version="0.1", features = ["bar_feat"] }
|
|
"#,
|
|
)
|
|
.file(
|
|
"src/main.rs",
|
|
r#"
|
|
fn main() {
|
|
bar::doit();
|
|
}
|
|
"#,
|
|
)
|
|
.build();
|
|
|
|
p.cargo("run")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] [..]
|
|
[DOWNLOADING] crates ...
|
|
[DOWNLOADED] [..]
|
|
[DOWNLOADED] [..]
|
|
[COMPILING] dep v0.1.0
|
|
[COMPILING] bar v0.1.0
|
|
[COMPILING] foo v0.1.0 [..]
|
|
[FINISHED] [..]
|
|
[RUNNING] `target/debug/foo[EXE]`
|
|
",
|
|
)
|
|
.with_stdout("it works")
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn nonexistent_required_features() {
|
|
Package::new("required_dependency", "0.1.0")
|
|
.feature("simple", &[])
|
|
.publish();
|
|
Package::new("optional_dependency", "0.2.0")
|
|
.feature("optional", &[])
|
|
.publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
[features]
|
|
existing = []
|
|
fancy = ["optional_dependency"]
|
|
[dependencies]
|
|
required_dependency = { version = "0.1", optional = false}
|
|
optional_dependency = { version = "0.2", optional = true}
|
|
[[example]]
|
|
name = "ololo"
|
|
required-features = ["not_present",
|
|
"existing",
|
|
"fancy",
|
|
"required_dependency/not_existing",
|
|
"required_dependency/simple",
|
|
"optional_dependency/optional",
|
|
"not_specified_dependency/some_feature"]
|
|
"#,
|
|
)
|
|
.file("src/main.rs", "fn main() {}")
|
|
.file("examples/ololo.rs", "fn main() {}")
|
|
.build();
|
|
|
|
p.cargo("check --examples")
|
|
.with_stderr_contains(
|
|
"\
|
|
[WARNING] invalid feature `not_present` in required-features of target `ololo`: \
|
|
`not_present` is not present in [features] section
|
|
[WARNING] invalid feature `required_dependency/not_existing` in required-features \
|
|
of target `ololo`: feature `not_existing` does not exist in package \
|
|
`required_dependency v0.1.0`
|
|
[WARNING] invalid feature `not_specified_dependency/some_feature` in required-features \
|
|
of target `ololo`: dependency `not_specified_dependency` does not exist
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid_feature_names_error() {
|
|
// Errors for more restricted feature syntax.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
# Invalid start character.
|
|
"+foo" = []
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid character `+` in feature name: `+foo`, the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`)
|
|
--> Cargo.toml:8:17
|
|
|
|
|
8 | \"+foo\" = []
|
|
| ^^^^^^
|
|
|
|
|
",
|
|
)
|
|
.run();
|
|
|
|
p.change_file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
# Invalid continue character.
|
|
"a&b" = []
|
|
"#,
|
|
);
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid character `&` in feature name: `a&b`, characters must be Unicode XID characters, '-', `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters)
|
|
--> Cargo.toml:8:13
|
|
|
|
|
8 | \"a&b\" = []
|
|
| ^^^^^
|
|
|
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn invalid_feature_name_slash_error() {
|
|
// Errors for more restricted feature syntax.
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
"foo/bar" = []
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_status(101)
|
|
.with_stderr(
|
|
"\
|
|
[ERROR] invalid character `/` in feature name: `foo/bar`, feature name is not allowed to contain slashes
|
|
--> Cargo.toml:7:17
|
|
|
|
|
7 | \"foo/bar\" = []
|
|
| ^^^^^^^^^
|
|
|
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn default_features_conflicting_warning() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[dependencies]
|
|
a = { path = "a", features = ["f1"], default-features = false, default_features = false }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"a/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
authors = []
|
|
|
|
[features]
|
|
default = ["f1"]
|
|
f1 = []
|
|
"#,
|
|
)
|
|
.file("a/src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("check")
|
|
.with_stderr_contains(
|
|
"[WARNING] conflicting between `default-features` and `default_features` in the `a` dependency.\n
|
|
`default_features` is ignored and not recommended for use in the future"
|
|
)
|
|
.run();
|
|
}
|