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 -Zallow-features to match rustc's -Z #9283

Merged
merged 15 commits into from
Apr 2, 2021
1 change: 1 addition & 0 deletions src/bin/cargo/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub fn main(config: &mut Config) -> CliResult {
"
Available unstable (nightly-only) flags:

-Z allow-features -- Allow *only* the listed unstable features
-Z avoid-dev-deps -- Avoid installing dev-dependencies if possible
-Z extra-link-arg -- Allow `cargo:rustc-link-arg` in build scripts
-Z minimal-versions -- Install minimal dependency versions instead of maximum
Expand Down
13 changes: 13 additions & 0 deletions src/cargo/core/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,19 @@ impl<'cfg> Compilation<'cfg> {
.env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
.cwd(pkg.root());

if is_rustc_tool {
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
if let Some(allow) = &self.config.cli_unstable().allow_features {
let mut arg = String::from("-Zallow-features=");
for (i, a) in allow.iter().enumerate() {
arg.push_str(a);
if i != allow.len() - 1 {
arg.push(',');
}
}
cmd.arg(&arg);
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
}
}

if self.config.cli_unstable().configurable_env {
// Apply any environment variables from the config
for (key, value) in self.config.env_config()?.iter() {
Expand Down
59 changes: 56 additions & 3 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
//! of that page. Update the rest of the documentation to add the new
//! feature.

use std::collections::HashSet;
use std::env;
use std::fmt;
use std::str::FromStr;
Expand Down Expand Up @@ -415,13 +416,18 @@ impl Features {
let mut ret = Features::default();
ret.nightly_features_allowed = config.nightly_features_allowed;
for feature in features {
ret.add(feature, warnings)?;
ret.add(feature, config, warnings)?;
ret.activated.push(feature.to_string());
}
Ok(ret)
}

fn add(&mut self, feature_name: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
fn add(
&mut self,
feature_name: &str,
config: &Config,
warnings: &mut Vec<String>,
) -> CargoResult<()> {
let nightly_features_allowed = self.nightly_features_allowed;
let (slot, feature) = match self.status(feature_name) {
Some(p) => p,
Expand Down Expand Up @@ -469,6 +475,24 @@ impl Features {
SEE_CHANNELS,
see_docs()
),
Status::Unstable
if !config
.cli_unstable()
.allow_features
.as_ref()
.map(|f| f.contains(feature_name))
.unwrap_or(true) =>
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
{
bail!(
"the feature `{}` is not in the list of allowed features: {:?}",
feature_name,
config
.cli_unstable()
.allow_features
.as_ref()
.expect("!unwrap_or(true) == false")
)
}
Status::Unstable => {}
Status::Removed => bail!(
"the cargo feature `{}` has been removed\n\
Expand Down Expand Up @@ -530,6 +554,8 @@ impl Features {
#[serde(default, rename_all = "kebab-case")]
pub struct CliUnstable {
pub print_im_a_teapot: bool,
pub allow_features: Option<HashSet<String>>,

ehuss marked this conversation as resolved.
Show resolved Hide resolved
pub unstable_options: bool,
pub no_index_update: bool,
pub avoid_dev_deps: bool,
Expand Down Expand Up @@ -627,7 +653,32 @@ impl CliUnstable {
}
let mut warnings = Vec::new();
for flag in flags {
self.add(flag, &mut warnings)?;
if flag.starts_with("allow-features=") {
self.add(flag, &mut warnings)?;
}
}
if let Some(allowed) = self.allow_features.take() {
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
for flag in flags {
let k = flag
.splitn(2, '=')
.next()
.expect("split always yields >=1 item");
if k == "allow-features" {
} else if allowed.contains(k) {
self.add(flag, &mut warnings)?;
} else {
bail!(
"the feature `{}` is not in the list of allowed features: {:?}",
k,
allowed
);
}
}
self.allow_features = Some(allowed);
} else {
for flag in flags {
self.add(flag, &mut warnings)?;
}
}
Ok(warnings)
}
Expand Down Expand Up @@ -655,6 +706,7 @@ impl CliUnstable {
fn parse_features(value: Option<&str>) -> Vec<String> {
match value {
None => Vec::new(),
Some("") => Vec::new(),
ehuss marked this conversation as resolved.
Show resolved Hide resolved
Some(v) => v.split(',').map(|s| s.to_string()).collect(),
}
}
Expand Down Expand Up @@ -699,6 +751,7 @@ impl CliUnstable {

match k {
"print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
"allow-features" => self.allow_features = Some(parse_features(v).into_iter().collect()),
"unstable-options" => self.unstable_options = parse_empty(k, v)?,
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
"avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
Expand Down
26 changes: 26 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ Each new feature described below should explain how to use it.
[nightly channel]: ../../book/appendix-07-nightly-rust.html
[stabilized]: https://doc.crates.io/contrib/process/unstable.html#stabilization

### allow-features

This permanently-unstable flag makes it so that only a listed set of
unstable features can be used. Specifically, if you pass
`-Zallow-features=foo,bar`, you'll continue to be able to pass `-Zfoo`
and `-Zbar` to `cargo`, but you will be unable to pass `-Zbaz`. You can
pass an empty string (`-Zallow-features=`) to disallow all unstable
features.

`-Zallow-features` also restricts which unstable features can be passed
to the `cargo-features` entry in `Cargo.toml`. If, for example, you want
to allow

```toml
cargo-features = ["test-dummy-unstable"]
```

where `test-dummy-unstable` is unstable, that features would also be
disallowed by `-Zallow-features=`, and allowed with
`-Zallow-features=test-dummy-unstable`.

The list of features passed to cargo's `-Zallow-features` is also passed
to any Rust tools that cargo ends up calling (like `rustc` or
`rustdoc`). Thus, if you run `cargo -Zallow-features=`, no unstable
Cargo _or_ Rust features can be used.

### extra-link-arg
* Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811)

Expand Down
198 changes: 198 additions & 0 deletions tests/testsuite/cargo_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,204 @@ release and is no longer necessary to be listed in the manifest
.run();
}

#[cargo_test]
fn allow_features() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["test-dummy-unstable"]

[package]
name = "a"
version = "0.0.1"
authors = []
im-a-teapot = true
"#,
)
.file("src/lib.rs", "")
.build();

// NOTE: We need to use RUSTC_BOOTSTRAP here since we also need nightly rustc
jonhoo marked this conversation as resolved.
Show resolved Hide resolved

p.cargo("-Zallow-features=test-dummy-unstable build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_stderr(
"\
[COMPILING] a [..]
[FINISHED] [..]
",
)
.run();

p.cargo("-Zallow-features=test-dummy-unstable,print-im-a-teapot -Zprint-im-a-teapot build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_stdout("im-a-teapot = true")
.with_stderr("[FINISHED] [..]")
.run();

p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr(
"\
error: the feature `print-im-a-teapot` is not in the list of allowed features: {\"test-dummy-unstable\"}
",
)
.run();

p.cargo("-Zallow-features= build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr(
"\
error: failed to parse manifest at `[..]`

Caused by:
the feature `test-dummy-unstable` is not in the list of allowed features: {}
",
)
.run();
}

#[cargo_test]
fn allow_features_to_rustc() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "a"
version = "0.0.1"
authors = []
"#,
)
.file(
"src/lib.rs",
r#"
#![feature(test_2018_feature)]
"#,
)
.build();

// NOTE: We need to use RUSTC_BOOTSTRAP here since we also need nightly rustc

p.cargo("-Zallow-features= build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr_contains(
"\
[COMPILING] a [..]
error[E0725]: the feature `test_2018_feature` is not in the list of allowed features
",
)
.with_stderr_contains(
"\
error: could not compile `a`
",
)
jonhoo marked this conversation as resolved.
Show resolved Hide resolved
.run();

p.cargo("-Zallow-features=test_2018_feature build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_stderr(
"\
[COMPILING] a [..]
[FINISHED] [..]
",
)
.run();
}

#[cargo_test]
fn allow_features_in_cfg() {
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["test-dummy-unstable"]

[package]
name = "a"
version = "0.0.1"
authors = []
im-a-teapot = true
"#,
)
.file(
".cargo/config.toml",
r#"
[unstable]
allow-features = ["test-dummy-unstable", "print-im-a-teapot"]
"#,
)
.file("src/lib.rs", "")
.build();

// NOTE: We need to use RUSTC_BOOTSTRAP here since we also need nightly rustc

p.cargo("build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_stderr(
"\
[COMPILING] a [..]
[FINISHED] [..]
",
)
.run();

p.cargo("-Zprint-im-a-teapot build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_stdout("im-a-teapot = true")
.with_stderr("[FINISHED] [..]")
.run();

p.cargo("-Zunstable-options build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr(
"\
error: the feature `unstable-options` is not in the list of allowed features: {[..]}
",
)
.run();

// -Zallow-features overrides .cargo/config
p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr(
"\
error: the feature `print-im-a-teapot` is not in the list of allowed features: {\"test-dummy-unstable\"}
",
)
.run();

p.cargo("-Zallow-features= build")
.masquerade_as_nightly_cargo()
.env("RUSTC_BOOTSTRAP", "1")
.with_status(101)
.with_stderr(
"\
error: failed to parse manifest at `[..]`

Caused by:
the feature `test-dummy-unstable` is not in the list of allowed features: {}
",
)
.run();
}

#[cargo_test]
fn nightly_feature_requires_nightly() {
let p = project()
Expand Down