Skip to content

Commit

Permalink
Add -Z check-cfg-features to enable compile-time checking of features
Browse files Browse the repository at this point in the history
  • Loading branch information
Urgau committed Feb 22, 2022
1 parent 46c9b51 commit 03fbb2e
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 3 deletions.
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
119 changes: 119 additions & 0 deletions tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5948,3 +5948,122 @@ fn primary_package_env_var() {

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

#[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();
}

#[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();
}
#[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();
}
36 changes: 36 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,38 @@ fn rustc_workspace_wrapper_excludes_published_deps() {
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]")
.run();
}

#[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();
}
36 changes: 36 additions & 0 deletions tests/testsuite/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4499,3 +4499,39 @@ fn test_workspaces_cwd() {
.with_stdout_contains("test test_integration_deep_cwd ... ok")
.run();
}

#[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();
}

0 comments on commit 03fbb2e

Please sign in to comment.