diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index ec3ff0fbe96..ed826be5804 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -327,9 +327,12 @@ See 'cargo help ' for more information on a specific command.\n", .arg(opt("locked", "Require Cargo.lock is up to date").global(true)) .arg(opt("offline", "Run without accessing the network").global(true)) .arg( - multi_opt("config", "KEY=VALUE", "Override a configuration value") - .global(true) - .hidden(true), + multi_opt( + "config", + "KEY=VALUE", + "Override a configuration value (unstable)", + ) + .global(true), ) .arg( Arg::with_name("unstable-features") diff --git a/src/bin/cargo/commands/login.rs b/src/bin/cargo/commands/login.rs index 0f67e0c0800..db254948285 100644 --- a/src/bin/cargo/commands/login.rs +++ b/src/bin/cargo/commands/login.rs @@ -10,6 +10,7 @@ pub fn cli() -> App { ) .arg(opt("quiet", "No output printed to stdout").short("q")) .arg(Arg::with_name("token")) + // --host is deprecated (use --registry instead) .arg( opt("host", "Host to set the token for") .value_name("HOST") diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index 10e89cd5ebd..d14c6887478 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -19,7 +19,9 @@ pub fn cli() -> App { "Display the tree for all packages in the workspace", "Exclude specific workspace members", ) + // Deprecated, use --no-dedupe instead. .arg(Arg::with_name("all").long("all").short("a").hidden(true)) + // Deprecated, use --target=all instead. .arg( Arg::with_name("all-targets") .long("all-targets") @@ -30,6 +32,7 @@ pub fn cli() -> App { "Filter dependencies matching the given target-triple (default host platform). \ Pass `all` to include all targets.", ) + // Deprecated, use -e=no-dev instead. .arg( Arg::with_name("no-dev-dependencies") .long("no-dev-dependencies") @@ -52,7 +55,9 @@ pub fn cli() -> App { ) .short("i"), ) + // Deprecated, use --prefix=none instead. .arg(Arg::with_name("no-indent").long("no-indent").hidden(true)) + // Deprecated, use --prefix=depth instead. .arg( Arg::with_name("prefix-depth") .long("prefix-depth") diff --git a/src/bin/cargo/commands/vendor.rs b/src/bin/cargo/commands/vendor.rs index e0f5dc14ca6..b3705a52665 100644 --- a/src/bin/cargo/commands/vendor.rs +++ b/src/bin/cargo/commands/vendor.rs @@ -32,21 +32,25 @@ pub fn cli() -> App { .long("versioned-dirs") .help("Always include version in subdir name"), ) + // Not supported. .arg( Arg::with_name("no-merge-sources") .long("no-merge-sources") .hidden(true), ) + // Not supported. .arg( Arg::with_name("relative-path") .long("relative-path") .hidden(true), ) + // Not supported. .arg( Arg::with_name("only-git-deps") .long("only-git-deps") .hidden(true), ) + // Not supported. .arg( Arg::with_name("disallow-duplicates") .long("disallow-duplicates") diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 45df0d247fe..494e3399300 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -6,44 +6,91 @@ //! unstable features are tracked in this file. //! //! If you're reading this then you're likely interested in adding a feature to -//! Cargo, and the good news is that it shouldn't be too hard! To do this you'll -//! want to follow these steps: +//! Cargo, and the good news is that it shouldn't be too hard! First determine +//! how the feature should be gated: //! -//! 1. Add your feature. Do this by searching for "look here" in this file and -//! expanding the macro invocation that lists all features with your new -//! feature. +//! * New syntax in Cargo.toml should use `cargo-features`. +//! * New CLI options should use `-Z unstable-options`. +//! * New functionality that may not have an interface, or the interface has +//! not yet been designed, or for more complex features that affect multiple +//! parts of Cargo should use a new `-Z` flag. +//! +//! See below for more details. +//! +//! When adding new tests for your feature, usually the tests should go into a +//! new module of the testsuite. See +//! for more information on +//! writing tests. Particularly, check out the "Testing Nightly Features" +//! section for testing unstable features. +//! +//! After you have added your feature, be sure to update the unstable +//! documentation at `src/doc/src/reference/unstable.md` to include a short +//! description of how to use your new feature. +//! +//! And hopefully that's it! +//! +//! ## New Cargo.toml syntax +//! +//! The steps for adding new Cargo.toml syntax are: +//! +//! 1. Add the cargo-features unstable gate. Search below for "look here" to +//! find the `features!` macro and add your feature to the list. +//! +//! 2. Update the Cargo.toml parsing code to handle your new feature. +//! +//! 3. Wherever you added the new parsing code, call +//! `features.require(Feature::my_feature_name())?` if the new syntax is +//! used. This will return an error if the user hasn't listed the feature +//! in `cargo-features` or this is not the nightly channel. +//! +//! ## `-Z unstable-options` +//! +//! `-Z unstable-options` is intended to force the user to opt-in to new CLI +//! flags, options, and new subcommands. +//! +//! The steps to add a new command-line option are: //! -//! 2. Find the appropriate place to place the feature gate in Cargo itself. If -//! you're extending the manifest format you'll likely just want to modify -//! the `Manifest::feature_gate` function, but otherwise you may wish to -//! place the feature gate elsewhere in Cargo. +//! 1. Add the option to the CLI parsing code. In the help text, be sure to +//! include `(unstable)` to note that this is an unstable option. +//! 2. Where the CLI option is loaded, be sure to call +//! [`CliUnstable::fail_if_stable_opt`]. This will return an error if `-Z +//! unstable options` was not passed. //! -//! 3. To actually perform the feature gate, you'll want to have code that looks -//! like: +//! ## `-Z` options //! -//! ```rust,compile_fail -//! use core::{Feature, Features}; +//! The steps to add a new `-Z` option are: //! -//! let feature = Feature::launch_into_space(); -//! package.manifest().unstable_features().require(feature).chain_err(|| { -//! "launching Cargo into space right now is unstable and may result in \ -//! unintended damage to your codebase, use with caution" -//! })?; -//! ``` +//! 1. Add the option to the [`CliUnstable`] struct below. Flags can take an +//! optional value if you want. +//! 2. Update the [`CliUnstable::add`] function to parse the flag. +//! 3. Wherever the new functionality is implemented, call +//! [`Config::cli_unstable`][crate::util::config::Config::cli_unstable] to +//! get an instance of `CliUnstable` and check if the option has been +//! enabled on the `CliUnstable` instance. Nightly gating is already +//! handled, so no need to worry about that. +//! 4. Update the `-Z help` documentation in the `main` function. //! -//! Notably you'll notice the `require` function called with your `Feature`, and -//! then you use `chain_err` to tack on more context for why the feature was -//! required when the feature isn't activated. +//! ## Stabilization //! -//! 4. Update the unstable documentation at -//! `src/doc/src/reference/unstable.md` to include a short description of -//! how to use your new feature. When the feature is stabilized, be sure -//! that the Cargo Guide or Reference is updated to fully document the -//! feature and remove the entry from the Unstable section. +//! For the stabilization process, see +//! . //! -//! And hopefully that's it! Bear with us though that this is, at the time of -//! this writing, a very new feature in Cargo. If the process differs from this -//! we'll be sure to update this documentation! +//! The steps for stabilizing are roughly: +//! +//! 1. Update the feature to be stable, based on the kind of feature: +//! 1. `cargo-features`: Change the feature to `stable` in the `features!` +//! macro below. +//! 2. `-Z unstable-options`: Find the call to `fail_if_stable_opt` and +//! remove it. Be sure to update the man pages if necessary. +//! 3. `-Z` flag: Change the parsing code in [`CliUnstable::add`] to call +//! `stabilized_warn` or `stabilized_err`. Remove it from the `-Z help` +//! docs in the `main` function. Remove the `(unstable)` note in the +//! clap help text if necessary. +//! 2. Remove `masquerade_as_nightly_cargo` from any tests, and remove +//! `cargo-features` from `Cargo.toml` test files if any. +//! 3. Remove the docs from unstable.md and update the redirect at the bottom +//! of that page. Update the rest of the documentation to add the new +//! feature. use std::cell::Cell; use std::env; @@ -54,6 +101,7 @@ use anyhow::{bail, Error}; use serde::{Deserialize, Serialize}; use crate::util::errors::CargoResult; +use crate::util::indented_lines; pub const SEE_CHANNELS: &str = "See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information \ @@ -115,13 +163,12 @@ impl FromStr for Edition { enum Status { Stable, Unstable, + Removed, } macro_rules! features { ( - pub struct Features { - $([$stab:ident] $feature:ident: bool,)* - } + $(($stab:ident, $feature:ident, $version:expr, $docs:expr),)* ) => ( #[derive(Default, Clone, Debug)] pub struct Features { @@ -137,6 +184,9 @@ macro_rules! features { } static FEAT: Feature = Feature { name: stringify!($feature), + stability: stab!($stab), + version: $version, + docs: $docs, get, }; &FEAT @@ -149,14 +199,14 @@ macro_rules! features { } impl Features { - fn status(&mut self, feature: &str) -> Option<(&mut bool, Status)> { + fn status(&mut self, feature: &str) -> Option<(&mut bool, &'static Feature)> { if feature.contains("_") { return None } let feature = feature.replace("-", "_"); $( if feature == stringify!($feature) { - return Some((&mut self.$feature, stab!($stab))) + return Some((&mut self.$feature, Feature::$feature())) } )* None @@ -172,6 +222,9 @@ macro_rules! stab { (unstable) => { Status::Unstable }; + (removed) => { + Status::Removed + }; } // A listing of all features in Cargo. @@ -186,57 +239,64 @@ macro_rules! stab { // character is translated to `-` when specified in the `cargo-features` // manifest entry in `Cargo.toml`. features! { - pub struct Features { - - // A dummy feature that doesn't actually gate anything, but it's used in - // testing to ensure that we can enable stable features. - [stable] test_dummy_stable: bool, + // A dummy feature that doesn't actually gate anything, but it's used in + // testing to ensure that we can enable stable features. + (stable, test_dummy_stable, "1.0", ""), - // A dummy feature that gates the usage of the `im-a-teapot` manifest - // entry. This is basically just intended for tests. - [unstable] test_dummy_unstable: bool, + // A dummy feature that gates the usage of the `im-a-teapot` manifest + // entry. This is basically just intended for tests. + (unstable, test_dummy_unstable, "", "reference/unstable.html"), - // Downloading packages from alternative registry indexes. - [stable] alternative_registries: bool, + // Downloading packages from alternative registry indexes. + (stable, alternative_registries, "1.34", "reference/registries.html"), - // Using editions - [stable] edition: bool, + // Using editions + (stable, edition, "1.31", "reference/manifest.html#the-edition-field"), - // Renaming a package in the manifest via the `package` key - [stable] rename_dependency: bool, + // Renaming a package in the manifest via the `package` key + (stable, rename_dependency, "1.31", "reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml"), - // Whether a lock file is published with this crate - // This is deprecated, and will likely be removed in a future version. - [unstable] publish_lockfile: bool, + // Whether a lock file is published with this crate + (removed, publish_lockfile, "", PUBLISH_LOCKFILE_REMOVED), - // Overriding profiles for dependencies. - [stable] profile_overrides: bool, + // Overriding profiles for dependencies. + (stable, profile_overrides, "1.41", "reference/profiles.html#overrides"), - // "default-run" manifest option, - [stable] default_run: bool, + // "default-run" manifest option, + (stable, default_run, "1.37", "reference/manifest.html#the-default-run-field"), - // Declarative build scripts. - [unstable] metabuild: bool, + // Declarative build scripts. + (unstable, metabuild, "", "reference/unstable.html#metabuild"), - // Specifying the 'public' attribute on dependencies - [unstable] public_dependency: bool, + // Specifying the 'public' attribute on dependencies + (unstable, public_dependency, "", "reference/unstable.html#public-dependency"), - // Allow to specify profiles other than 'dev', 'release', 'test', etc. - [unstable] named_profiles: bool, + // Allow to specify profiles other than 'dev', 'release', 'test', etc. + (unstable, named_profiles, "", "reference/unstable.html#custom-named-profiles"), - // Opt-in new-resolver behavior. - [stable] resolver: bool, + // Opt-in new-resolver behavior. + (stable, resolver, "1.51", "reference/resolver.html#resolver-versions"), - // Allow to specify whether binaries should be stripped. - [unstable] strip: bool, + // Allow to specify whether binaries should be stripped. + (unstable, strip, "", "reference/unstable.html#profile-strip-option"), - // Specifying a minimal 'rust-version' attribute for crates - [unstable] rust_version: bool, - } + // Specifying a minimal 'rust-version' attribute for crates + (unstable, rust_version, "", "reference/unstable.html#rust-version"), } +const PUBLISH_LOCKFILE_REMOVED: &str = "The publish-lockfile key in Cargo.toml \ + has been removed. The Cargo.lock file is always included when a package is \ + published if the package contains a binary target. `cargo install` requires \ + the `--locked` flag to use the Cargo.lock file.\n\ + See https://doc.rust-lang.org/cargo/commands/cargo-package.html and \ + https://doc.rust-lang.org/cargo/commands/cargo-install.html for more \ + information."; + pub struct Feature { name: &'static str, + stability: Status, + version: &'static str, + docs: &'static str, get: fn(&Features) -> bool, } @@ -250,35 +310,61 @@ impl Features { Ok(ret) } - fn add(&mut self, feature: &str, warnings: &mut Vec) -> CargoResult<()> { - let (slot, status) = match self.status(feature) { + fn add(&mut self, feature_name: &str, warnings: &mut Vec) -> CargoResult<()> { + let (slot, feature) = match self.status(feature_name) { Some(p) => p, - None => bail!("unknown cargo feature `{}`", feature), + None => bail!("unknown cargo feature `{}`", feature_name), }; if *slot { - bail!("the cargo feature `{}` has already been activated", feature); + bail!( + "the cargo feature `{}` has already been activated", + feature_name + ); } - match status { + let see_docs = || { + let url_channel = match channel().as_str() { + "dev" | "nightly" => "nightly/", + "beta" => "beta/", + _ => "", + }; + format!( + "See https://doc.rust-lang.org/{}cargo/{} for more information \ + about using this feature.", + url_channel, feature.docs + ) + }; + + match feature.stability { Status::Stable => { let warning = format!( - "the cargo feature `{}` is now stable \ - and is no longer necessary to be listed \ - in the manifest", - feature + "the cargo feature `{}` has been stabilized in the {} \ + release and is no longer necessary to be listed in the \ + manifest\n {}", + feature_name, + feature.version, + see_docs() ); warnings.push(warning); } Status::Unstable if !nightly_features_allowed() => bail!( "the cargo feature `{}` requires a nightly version of \ Cargo, but this is the `{}` channel\n\ - {}", - feature, + {}\n{}", + feature_name, channel(), - SEE_CHANNELS + SEE_CHANNELS, + see_docs() ), Status::Unstable => {} + Status::Removed => bail!( + "the cargo feature `{}` has been removed\n\ + Remove the feature from Cargo.toml to remove this error.\n\ + {}", + feature_name, + feature.docs + ), } *slot = true; @@ -328,26 +414,6 @@ impl Features { /// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for /// gating unstable functionality to Cargo. These flags are only available on /// the nightly channel of Cargo. -/// -/// This struct doesn't have quite the same convenience macro that the features -/// have above, but the procedure should still be relatively stable for adding a -/// new unstable flag: -/// -/// 1. First, add a field to this `CliUnstable` structure. All flags are allowed -/// to have a value as the `-Z` flags are either of the form `-Z foo` or -/// `-Z foo=bar`, and it's up to you how to parse `bar`. -/// -/// 2. Add an arm to the match statement in `CliUnstable::add` below to match on -/// your new flag. The key (`k`) is what you're matching on and the value is -/// in `v`. -/// -/// 3. (optional) Add a new parsing function to parse your datatype. As of now -/// there's an example for `bool`, but more can be added! -/// -/// 4. In Cargo use `config.cli_unstable()` to get a reference to this structure -/// and then test for your flag or your value and act accordingly. -/// -/// If you have any trouble with this, please let us know! #[derive(Default, Debug, Deserialize)] #[serde(default, rename_all = "kebab-case")] pub struct CliUnstable { @@ -380,6 +446,40 @@ pub struct CliUnstable { pub credential_process: bool, } +const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \ + enabled when used on an interactive console.\n\ + See https://doc.rust-lang.org/cargo/reference/config.html#termprogresswhen \ + for information on controlling the progress bar."; + +const STABILIZED_OFFLINE: &str = "Offline mode is now available via the \ + --offline CLI option"; + +const STABILIZED_CACHE_MESSAGES: &str = "Message caching is now always enabled."; + +const STABILIZED_INSTALL_UPGRADE: &str = "Packages are now always upgraded if \ + they appear out of date.\n\ + See https://doc.rust-lang.org/cargo/commands/cargo-install.html for more \ + information on how upgrading works."; + +const STABILIZED_CONFIG_PROFILE: &str = "See \ + https://doc.rust-lang.org/cargo/reference/config.html#profile for more \ + information about specifying profiles in config."; + +const STABILIZED_CRATE_VERSIONS: &str = "The crate version is now \ + automatically added to the documentation."; + +const STABILIZED_PACKAGE_FEATURES: &str = "Enhanced feature flag behavior is now \ + available in virtual workspaces, and `member/feature-name` syntax is also \ + always available. Other extensions require setting `resolver = \"2\"` in \ + Cargo.toml.\n\ + See https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags \ + for more information."; + +const STABILIZED_FEATURES: &str = "The new feature resolver is now available \ + by specifying `resolver = \"2\"` in Cargo.toml.\n\ + See https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 \ + for more information."; + fn deserialize_build_std<'de, D>(deserializer: D) -> Result>, D::Error> where D: serde::Deserializer<'de>, @@ -395,7 +495,7 @@ where } impl CliUnstable { - pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> { + pub fn parse(&mut self, flags: &[String]) -> CargoResult> { if !flags.is_empty() && !nightly_features_allowed() { bail!( "the `-Z` flag is only accepted on the nightly channel of Cargo, \ @@ -405,13 +505,14 @@ impl CliUnstable { SEE_CHANNELS ); } + let mut warnings = Vec::new(); for flag in flags { - self.add(flag)?; + self.add(flag, &mut warnings)?; } - Ok(()) + Ok(warnings) } - fn add(&mut self, flag: &str) -> CargoResult<()> { + fn add(&mut self, flag: &str, warnings: &mut Vec) -> CargoResult<()> { let mut parts = flag.splitn(2, '='); let k = parts.next().unwrap(); let v = parts.next(); @@ -456,6 +557,26 @@ impl CliUnstable { }) } + let mut stabilized_warn = |key: &str, version: &str, message: &str| { + warnings.push(format!( + "flag `-Z {}` has been stabilized in the {} release, \ + and is no longer necessary\n{}", + key, + version, + indented_lines(message) + )); + }; + + // Use this if the behavior now requires another mechanism to enable. + let stabilized_err = |key: &str, version: &str, message: &str| { + Err(anyhow::format_err!( + "flag `-Z {}` has been stabilized in the {} release\n{}", + key, + version, + indented_lines(message) + )) + }; + match k { "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?, "unstable-options" => self.unstable_options = parse_empty(k, v)?, @@ -477,7 +598,27 @@ impl CliUnstable { "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?, "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?, "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?, - "features" => self.features = Some(parse_features(v)), + "features" => { + // For now this is still allowed (there are still some + // unstable options like "compare"). This should be removed at + // some point, and migrate to a new -Z flag for any future + // things. + let feats = parse_features(v); + let stab: Vec<_> = feats + .iter() + .filter(|feat| { + matches!( + feat.as_str(), + "build_dep" | "host_dep" | "dev_dep" | "itarget" | "all" + ) + }) + .collect(); + if !stab.is_empty() || feats.is_empty() { + // Make this stabilized_err once -Zfeature support is removed. + stabilized_warn(k, "1.51", STABILIZED_FEATURES); + } + self.features = Some(feats); + } "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, "multitarget" => self.multitarget = parse_empty(k, v)?, "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, @@ -486,6 +627,13 @@ impl CliUnstable { "weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?, "extra-link-arg" => self.extra_link_arg = parse_empty(k, v)?, "credential-process" => self.credential_process = parse_empty(k, v)?, + "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS), + "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?, + "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES), + "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE), + "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE), + "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS), + "package-features" => stabilized_warn(k, "1.51", STABILIZED_PACKAGE_FEATURES), _ => bail!("unknown `-Z` flag specified: {}", k), } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 4608adcbe3f..330e07f0627 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -39,7 +39,6 @@ pub struct Manifest { custom_metadata: Option, profiles: Option, publish: Option>, - publish_lockfile: bool, replace: Vec<(PackageIdSpec, Dependency)>, patch: HashMap>, workspace: WorkspaceConfig, @@ -374,7 +373,6 @@ impl Manifest { custom_metadata: Option, profiles: Option, publish: Option>, - publish_lockfile: bool, replace: Vec<(PackageIdSpec, Dependency)>, patch: HashMap>, workspace: WorkspaceConfig, @@ -407,7 +405,6 @@ impl Manifest { original, im_a_teapot, default_run, - publish_lockfile, metabuild, resolve_behavior, } diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index bccb41121eb..1f8ceb691c0 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -37,7 +37,7 @@ use log::debug; use std::fmt; pub use crate::util::errors::{InternalError, VerboseError}; -pub use crate::util::{CargoResult, CliError, CliResult, Config}; +pub use crate::util::{indented_lines, CargoResult, CliError, CliResult, Config}; pub const CARGO_ENV: &str = "CARGO"; @@ -163,13 +163,11 @@ fn _display_error(err: &Error, shell: &mut Shell, as_err: bool) -> bool { return true; } drop(writeln!(shell.err(), "\nCaused by:")); - for line in cause.to_string().lines() { - if line.is_empty() { - drop(writeln!(shell.err())); - } else { - drop(writeln!(shell.err(), " {}", line)); - } - } + drop(write!( + shell.err(), + "{}", + indented_lines(&cause.to_string()) + )); } false } diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 27c6e54e74f..4f56ab242f4 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -169,7 +169,7 @@ pub trait AppExt: Sized { } fn arg_unit_graph(self) -> Self { - self._arg(opt("unit-graph", "Output build graph in JSON (unstable)").hidden(true)) + self._arg(opt("unit-graph", "Output build graph in JSON (unstable)")) } fn arg_new_opts(self) -> Self { @@ -214,13 +214,10 @@ pub trait AppExt: Sized { } fn arg_ignore_rust_version(self) -> Self { - self._arg( - opt( - "ignore-rust-version", - "Ignore `rust-version` specification in packages", - ) - .hidden(true), // nightly only (`rust-version` feature) - ) + self._arg(opt( + "ignore-rust-version", + "Ignore `rust-version` specification in packages (unstable)", + )) } } diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 5cd6bd85232..b492e94ed81 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -704,7 +704,9 @@ impl Config { unstable_flags: &[String], cli_config: &[String], ) -> CargoResult<()> { - self.unstable_flags.parse(unstable_flags)?; + for warning in self.unstable_flags.parse(unstable_flags)? { + self.shell().warn(warning)?; + } if !unstable_flags.is_empty() { // store a copy of the cli flags separately for `load_unstable_flags_from_config` // (we might also need it again for `reload_rooted_at`) diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 11ac2bae679..b9ca5797fa2 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -79,3 +79,15 @@ pub fn elapsed(duration: Duration) -> String { pub fn is_ci() -> bool { std::env::var("CI").is_ok() || std::env::var("TF_BUILD").is_ok() } + +pub fn indented_lines(text: &str) -> String { + text.lines() + .map(|line| { + if line.is_empty() { + String::from("\n") + } else { + format!(" {}\n", line) + } + }) + .collect() +} diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 44dd7140df6..24f362ce75f 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -69,6 +69,17 @@ fn do_read_manifest( parse(contents, pretty_filename, config)? }; + // Provide a helpful error message for a common user error. + if let Some(package) = toml.get("package").or_else(|| toml.get("project")) { + if let Some(feats) = package.get("cargo-features") { + bail!( + "cargo-features = {} was found in the wrong location, it \ + should be set at the top of Cargo.toml before any tables", + toml::to_string(feats).unwrap() + ); + } + } + let mut unused = BTreeSet::new(); let manifest: TomlManifest = serde_ignored::deserialize(toml, |path| { let mut key = String::new(); @@ -814,7 +825,6 @@ pub struct TomlProject { exclude: Option>, include: Option>, publish: Option, - publish_lockfile: Option, workspace: Option, im_a_teapot: Option, autobins: Option, @@ -1293,19 +1303,6 @@ impl TomlManifest { None | Some(VecStringOrBool::Bool(true)) => None, }; - let publish_lockfile = match project.publish_lockfile { - Some(b) => { - features.require(Feature::publish_lockfile())?; - warnings.push( - "The `publish-lockfile` feature is deprecated and currently \ - has no effect. It may be removed in a future version." - .to_string(), - ); - b - } - None => features.is_enabled(Feature::publish_lockfile()), - }; - if summary.features().contains_key("default-features") { warnings.push( "`default-features = [\"..\"]` was found in [features]. \ @@ -1337,7 +1334,6 @@ impl TomlManifest { custom_metadata, profiles, publish, - publish_lockfile, replace, patch, workspace_config, diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 2e60e6a6bd5..52e3459a389 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -1,24 +1,62 @@ ## Unstable Features -Experimental Cargo features are only available on the nightly channel. You -typically use one of the `-Z` flags to enable them. Run `cargo -Z help` to -see a list of flags available. +Experimental Cargo features are only available on the [nightly channel]. You +are encouraged to experiment with these features to see if they meet your +needs, and if there are any issues or problems. Check the linked tracking +issues listed below for more information on the feature, and click the GitHub +subscribe button if you want future updates. -`-Z unstable-options` is a generic flag for enabling other unstable -command-line flags. Options requiring this will be called out below. +After some period of time, if the feature does not have any major concerns, it +can be [stabilized], which will make it available on stable once the current +nightly release reaches the stable channel (anywhere from 6 to 12 weeks). -Anything which can be configured with a Z flag can also be set in the cargo -config file (`.cargo/config.toml`) in the `unstable` table. For example: +There are three different ways that unstable features can be enabled based on +how the feature works: -```toml -[unstable] -mtime-on-use = true -multitarget = true -timings = ["html"] -``` +* New syntax in `Cargo.toml` requires a `cargo-features` key at the top of + `Cargo.toml`, before any tables. For example: + + ```toml + # This specifies which new Cargo.toml features are enabled. + cargo-features = ["test-dummy-unstable"] + + [package] + name = "my-package" + version = "0.1.0" + im-a-teapot = true # This is a new option enabled by test-dummy-unstable. + ``` + +* New command-line flags, options, and subcommands require the `-Z + unstable-options` CLI option to also be included. For example, the new + `--out-dir` option is only available on nightly: + + ```cargo +nightly build --out-dir=out -Z unstable-options``` + +* `-Z` command-line flags are used to enable new functionality that may not + have an interface, or the interface has not yet been designed, or for more + complex features that affect multiple parts of Cargo. For example, the + [timings](#timings) feature can be enabled with: + + ```cargo +nightly build -Z timings``` -Some unstable features will require you to specify the `cargo-features` key in -`Cargo.toml`. + Run `cargo -Z help` to see a list of flags available. + + Anything which can be configured with a `-Z` flag can also be set in the + cargo [config file] (`.cargo/config.toml`) in the `unstable` table. For + example: + + ```toml + [unstable] + mtime-on-use = true + multitarget = true + timings = ["html"] + ``` + +Each new feature described below should explain how to use it. + +[config file]: config.md +[nightly channel]: ../../book/appendix-07-nightly-rust.html +[stabilized]: https://doc.crates.io/contrib/process/unstable.html#stabilization ### extra-link-arg * Original Pull Request: [#7811](https://github.com/rust-lang/cargo/pull/7811) @@ -1022,3 +1060,35 @@ name = "mypackage" version = "0.0.1" rust-version = "1.42" ``` + + diff --git a/tests/testsuite/cargo_features.rs b/tests/testsuite/cargo_features.rs index 02a41c4fde6..806fc4195c9 100644 --- a/tests/testsuite/cargo_features.rs +++ b/tests/testsuite/cargo_features.rs @@ -103,8 +103,9 @@ fn stable_feature_warns() { p.cargo("build") .with_stderr( "\ -warning: the cargo feature `test-dummy-stable` is now stable and is no longer \ -necessary to be listed in the manifest +warning: the cargo feature `test-dummy-stable` has been stabilized in the 1.0 \ +release and is no longer necessary to be listed in the manifest + See https://doc.rust-lang.org/[..]cargo/ for more information about using this feature. [COMPILING] a [..] [FINISHED] [..] ", @@ -149,6 +150,8 @@ Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ but this is the `stable` channel See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. ", ) .run(); @@ -214,6 +217,8 @@ Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ but this is the `stable` channel See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. ", ) .run(); @@ -256,6 +261,8 @@ Caused by: the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ but this is the `stable` channel See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. ", ) .run(); @@ -327,3 +334,63 @@ fn publish_allowed() { .masquerade_as_nightly_cargo() .run(); } + +#[cargo_test] +fn wrong_position() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + cargo-features = ["test-dummy-unstable"] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at [..] + +Caused by: + cargo-features = [\"test-dummy-unstable\"] was found in the wrong location, it \ + should be set at the top of Cargo.toml before any tables +", + ) + .run(); +} + +#[cargo_test] +fn z_stabilized() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check -Z cache-messages") + .masquerade_as_nightly_cargo() + .with_stderr( + "\ +warning: flag `-Z cache-messages` has been stabilized in the 1.40 release, \ + and is no longer necessary + Message caching is now always enabled. + +[CHECKING] foo [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check -Z offline") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "\ +error: flag `-Z offline` has been stabilized in the 1.36 release + Offline mode is now available via the --offline CLI option + +", + ) + .run(); +} diff --git a/tests/testsuite/pub_priv.rs b/tests/testsuite/pub_priv.rs index a10cb1ef0ef..ba77e7f4a68 100644 --- a/tests/testsuite/pub_priv.rs +++ b/tests/testsuite/pub_priv.rs @@ -115,6 +115,7 @@ error: failed to parse manifest at `[..]` Caused by: the cargo feature `public-dependency` requires a nightly version of Cargo, but this is the `stable` channel See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html#public-dependency for more information about using this feature. " ) .run() diff --git a/tests/testsuite/publish_lockfile.rs b/tests/testsuite/publish_lockfile.rs index a858444ceef..7be995545f8 100644 --- a/tests/testsuite/publish_lockfile.rs +++ b/tests/testsuite/publish_lockfile.rs @@ -48,14 +48,16 @@ fn deprecated() { .build(); p.cargo("package") .masquerade_as_nightly_cargo() + .with_status(101) .with_stderr( "\ -[PACKAGING] foo v0.1.0 ([..]) -[VERIFYING] foo v0.1.0 ([..]) -[WARNING] The `publish-lockfile` feature is deprecated and currently has no effect. \ - It may be removed in a future version. -[COMPILING] foo v0.1.0 ([..]) -[FINISHED] dev [..] +[ERROR] failed to parse manifest at [..] + +Caused by: + the cargo feature `publish-lockfile` has been removed + Remove the feature from Cargo.toml to remove this error. + The publish-lockfile key [..] + See [..] ", ) .run(); diff --git a/tests/testsuite/weak_dep_features.rs b/tests/testsuite/weak_dep_features.rs index 6b8ed355add..255e5628e24 100644 --- a/tests/testsuite/weak_dep_features.rs +++ b/tests/testsuite/weak_dep_features.rs @@ -336,7 +336,7 @@ optional dependency with `?` is not allowed in required-features #[cargo_test] fn weak_with_host_decouple() { - // -Z weak-opt-features with -Z features=host + // -Z weak-opt-features with new resolver // // foo v0.1.0 // └── common v1.0.0 @@ -390,6 +390,7 @@ fn weak_with_host_decouple() { [package] name = "foo" version = "0.1.0" + resolver = "2" [dependencies] common = { version = "1.0", features = ["feat"] } @@ -416,7 +417,7 @@ fn weak_with_host_decouple() { ) .build(); - p.cargo("run -Z weak-dep-features -Z features=host_dep") + p.cargo("run -Z weak-dep-features") .masquerade_as_nightly_cargo() .with_stderr( "\