| //! 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#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| bar = ["baz"] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .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 invalid2() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| bar = ["baz"] |
| |
| [dependencies.bar] |
| path = "foo" |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Features and dependencies cannot have the same name: `bar` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid3() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| bar = ["baz"] |
| |
| [dependencies.baz] |
| path = "foo" |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `bar` depends on `baz` which is not an optional dependency. |
| Consider adding `optional = true` to the dependency |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid4() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build") |
| .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("build --features test") |
| .with_status(101) |
| .with_stderr("error: Package `foo v0.0.1 ([..])` does not have these features: `test`") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid5() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [dev-dependencies.bar] |
| path = "bar" |
| optional = true |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .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#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| foo = ["bar/baz"] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build --features foo") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `foo` requires a feature of `bar` which is not a dependency |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid7() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| foo = ["bar/baz"] |
| bar = [] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build --features foo") |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `foo` requires a feature of `bar` which is not a dependency |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid8() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --features foo") |
| .with_status(101) |
| .with_stderr("[ERROR] feature names may not contain slashes: `foo/bar`") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn invalid9() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --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#" |
| [project] |
| 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("build").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#" |
| [project] |
| 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("build") |
| .with_status(101) |
| .with_stderr("[ERROR] feature names may not contain slashes: `bar/qux`") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn no_feature_doesnt_build() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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") |
| .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(); |
| } |
| |
| #[cargo_test] |
| fn default_feature_pulled_in() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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") |
| .with_stderr( |
| "\ |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [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#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| default = ["default"] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .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#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| foo = ["bar"] |
| bar = ["foo"] |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").with_stdout("").run(); |
| } |
| |
| #[cargo_test] |
| fn groups_on_groups_on_groups() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build") |
| .with_stderr( |
| "\ |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] 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#" |
| [project] |
| 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("build --features") |
| .arg("bar baz") |
| .with_stderr( |
| "\ |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] 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#" |
| [project] |
| 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("build") |
| .with_stderr( |
| "\ |
| [COMPILING] d2 v0.0.1 ([CWD]/d2) |
| [COMPILING] d1 v0.0.1 ([CWD]/d1) |
| [COMPILING] 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("build") |
| .with_stderr( |
| "\ |
| [COMPILING] a v0.1.0 ([CWD]/a) |
| [COMPILING] b v0.1.0 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| p.root().move_into_the_past(); |
| |
| p.cargo("build -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("build --features").arg("").run(); |
| } |
| |
| // Tests that all cmd lines work with `--features ""` |
| #[cargo_test] |
| fn transitive_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --features foo").run(); |
| } |
| |
| #[cargo_test] |
| fn everything_in_the_lockfile() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build").run(); |
| p.cargo("build").with_stdout("").run(); |
| p.cargo("build").with_stdout("").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("build").run(); |
| p.cargo("build").with_stdout("").run(); |
| p.cargo("build").with_stdout("").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("build") |
| .with_stderr( |
| "\ |
| [COMPILING] 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("build --features a -v").run(); |
| } |
| |
| #[cargo_test] |
| fn dep_feature_in_cmd_line() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build") |
| .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("build --features derived/derived-feat").run(); |
| |
| // Trying to enable features of transitive dependencies is an error |
| p.cargo("build --features bar/some-feat") |
| .with_status(101) |
| .with_stderr("error: Package `foo v0.0.1 ([..])` does not have these features: `bar`") |
| .run(); |
| |
| // Hierarchical feature specification should still be disallowed |
| p.cargo("build --features derived/bar/some-feat") |
| .with_status(101) |
| .with_stderr("[ERROR] feature names may not contain slashes: `bar/some-feat`") |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn all_features_flag_enables_all_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --all-features").run(); |
| } |
| |
| #[cargo_test] |
| fn many_cli_features_comma_delimited() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --features bar,baz") |
| .with_stderr( |
| "\ |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] 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#" |
| [project] |
| 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("build --features") |
| .arg("bar,baz bam bap") |
| .with_stderr( |
| "\ |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] ba[..] v0.0.1 ([CWD]/ba[..]) |
| [COMPILING] foo v0.0.1 ([CWD]) |
| [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_invalid_feature() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| bar = ["baz"] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .masquerade_as_nightly_cargo() |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `bar` includes `baz` which is not defined as a feature |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_invalid_dependency() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| bar = ["crate:baz"] |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .masquerade_as_nightly_cargo() |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `bar` includes `crate:baz` which is not a known dependency |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_non_optional_dependency() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| bar = ["crate:baz"] |
| |
| [dependencies] |
| baz = "0.1" |
| "#, |
| ) |
| .file("src/main.rs", "") |
| .build(); |
| |
| p.cargo("build") |
| .masquerade_as_nightly_cargo() |
| .with_status(101) |
| .with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `bar` includes `crate:baz` which is not an optional dependency. |
| Consider adding `optional = true` to the dependency |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_implicit_feature() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| bar = ["baz"] |
| |
| [dependencies] |
| baz = { version = "0.1", optional = true } |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").masquerade_as_nightly_cargo().run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_shadowed_dep() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| baz = [] |
| |
| [dependencies] |
| baz = { version = "0.1", optional = true } |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `baz` includes the optional dependency of the same name, but this is left implicit in the features included by this feature. |
| Consider adding `crate:baz` to this feature's requirements. |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_shadowed_non_optional() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| baz = [] |
| |
| [dependencies] |
| baz = "0.1" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `baz` includes the dependency of the same name, but this is left implicit in the features included by this feature. |
| Additionally, the dependency must be marked as optional to be included in the feature definition. |
| Consider adding `crate:baz` to this feature's requirements and marking the dependency as `optional = true` |
| ", |
| ) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn namespaced_implicit_non_optional() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| bar = ["baz"] |
| |
| [dependencies] |
| baz = "0.1" |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr( |
| "\ |
| [ERROR] failed to parse manifest at `[..]` |
| |
| Caused by: |
| Feature `bar` includes `baz` which is not defined as a feature. |
| A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition |
| ", |
| ).run( |
| ); |
| } |
| |
| #[cargo_test] |
| fn namespaced_same_name() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| cargo-features = ["namespaced-features"] |
| |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| namespaced-features = true |
| |
| [features] |
| baz = ["crate:baz"] |
| |
| [dependencies] |
| baz = { version = "0.1", optional = true } |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .build(); |
| |
| p.cargo("build").masquerade_as_nightly_cargo().run(); |
| } |
| |
| #[cargo_test] |
| fn only_dep_is_optional() { |
| Package::new("bar", "0.1.0").publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build").run(); |
| } |
| |
| #[cargo_test] |
| fn all_features_all_crates() { |
| Package::new("bar", "0.1.0").publish(); |
| |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| name = "foo" |
| version = "0.0.1" |
| authors = [] |
| |
| [workspace] |
| members = ['bar'] |
| "#, |
| ) |
| .file("src/main.rs", "fn main() {}") |
| .file( |
| "bar/Cargo.toml", |
| r#" |
| [project] |
| name = "bar" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| foo = [] |
| "#, |
| ) |
| .file("bar/src/main.rs", "#[cfg(feature = \"foo\")] fn main() {}") |
| .build(); |
| |
| p.cargo("build --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("build --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#" |
| [project] |
| 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("build") |
| .with_stderr( |
| r#" |
| [WARNING] `default-features = [".."]` was found in [features]. Did you mean to use `default = [".."]`? |
| [COMPILING] 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#" |
| [project] |
| 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#" |
| [project] |
| name = "bar" |
| version = "0.0.1" |
| authors = [] |
| |
| [features] |
| a = [] |
| "#, |
| ) |
| .file("bar/src/lib.rs", "pub fn bar() {}") |
| .build(); |
| |
| p.cargo("build --features bar/a").run(); |
| } |
| |
| #[cargo_test] |
| fn features_option_given_twice() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --features a --features b").run(); |
| } |
| |
| #[cargo_test] |
| fn multi_multi_features() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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("build --features a --features").arg("b c").run(); |
| } |
| |
| #[cargo_test] |
| fn cli_parse_ok() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [project] |
| 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 virtual_ws_flags() { |
| // Reject features flags in the root of a virtual workspace. |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [workspace] |
| members = ["a"] |
| "#, |
| ) |
| .file( |
| "a/Cargo.toml", |
| r#" |
| [package] |
| name = "a" |
| version = "0.1.0" |
| |
| [features] |
| f1 = [] |
| "#, |
| ) |
| .file("a/src/lib.rs", "") |
| .build(); |
| |
| p.cargo("build --features=f1") |
| .with_stderr( |
| "[ERROR] --features is not allowed in the root of a virtual workspace\n\ |
| note: while this was previously accepted, it didn't actually do anything", |
| ) |
| .with_status(101) |
| .run(); |
| |
| p.cargo("build --no-default-features") |
| .with_stderr( |
| "[ERROR] --no-default-features is not allowed in the root of a virtual workspace\n\ |
| note: while this was previously accepted, it didn't actually do anything", |
| ) |
| .with_status(101) |
| .run(); |
| |
| // It's OK if cwd is in a member. |
| p.cargo("check --features=f1 -v") |
| .cwd("a") |
| .with_stderr( |
| "\ |
| [CHECKING] a [..] |
| [RUNNING] `rustc --crate-name a a/src/lib.rs [..]--cfg [..]feature[..]f1[..] |
| [FINISHED] dev [..] |
| ", |
| ) |
| .run(); |
| |
| p.cargo("clean").run(); |
| |
| // And -Zpackage-features is OK because it is designed to support this. |
| p.cargo("check --features=f1 -p a -Z package-features -v") |
| .masquerade_as_nightly_cargo() |
| .with_stderr( |
| "\ |
| [CHECKING] a [..] |
| [RUNNING] `rustc --crate-name a a/src/lib.rs [..]--cfg [..]feature[..]f1[..] |
| [FINISHED] dev [..] |
| ", |
| ) |
| .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(); |
| } |