cargo/tests/testsuite/cfg.rs
2023-02-20 12:20:26 -06:00

515 lines
13 KiB
Rust

//! Tests for cfg() expressions.
use cargo_test_support::registry::Package;
use cargo_test_support::rustc_host;
use cargo_test_support::{basic_manifest, project};
#[cargo_test]
fn cfg_easy() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[target.'cfg(unix)'.dependencies]
b = { path = 'b' }
[target."cfg(windows)".dependencies]
b = { path = 'b' }
"#,
)
.file("src/lib.rs", "extern crate b;")
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "")
.build();
p.cargo("check -v").run();
}
#[cargo_test]
fn dont_include() {
let other_family = if cfg!(unix) { "windows" } else { "unix" };
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[target.'cfg({})'.dependencies]
b = {{ path = 'b' }}
"#,
other_family
),
)
.file("src/lib.rs", "")
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "")
.build();
p.cargo("check")
.with_stderr(
"\
[CHECKING] a v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[cargo_test]
fn works_through_the_registry() {
Package::new("baz", "0.1.0").publish();
Package::new("bar", "0.1.0")
.target_dep("baz", "0.1.0", "cfg(unix)")
.target_dep("baz", "0.1.0", "cfg(windows)")
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[dependencies]
bar = "0.1.0"
"#,
)
.file(
"src/lib.rs",
"#[allow(unused_extern_crates)] extern crate bar;",
)
.build();
p.cargo("check")
.with_stderr(
"\
[UPDATING] [..] index
[DOWNLOADING] crates ...
[DOWNLOADED] [..]
[DOWNLOADED] [..]
[CHECKING] baz v0.1.0
[CHECKING] bar v0.1.0
[CHECKING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[cargo_test]
fn ignore_version_from_other_platform() {
let this_family = if cfg!(unix) { "unix" } else { "windows" };
let other_family = if cfg!(unix) { "windows" } else { "unix" };
Package::new("bar", "0.1.0").publish();
Package::new("bar", "0.2.0").publish();
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[target.'cfg({})'.dependencies]
bar = "0.1.0"
[target.'cfg({})'.dependencies]
bar = "0.2.0"
"#,
this_family, other_family
),
)
.file(
"src/lib.rs",
"#[allow(unused_extern_crates)] extern crate bar;",
)
.build();
p.cargo("check")
.with_stderr(
"\
[UPDATING] [..] index
[DOWNLOADING] crates ...
[DOWNLOADED] [..]
[CHECKING] bar v0.1.0
[CHECKING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
#[cargo_test]
fn bad_target_spec() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[target.'cfg(4)'.dependencies]
bar = "0.1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("check")
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
failed to parse `4` as a cfg expression: unexpected character `4` in cfg, [..]
",
)
.run();
}
#[cargo_test]
fn bad_target_spec2() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[target.'cfg(bar =)'.dependencies]
baz = "0.1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("check")
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended
",
)
.run();
}
#[cargo_test]
fn multiple_match_ok() {
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[target.'cfg(unix)'.dependencies]
b = {{ path = 'b' }}
[target.'cfg(target_family = "unix")'.dependencies]
b = {{ path = 'b' }}
[target."cfg(windows)".dependencies]
b = {{ path = 'b' }}
[target.'cfg(target_family = "windows")'.dependencies]
b = {{ path = 'b' }}
[target."cfg(any(windows, unix))".dependencies]
b = {{ path = 'b' }}
[target.{}.dependencies]
b = {{ path = 'b' }}
"#,
rustc_host()
),
)
.file("src/lib.rs", "extern crate b;")
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "")
.build();
p.cargo("check -v").run();
}
#[cargo_test]
fn any_ok() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[target."cfg(any(windows, unix))".dependencies]
b = { path = 'b' }
"#,
)
.file("src/lib.rs", "extern crate b;")
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "")
.build();
p.cargo("check -v").run();
}
// https://github.com/rust-lang/cargo/issues/5313
#[cargo_test]
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
fn cfg_looks_at_rustflags_for_target() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
[target.'cfg(with_b)'.dependencies]
b = { path = 'b' }
"#,
)
.file(
"src/main.rs",
r#"
#[cfg(with_b)]
extern crate b;
fn main() { b::foo(); }
"#,
)
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "pub fn foo() {}")
.build();
p.cargo("check --target x86_64-unknown-linux-gnu")
.env("RUSTFLAGS", "--cfg with_b")
.run();
}
#[cargo_test]
fn bad_cfg_discovery() {
// Check error messages when `rustc -v` and `rustc --print=*` parsing fails.
//
// This is a `rustc` replacement which behaves differently based on an
// environment variable.
let p = project()
.at("compiler")
.file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
.file(
"src/main.rs",
r#"
fn run_rustc() -> String {
let mut cmd = std::process::Command::new("rustc");
for arg in std::env::args_os().skip(1) {
cmd.arg(arg);
}
String::from_utf8(cmd.output().unwrap().stdout).unwrap()
}
fn main() {
let mode = std::env::var("FUNKY_MODE").unwrap();
if mode == "bad-version" {
println!("foo");
return;
}
if std::env::args_os().any(|a| a == "-vV") {
print!("{}", run_rustc());
return;
}
if mode == "no-crate-types" {
return;
}
if mode == "bad-crate-type" {
println!("foo");
return;
}
let output = run_rustc();
let mut lines = output.lines();
let sysroot = loop {
let line = lines.next().unwrap();
if line.contains("___") {
println!("{}", line);
} else {
break line;
}
};
if mode == "no-sysroot" {
return;
}
println!("{}", sysroot);
if mode == "no-split-debuginfo" {
return;
}
loop {
let line = lines.next().unwrap();
if line == "___" {
println!("\n{line}");
break;
} else {
// As the number split-debuginfo options varies,
// concat them into one line.
print!("{line},");
}
};
if mode != "bad-cfg" {
panic!("unexpected");
}
println!("123");
}
"#,
)
.build();
p.cargo("build").run();
let funky_rustc = p.bin("compiler");
let p = project().file("src/lib.rs", "").build();
p.cargo("check")
.env("RUSTC", &funky_rustc)
.env("FUNKY_MODE", "bad-version")
.with_status(101)
.with_stderr(
"\
[ERROR] `rustc -vV` didn't have a line for `host:`, got:
foo
",
)
.run();
p.cargo("check")
.env("RUSTC", &funky_rustc)
.env("FUNKY_MODE", "no-crate-types")
.with_status(101)
.with_stderr(
"\
[ERROR] malformed output when learning about crate-type bin information
command was: `[..]compiler[..] --crate-name ___ [..]`
(no output received)
",
)
.run();
p.cargo("check")
.env("RUSTC", &funky_rustc)
.env("FUNKY_MODE", "no-sysroot")
.with_status(101)
.with_stderr(
"\
[ERROR] output of --print=sysroot missing when learning about target-specific information from rustc
command was: `[..]compiler[..]--crate-type [..]`
--- stdout
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
",
)
.run();
p.cargo("check")
.env("RUSTC", &funky_rustc)
.env("FUNKY_MODE", "no-split-debuginfo")
.with_status(101)
.with_stderr(
"\
[ERROR] output of --print=split-debuginfo missing when learning about target-specific information from rustc
command was: `[..]compiler[..]--crate-type [..]`
--- stdout
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]
",
)
.run();
p.cargo("check")
.env("RUSTC", &funky_rustc)
.env("FUNKY_MODE", "bad-cfg")
.with_status(101)
.with_stderr(
"\
[ERROR] failed to parse the cfg from `rustc --print=cfg`, got:
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]___[..]
[..]
[..],[..]
___
123
Caused by:
failed to parse `123` as a cfg expression: unexpected character `1` in cfg, \
expected parens, a comma, an identifier, or a string
",
)
.run();
}
#[cargo_test]
fn exclusive_dep_kinds() {
// Checks for a bug where the same package with different cfg expressions
// was not being filtered correctly.
Package::new("bar", "1.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[target.'cfg(abc)'.dependencies]
bar = "1.0"
[target.'cfg(not(abc))'.build-dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.file("build.rs", "extern crate bar; fn main() {}")
.build();
p.cargo("check").run();
p.change_file("src/lib.rs", "extern crate bar;");
p.cargo("check")
.with_status(101)
// can't find crate for `bar`
.with_stderr_contains("[..]E0463[..]")
.run();
}