Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -Z check-cfg-features to enable compile-time checking of features #10408

Merged
merged 3 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,28 @@ fn add_allow_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) {
}
}

/// Add all features as cfg
fn add_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit) {
for feat in &unit.features {
cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
}

if cx.bcx.config.cli_unstable().check_cfg_features {
// This generate something like this:
// - values(feature)
// - values(feature, "foo", "bar")
let mut arg = String::from("values(feature");
for (&feat, _) in unit.pkg.summary().features() {
arg.push_str(", \"");
arg.push_str(&feat);
arg.push_str("\"");
}
arg.push(')');

cmd.arg("-Zunstable-options").arg("--check-cfg").arg(&arg);
}
}

/// Add error-format flags to the command.
///
/// Cargo always uses JSON output. This has several benefits, such as being
Expand Down Expand Up @@ -978,9 +1000,7 @@ fn build_base_args(
cmd.arg("--cfg").arg("test");
}

for feat in &unit.features {
cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
}
add_features(cx, cmd, unit);

let meta = cx.files().metadata(unit);
cmd.arg("-C").arg(&format!("metadata={}", meta));
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ unstable_cli_options!(
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
config_include: bool = ("Enable the `include` key in config files"),
credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
check_cfg_features: bool = ("Enable compile-time checking of features in `cfg`"),
doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"),
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
Expand Down Expand Up @@ -834,6 +835,7 @@ impl CliUnstable {
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
"advanced-env" => self.advanced_env = parse_empty(k, v)?,
"config-include" => self.config_include = parse_empty(k, v)?,
"check-cfg-features" => self.check_cfg_features = parse_empty(k, v)?,
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
// can also be set in .cargo/config or with and ENV
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
Expand Down
14 changes: 14 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,20 @@ For instance:
cargo doc -Z unstable-options -Z rustdoc-scrape-examples=examples
```

### check-cfg-features

* RFC: [#3013](https://github.com/rust-lang/rfcs/pull/3013)

The `-Z check-cfg-features` argument tells Cargo to pass all possible features of a package to
`rustc` unstable `--check-cfg` command line as `--check-cfg=values(feature, ...)`. This enables
compile time checking of feature values in `#[cfg]`, `cfg!` and `#[cfg_attr]`. Note than this
command line options will probably become the default when stabilizing.
For instance:

```
cargo check -Z unstable-options -Z check-cfg-features
```

## Stabilized and removed features

### Compile progress
Expand Down
164 changes: 164 additions & 0 deletions tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5948,3 +5948,167 @@ fn primary_package_env_var() {

foo.cargo("test").run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[features]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.build();

p.cargo("build -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features_with_deps() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[dependencies]
bar = { path = "bar/" }

[features]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();

p.cargo("build -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] bar v0.1.0 [..]
[RUNNING] `rustc [..] --check-cfg 'values(feature)' [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features_with_opt_deps() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[dependencies]
bar = { path = "bar/", optional = true }

[features]
default = ["bar"]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();

p.cargo("build -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] bar v0.1.0 [..]
[RUNNING] `rustc [..] --check-cfg 'values(feature)' [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"bar\", \"default\", \"f_a\", \"f_b\")' [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features_with_namespaced_features() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[dependencies]
bar = { path = "bar/", optional = true }

[features]
f_a = ["dep:bar"]
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();

p.cargo("build -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
37 changes: 37 additions & 0 deletions tests/testsuite/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::fmt::{self, Write};

use cargo_test_support::install::exe;
use cargo_test_support::is_nightly;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::tools;
Expand Down Expand Up @@ -997,3 +998,39 @@ fn rustc_workspace_wrapper_excludes_published_deps() {
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[features]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.build();

p.cargo("check -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[CHECKING] foo v0.1.0 [..]
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
.run();
}
37 changes: 37 additions & 0 deletions tests/testsuite/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4499,3 +4499,40 @@ fn test_workspaces_cwd() {
.with_stdout_contains("test test_integration_deep_cwd ... ok")
.run();
}

#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
#[cargo_test]
fn check_cfg_features() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}

let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"

[features]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.build();

p.cargo("test -v -Z check-cfg-features")
.masquerade_as_nightly_cargo()
.with_stderr(
"\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
[RUNNING] [..]
",
)
.run();
}