Skip to content

Commit

Permalink
Auto merge of #9283 - jonhoo:z-allowed-features, r=ehuss
Browse files Browse the repository at this point in the history
Add -Zallow-features to match rustc's -Z

This PR implements the `-Zallow-features` permanently-unstable feature flag that explicitly enumerates which unstable features are allowed (assuming unstable features are permitted in the first place). This mirrors the `-Zallow-features` flag of `rustc` which serves the same purpose for `rustc` features:

https://github.com/rust-lang/rust/blob/5fe790e3c40710ecb95ddaadb98b59a3bb4f8326/compiler/rustc_session/src/options.rs#L856-L857

This flag makes it easier to beta-test unstable features "safely" by ensuring that only a single unstable feature is used if you only have control over build system, and not the source code that developers may end up using, as discussed in [this internals thread](https://internals.rust-lang.org/t/mechanism-for-beta-testing-unstable-features/14280).
  • Loading branch information
bors committed Apr 2, 2021
2 parents 3c44c3c + 8d79a75 commit 10ef854
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 33 deletions.
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
20 changes: 12 additions & 8 deletions src/cargo/core/compiler/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1327,14 +1327,18 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
// Include metadata since it is exposed as environment variables.
let m = unit.pkg.manifest().metadata();
let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository));
let config = if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map {
cx.bcx
.config
.doc_extern_map()
.map_or(0, |map| util::hash_u64(map))
} else {
0
};
let mut config = 0u64;
if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map {
config = config.wrapping_add(
cx.bcx
.config
.doc_extern_map()
.map_or(0, |map| util::hash_u64(map)),
);
}
if let Some(allow_features) = &cx.bcx.config.cli_unstable().allow_features {
config = config.wrapping_add(util::hash_u64(allow_features));
}
let compile_kind = unit.kind.fingerprint_hash();
Ok(Fingerprint {
rustc: util::hash_u64(&cx.bcx.rustc().verbose_version),
Expand Down
13 changes: 12 additions & 1 deletion src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use crate::core::{Feature, PackageId, Target};
use crate::util::errors::{CargoResult, CargoResultExt, VerboseError};
use crate::util::interning::InternedString;
use crate::util::machine_message::{self, Message};
use crate::util::{add_path_args, internal, profile};
use crate::util::{add_path_args, internal, iter_join_onto, profile};
use cargo_util::{paths, ProcessBuilder, ProcessError};

const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
Expand Down Expand Up @@ -615,6 +615,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
}

add_error_format_and_color(cx, &mut rustdoc, false);
add_allow_features(cx, &mut rustdoc);

if let Some(args) = cx.bcx.extra_args_for(unit) {
rustdoc.args(args);
Expand Down Expand Up @@ -697,6 +698,15 @@ fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuild
}
}

/// Forward -Zallow-features if it is set for cargo.
fn add_allow_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) {
if let Some(allow) = &cx.bcx.config.cli_unstable().allow_features {
let mut arg = String::from("-Zallow-features=");
let _ = iter_join_onto(&mut arg, allow, ",");
cmd.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 @@ -773,6 +783,7 @@ fn build_base_args(

add_path_args(bcx.ws, unit, cmd);
add_error_format_and_color(cx, cmd, cx.rmeta_required(unit));
add_allow_features(cx, cmd);

if !test {
for crate_type in crate_types.iter() {
Expand Down
86 changes: 63 additions & 23 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::BTreeSet;
use std::env;
use std::fmt;
use std::str::FromStr;
Expand All @@ -101,7 +102,7 @@ use cargo_util::ProcessBuilder;
use serde::{Deserialize, Serialize};

use crate::util::errors::CargoResult;
use crate::util::indented_lines;
use crate::util::{indented_lines, iter_join};
use crate::Config;

pub const SEE_CHANNELS: &str =
Expand Down Expand Up @@ -416,13 +417,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 @@ -470,7 +476,17 @@ impl Features {
SEE_CHANNELS,
see_docs()
),
Status::Unstable => {}
Status::Unstable => {
if let Some(allow) = &config.cli_unstable().allow_features {
if !allow.contains(feature_name) {
bail!(
"the feature `{}` is not in the list of allowed features: [{}]",
feature_name,
iter_join(allow, ", "),
);
}
}
}
Status::Removed => bail!(
"the cargo feature `{}` has been removed\n\
Remove the feature from Cargo.toml to remove this error.\n\
Expand Down Expand Up @@ -530,37 +546,42 @@ impl Features {
#[derive(Default, Debug, Deserialize)]
#[serde(default, rename_all = "kebab-case")]
pub struct CliUnstable {
// Permanently unstable features:
pub allow_features: Option<BTreeSet<String>>,
pub print_im_a_teapot: bool,
pub unstable_options: bool,
pub no_index_update: bool,
pub avoid_dev_deps: bool,
pub minimal_versions: bool,

// All other unstable features.
// Please keep this list lexiographically ordered.
pub advanced_env: bool,
pub config_include: bool,
pub dual_proc_macros: bool,
pub mtime_on_use: bool,
pub named_profiles: bool,
pub avoid_dev_deps: bool,
pub binary_dep_depinfo: bool,
#[serde(deserialize_with = "deserialize_build_std")]
pub build_std: Option<Vec<String>>,
pub build_std_features: Option<Vec<String>>,
pub timings: Option<Vec<String>>,
pub doctest_xcompile: bool,
pub config_include: bool,
pub configurable_env: bool,
pub credential_process: bool,
pub doctest_in_workspace: bool,
pub panic_abort_tests: bool,
pub jobserver_per_rustc: bool,
pub doctest_xcompile: bool,
pub dual_proc_macros: bool,
pub enable_future_incompat_feature: bool,
pub extra_link_arg: bool,
pub features: Option<Vec<String>>,
pub separate_nightlies: bool,
pub jobserver_per_rustc: bool,
pub minimal_versions: bool,
pub mtime_on_use: bool,
pub multitarget: bool,
pub named_profiles: bool,
pub namespaced_features: bool,
pub no_index_update: bool,
pub panic_abort_tests: bool,
pub patch_in_config: bool,
pub rustdoc_map: bool,
pub separate_nightlies: bool,
pub terminal_width: Option<Option<usize>>,
pub namespaced_features: bool,
pub timings: Option<Vec<String>>,
pub unstable_options: bool,
pub weak_dep_features: bool,
pub extra_link_arg: bool,
pub patch_in_config: bool,
pub credential_process: bool,
pub configurable_env: bool,
pub enable_future_incompat_feature: bool,
}

const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
Expand Down Expand Up @@ -627,6 +648,13 @@ impl CliUnstable {
);
}
let mut warnings = Vec::new();
// We read flags twice, first to get allowed-features (if specified),
// and then to read the remaining unstable flags.
for flag in flags {
if flag.starts_with("allow-features=") {
self.add(flag, &mut warnings)?;
}
}
for flag in flags {
self.add(flag, &mut warnings)?;
}
Expand Down Expand Up @@ -656,6 +684,7 @@ impl CliUnstable {
fn parse_features(value: Option<&str>) -> Vec<String> {
match value {
None => Vec::new(),
Some("") => Vec::new(),
Some(v) => v.split(',').map(|s| s.to_string()).collect(),
}
}
Expand Down Expand Up @@ -698,8 +727,19 @@ impl CliUnstable {
))
};

if let Some(allowed) = &self.allow_features {
if k != "allow-features" && !allowed.contains(k) {
bail!(
"the feature `{}` is not in the list of allowed features: [{}]",
k,
iter_join(allowed, ", ")
);
}
}

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
27 changes: 27 additions & 0 deletions src/cargo/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt;
use std::time::Duration;

pub use self::canonical_url::CanonicalUrl;
Expand Down Expand Up @@ -65,6 +66,32 @@ pub fn elapsed(duration: Duration) -> String {
}
}

pub fn iter_join_onto<W, I, T>(mut w: W, iter: I, delim: &str) -> fmt::Result
where
W: fmt::Write,
I: IntoIterator<Item = T>,
T: std::fmt::Display,
{
let mut it = iter.into_iter().peekable();
while let Some(n) = it.next() {
write!(w, "{}", n)?;
if it.peek().is_some() {
write!(w, "{}", delim)?;
}
}
Ok(())
}

pub fn iter_join<I, T>(iter: I, delim: &str) -> String
where
I: IntoIterator<Item = T>,
T: std::fmt::Display,
{
let mut s = String::new();
let _ = iter_join_onto(&mut s, iter, delim);
s
}

pub fn indented_lines(text: &str) -> String {
text.lines()
.map(|line| {
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
Loading

0 comments on commit 10ef854

Please sign in to comment.