]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/features.rs
bump version to 0.66.0+pve1-1~bpo11+pve1
[cargo.git] / src / cargo / core / features.rs
CommitLineData
f7c91ba6 1//! Support for nightly features in Cargo itself.
3181ef24
AC
2//!
3//! This file is the version of `feature_gate.rs` in upstream Rust for Cargo
4//! itself and is intended to be the avenue for which new features in Cargo are
5//! gated by default and then eventually stabilized. All known stable and
6//! unstable features are tracked in this file.
7//!
8//! If you're reading this then you're likely interested in adding a feature to
778b5357
EH
9//! Cargo, and the good news is that it shouldn't be too hard! First determine
10//! how the feature should be gated:
3181ef24 11//!
778b5357
EH
12//! * New syntax in Cargo.toml should use `cargo-features`.
13//! * New CLI options should use `-Z unstable-options`.
14//! * New functionality that may not have an interface, or the interface has
15//! not yet been designed, or for more complex features that affect multiple
16//! parts of Cargo should use a new `-Z` flag.
17//!
18//! See below for more details.
19//!
20//! When adding new tests for your feature, usually the tests should go into a
21//! new module of the testsuite. See
22//! <https://doc.crates.io/contrib/tests/writing.html> for more information on
23//! writing tests. Particularly, check out the "Testing Nightly Features"
24//! section for testing unstable features.
25//!
26//! After you have added your feature, be sure to update the unstable
27//! documentation at `src/doc/src/reference/unstable.md` to include a short
28//! description of how to use your new feature.
29//!
30//! And hopefully that's it!
31//!
32//! ## New Cargo.toml syntax
33//!
34//! The steps for adding new Cargo.toml syntax are:
35//!
36//! 1. Add the cargo-features unstable gate. Search below for "look here" to
37//! find the `features!` macro and add your feature to the list.
38//!
39//! 2. Update the Cargo.toml parsing code to handle your new feature.
40//!
41//! 3. Wherever you added the new parsing code, call
42//! `features.require(Feature::my_feature_name())?` if the new syntax is
43//! used. This will return an error if the user hasn't listed the feature
44//! in `cargo-features` or this is not the nightly channel.
45//!
46//! ## `-Z unstable-options`
3181ef24 47//!
778b5357
EH
48//! `-Z unstable-options` is intended to force the user to opt-in to new CLI
49//! flags, options, and new subcommands.
3181ef24 50//!
778b5357 51//! The steps to add a new command-line option are:
3181ef24 52//!
778b5357
EH
53//! 1. Add the option to the CLI parsing code. In the help text, be sure to
54//! include `(unstable)` to note that this is an unstable option.
55//! 2. Where the CLI option is loaded, be sure to call
56//! [`CliUnstable::fail_if_stable_opt`]. This will return an error if `-Z
57//! unstable options` was not passed.
3181ef24 58//!
778b5357 59//! ## `-Z` options
3181ef24 60//!
778b5357 61//! The steps to add a new `-Z` option are:
3181ef24 62//!
778b5357
EH
63//! 1. Add the option to the [`CliUnstable`] struct below. Flags can take an
64//! optional value if you want.
d7122e69 65//! 2. Update the [`CliUnstable::add`][CliUnstable] function to parse the flag.
778b5357
EH
66//! 3. Wherever the new functionality is implemented, call
67//! [`Config::cli_unstable`][crate::util::config::Config::cli_unstable] to
68//! get an instance of `CliUnstable` and check if the option has been
69//! enabled on the `CliUnstable` instance. Nightly gating is already
70//! handled, so no need to worry about that.
740525f2 71//!
778b5357
EH
72//! ## Stabilization
73//!
74//! For the stabilization process, see
75//! <https://doc.crates.io/contrib/process/unstable.html#stabilization>.
76//!
77//! The steps for stabilizing are roughly:
78//!
79//! 1. Update the feature to be stable, based on the kind of feature:
80//! 1. `cargo-features`: Change the feature to `stable` in the `features!`
2c99f654 81//! macro below, and include the version and a URL for the documentation.
778b5357
EH
82//! 2. `-Z unstable-options`: Find the call to `fail_if_stable_opt` and
83//! remove it. Be sure to update the man pages if necessary.
d7122e69
PM
84//! 3. `-Z` flag: Change the parsing code in [`CliUnstable::add`][CliUnstable]
85//! to call `stabilized_warn` or `stabilized_err` and remove the field from
05d37ae2 86//! `CliUnstable. Remove the `(unstable)` note in the clap help text if
87//! necessary.
778b5357 88//! 2. Remove `masquerade_as_nightly_cargo` from any tests, and remove
c239e407
SS
89//! `cargo-features` from `Cargo.toml` test files if any. You can
90//! quickly find what needs to be removed by searching for the name
91//! of the feature, e.g. `print_im_a_teapot`
2c99f654
EH
92//! 3. Update the docs in unstable.md to move the section to the bottom
93//! and summarize it similar to the other entries. Update the rest of the
94//! documentation to add the new feature.
3181ef24 95
6be9eecb 96use std::collections::BTreeSet;
3181ef24 97use std::env;
2c99f654 98use std::fmt::{self, Write};
1d82d2b3 99use std::str::FromStr;
3181ef24 100
3a18c89a 101use anyhow::{bail, Error};
88810035 102use cargo_util::ProcessBuilder;
9ed82b57 103use serde::{Deserialize, Serialize};
84130089 104
df0c64eb 105use crate::core::resolver::ResolveBehavior;
04ddd4d0 106use crate::util::errors::CargoResult;
ba584a95 107use crate::util::{indented_lines, iter_join};
4b096bea 108use crate::Config;
3181ef24 109
05d37ae2 110pub const HIDDEN: &str = "";
35b843ef
EH
111pub const SEE_CHANNELS: &str =
112 "See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information \
113 about Rust release channels.";
114
3bbe93ce 115/// The edition of the compiler (RFC 2052)
1e682848 116#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
3bbe93ce
KN
117pub enum Edition {
118 /// The 2015 edition
119 Edition2015,
120 /// The 2018 edition
121 Edition2018,
de839dab
MB
122 /// The 2021 edition
123 Edition2021,
1d82d2b3
MG
124}
125
aa61976c
EH
126// Adding a new edition:
127// - Add the next edition to the enum.
128// - Update every match expression that now fails to compile.
129// - Update the `FromStr` impl.
130// - Update CLI_VALUES to include the new edition.
131// - Set LATEST_UNSTABLE to Some with the new edition.
132// - Add an unstable feature to the features! macro below for the new edition.
133// - Gate on that new feature in TomlManifest::to_real_manifest.
134// - Update the shell completion files.
135// - Update any failing tests (hopefully there are very few).
2c99f654
EH
136// - Update unstable.md to add a new section for this new edition (see
137// https://github.com/rust-lang/cargo/blob/3ebb5f15a940810f250b68821149387af583a79e/src/doc/src/reference/unstable.md?plain=1#L1238-L1264
138// as an example).
aa61976c
EH
139//
140// Stabilization instructions:
141// - Set LATEST_UNSTABLE to None.
142// - Set LATEST_STABLE to the new version.
143// - Update `is_stable` to `true`.
144// - Set the editionNNNN feature to stable in the features macro below.
7a637709 145// - Update any tests that are affected.
aa61976c 146// - Update the man page for the --edition flag.
2c99f654
EH
147// - Update unstable.md to move the edition section to the bottom.
148// - Update the documentation:
149// - Update any features impacted by the edition.
150// - Update manifest.md#the-edition-field.
151// - Update the --edition flag (options-new.md).
152// - Rebuild man pages.
c221fec9 153impl Edition {
aa61976c
EH
154 /// The latest edition that is unstable.
155 ///
156 /// This is `None` if there is no next unstable edition.
e6a783ac 157 pub const LATEST_UNSTABLE: Option<Edition> = None;
aa61976c 158 /// The latest stable edition.
e6a783ac 159 pub const LATEST_STABLE: Edition = Edition::Edition2021;
aa61976c
EH
160 /// Possible values allowed for the `--edition` CLI flag.
161 ///
162 /// This requires a static value due to the way clap works, otherwise I
163 /// would have built this dynamically.
fc0ca1e1 164 pub const CLI_VALUES: [&'static str; 3] = ["2015", "2018", "2021"];
2ae72ff7
EH
165
166 /// Returns the first version that a particular edition was released on
167 /// stable.
c221fec9
DO
168 pub(crate) fn first_version(&self) -> Option<semver::Version> {
169 use Edition::*;
170 match self {
171 Edition2015 => None,
172 Edition2018 => Some(semver::Version::new(1, 31, 0)),
db3776cf 173 Edition2021 => Some(semver::Version::new(1, 56, 0)),
c221fec9
DO
174 }
175 }
2ae72ff7
EH
176
177 /// Returns `true` if this edition is stable in this release.
178 pub fn is_stable(&self) -> bool {
179 use Edition::*;
180 match self {
181 Edition2015 => true,
182 Edition2018 => true,
e6a783ac 183 Edition2021 => true,
2ae72ff7
EH
184 }
185 }
186
28850225
EH
187 /// Returns the previous edition from this edition.
188 ///
189 /// Returns `None` for 2015.
190 pub fn previous(&self) -> Option<Edition> {
191 use Edition::*;
192 match self {
193 Edition2015 => None,
194 Edition2018 => Some(Edition2015),
195 Edition2021 => Some(Edition2018),
196 }
197 }
198
c3032137
EH
199 /// Returns the next edition from this edition, returning the last edition
200 /// if this is already the last one.
201 pub fn saturating_next(&self) -> Edition {
202 use Edition::*;
203 match self {
204 Edition2015 => Edition2018,
205 Edition2018 => Edition2021,
206 Edition2021 => Edition2021,
207 }
208 }
209
2ae72ff7
EH
210 /// Updates the given [`ProcessBuilder`] to include the appropriate flags
211 /// for setting the edition.
212 pub(crate) fn cmd_edition_arg(&self, cmd: &mut ProcessBuilder) {
213 if *self != Edition::Edition2015 {
214 cmd.arg(format!("--edition={}", self));
215 }
216 if !self.is_stable() {
217 cmd.arg("-Z").arg("unstable-options");
218 }
219 }
28850225
EH
220
221 /// Whether or not this edition supports the `rust_*_compatibility` lint.
222 ///
5825206a
EH
223 /// Ideally this would not be necessary, but editions may not have any
224 /// lints, and thus `rustc` doesn't recognize it. Perhaps `rustc` could
225 /// create an empty group instead?
28850225
EH
226 pub(crate) fn supports_compat_lint(&self) -> bool {
227 use Edition::*;
228 match self {
229 Edition2015 => false,
230 Edition2018 => true,
5825206a 231 Edition2021 => true,
28850225
EH
232 }
233 }
aa61976c
EH
234
235 /// Whether or not this edition supports the `rust_*_idioms` lint.
236 ///
237 /// Ideally this would not be necessary...
238 pub(crate) fn supports_idiom_lint(&self) -> bool {
239 use Edition::*;
240 match self {
241 Edition2015 => false,
242 Edition2018 => true,
243 Edition2021 => false,
244 }
245 }
df0c64eb
WL
246
247 pub(crate) fn default_resolve_behavior(&self) -> ResolveBehavior {
248 if *self >= Edition::Edition2021 {
249 ResolveBehavior::V2
250 } else {
251 ResolveBehavior::V1
252 }
253 }
c221fec9
DO
254}
255
3bbe93ce 256impl fmt::Display for Edition {
b8b7faee 257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2d1af7b5 258 match *self {
3bbe93ce
KN
259 Edition::Edition2015 => f.write_str("2015"),
260 Edition::Edition2018 => f.write_str("2018"),
de839dab 261 Edition::Edition2021 => f.write_str("2021"),
2d1af7b5
MG
262 }
263 }
264}
3bbe93ce 265impl FromStr for Edition {
84130089
AC
266 type Err = Error;
267 fn from_str(s: &str) -> Result<Self, Error> {
1d82d2b3 268 match s {
3bbe93ce
KN
269 "2015" => Ok(Edition::Edition2015),
270 "2018" => Ok(Edition::Edition2018),
de839dab
MB
271 "2021" => Ok(Edition::Edition2021),
272 s if s.parse().map_or(false, |y: u16| y > 2021 && y < 2050) => bail!(
f57c4c9b 273 "this version of Cargo is older than the `{}` edition, \
de839dab 274 and only supports `2015`, `2018`, and `2021` editions.",
f57c4c9b
K
275 s
276 ),
50db59e0 277 s => bail!(
de839dab
MB
278 "supported edition values are `2015`, `2018`, or `2021`, \
279 but `{}` is unknown",
fecb7246
AC
280 s
281 ),
1d82d2b3
MG
282 }
283 }
284}
285
3d029039 286#[derive(PartialEq)]
3181ef24
AC
287enum Status {
288 Stable,
289 Unstable,
7bbef3de 290 Removed,
3181ef24
AC
291}
292
293macro_rules! features {
294 (
7bbef3de 295 $(($stab:ident, $feature:ident, $version:expr, $docs:expr),)*
3181ef24
AC
296 ) => (
297 #[derive(Default, Clone, Debug)]
298 pub struct Features {
299 $($feature: bool,)*
300 activated: Vec<String>,
3a86ecf2 301 nightly_features_allowed: bool,
2c99f654 302 is_local: bool,
3181ef24
AC
303 }
304
305 impl Feature {
306 $(
307 pub fn $feature() -> &'static Feature {
f26fc37d 308 fn get(features: &Features) -> bool {
3d029039 309 stab!($stab) == Status::Stable || features.$feature
3181ef24
AC
310 }
311 static FEAT: Feature = Feature {
312 name: stringify!($feature),
7bbef3de
EH
313 stability: stab!($stab),
314 version: $version,
315 docs: $docs,
676edacf 316 get,
3181ef24
AC
317 };
318 &FEAT
319 }
320 )*
f26fc37d
AC
321
322 fn is_enabled(&self, features: &Features) -> bool {
323 (self.get)(features)
324 }
3181ef24
AC
325 }
326
327 impl Features {
7bbef3de 328 fn status(&mut self, feature: &str) -> Option<(&mut bool, &'static Feature)> {
3181ef24
AC
329 if feature.contains("_") {
330 return None
331 }
332 let feature = feature.replace("-", "_");
333 $(
334 if feature == stringify!($feature) {
7bbef3de 335 return Some((&mut self.$feature, Feature::$feature()))
3181ef24
AC
336 }
337 )*
338 None
339 }
340 }
341 )
342}
343
344macro_rules! stab {
a4947c2b
EH
345 (stable) => {
346 Status::Stable
347 };
348 (unstable) => {
349 Status::Unstable
350 };
7bbef3de
EH
351 (removed) => {
352 Status::Removed
353 };
3181ef24
AC
354}
355
f5723e51
AR
356// A listing of all features in Cargo.
357//
358// "look here"
359//
360// This is the macro that lists all stable and unstable features in Cargo.
361// You'll want to add to this macro whenever you add a feature to Cargo, also
362// following the directions above.
363//
364// Note that all feature names here are valid Rust identifiers, but the `_`
365// character is translated to `-` when specified in the `cargo-features`
366// manifest entry in `Cargo.toml`.
3181ef24 367features! {
7bbef3de
EH
368 // A dummy feature that doesn't actually gate anything, but it's used in
369 // testing to ensure that we can enable stable features.
370 (stable, test_dummy_stable, "1.0", ""),
3181ef24 371
7bbef3de
EH
372 // A dummy feature that gates the usage of the `im-a-teapot` manifest
373 // entry. This is basically just intended for tests.
374 (unstable, test_dummy_unstable, "", "reference/unstable.html"),
3181ef24 375
7bbef3de
EH
376 // Downloading packages from alternative registry indexes.
377 (stable, alternative_registries, "1.34", "reference/registries.html"),
d89cd903 378
7bbef3de
EH
379 // Using editions
380 (stable, edition, "1.31", "reference/manifest.html#the-edition-field"),
fa5be237 381
7bbef3de
EH
382 // Renaming a package in the manifest via the `package` key
383 (stable, rename_dependency, "1.31", "reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml"),
79942fea 384
7bbef3de 385 // Whether a lock file is published with this crate
2c99f654 386 (removed, publish_lockfile, "1.37", "reference/unstable.html#publish-lockfile"),
a4a3302d 387
7bbef3de
EH
388 // Overriding profiles for dependencies.
389 (stable, profile_overrides, "1.41", "reference/profiles.html#overrides"),
575d6e81 390
7bbef3de
EH
391 // "default-run" manifest option,
392 (stable, default_run, "1.37", "reference/manifest.html#the-default-run-field"),
0b6f4206 393
7bbef3de
EH
394 // Declarative build scripts.
395 (unstable, metabuild, "", "reference/unstable.html#metabuild"),
2be857af 396
7bbef3de
EH
397 // Specifying the 'public' attribute on dependencies
398 (unstable, public_dependency, "", "reference/unstable.html#public-dependency"),
87f1a4b2 399
7bbef3de 400 // Allow to specify profiles other than 'dev', 'release', 'test', etc.
895f5271 401 (stable, named_profiles, "1.57", "reference/profiles.html#custom-profiles"),
756ab7d5 402
7bbef3de
EH
403 // Opt-in new-resolver behavior.
404 (stable, resolver, "1.51", "reference/resolver.html#resolver-versions"),
787e75b7 405
7bbef3de 406 // Allow to specify whether binaries should be stripped.
75e544d5 407 (stable, strip, "1.58", "reference/profiles.html#strip-option"),
9551ed34 408
7bbef3de 409 // Specifying a minimal 'rust-version' attribute for crates
d7834565 410 (stable, rust_version, "1.56", "reference/manifest.html#the-rust-version-field"),
2ae72ff7
EH
411
412 // Support for 2021 edition.
e6a783ac 413 (stable, edition2021, "1.56", "reference/manifest.html#the-edition-field"),
5e11afc1
LG
414
415 // Allow to specify per-package targets (compile kinds)
416 (unstable, per_package_target, "", "reference/unstable.html#per-package-target"),
538d59a8 417
418 // Allow to specify which codegen backend should be used.
419 (unstable, codegen_backend, "", "reference/unstable.html#codegen-backend"),
9e2790f8
TS
420
421 // Allow specifying different binary name apart from the crate name
422 (unstable, different_binary_name, "", "reference/unstable.html#different-binary-name"),
68fd6731
ZH
423
424 // Allow specifying rustflags directly in a profile
425 (unstable, profile_rustflags, "", "reference/unstable.html#profile-rustflags-option"),
ccb321a6 426
427 // Allow specifying rustflags directly in a profile
2c810afd 428 (stable, workspace_inheritance, "1.64", "reference/unstable.html#workspace-inheritance"),
3181ef24
AC
429}
430
431pub struct Feature {
432 name: &'static str,
7bbef3de
EH
433 stability: Status,
434 version: &'static str,
435 docs: &'static str,
f26fc37d 436 get: fn(&Features) -> bool,
3181ef24
AC
437}
438
439impl Features {
4b096bea
JN
440 pub fn new(
441 features: &[String],
442 config: &Config,
443 warnings: &mut Vec<String>,
2c99f654 444 is_local: bool,
4b096bea 445 ) -> CargoResult<Features> {
3181ef24 446 let mut ret = Features::default();
a6394bcc 447 ret.nightly_features_allowed = config.nightly_features_allowed;
2c99f654 448 ret.is_local = is_local;
3181ef24 449 for feature in features {
4a90ab69 450 ret.add(feature, config, warnings)?;
3181ef24
AC
451 ret.activated.push(feature.to_string());
452 }
453 Ok(ret)
454 }
455
4a90ab69
JG
456 fn add(
457 &mut self,
458 feature_name: &str,
459 config: &Config,
460 warnings: &mut Vec<String>,
461 ) -> CargoResult<()> {
a6394bcc 462 let nightly_features_allowed = self.nightly_features_allowed;
2c99f654 463 let is_local = self.is_local;
7bbef3de 464 let (slot, feature) = match self.status(feature_name) {
3181ef24 465 Some(p) => p,
7bbef3de 466 None => bail!("unknown cargo feature `{}`", feature_name),
3181ef24
AC
467 };
468
469 if *slot {
7bbef3de
EH
470 bail!(
471 "the cargo feature `{}` has already been activated",
472 feature_name
473 );
3181ef24
AC
474 }
475
7bbef3de
EH
476 let see_docs = || {
477 let url_channel = match channel().as_str() {
478 "dev" | "nightly" => "nightly/",
479 "beta" => "beta/",
480 _ => "",
481 };
482 format!(
483 "See https://doc.rust-lang.org/{}cargo/{} for more information \
484 about using this feature.",
485 url_channel, feature.docs
486 )
487 };
488
489 match feature.stability {
3181ef24 490 Status::Stable => {
2c99f654
EH
491 // The user can't do anything about non-local packages.
492 // Warnings are usually suppressed, but just being cautious here.
493 if is_local {
494 let warning = format!(
495 "the cargo feature `{}` has been stabilized in the {} \
496 release and is no longer necessary to be listed in the \
497 manifest\n {}",
498 feature_name,
499 feature.version,
500 see_docs()
501 );
502 warnings.push(warning);
503 }
3181ef24 504 }
a6394bcc 505 Status::Unstable if !nightly_features_allowed => bail!(
1e682848 506 "the cargo feature `{}` requires a nightly version of \
35b843ef 507 Cargo, but this is the `{}` channel\n\
7bbef3de
EH
508 {}\n{}",
509 feature_name,
35b843ef 510 channel(),
7bbef3de
EH
511 SEE_CHANNELS,
512 see_docs()
1e682848 513 ),
98372390
JG
514 Status::Unstable => {
515 if let Some(allow) = &config.cli_unstable().allow_features {
516 if !allow.contains(feature_name) {
517 bail!(
be333390 518 "the feature `{}` is not in the list of allowed features: [{}]",
98372390 519 feature_name,
be333390 520 iter_join(allow, ", "),
98372390
JG
521 );
522 }
523 }
4a90ab69 524 }
2c99f654
EH
525 Status::Removed => {
526 let mut msg = format!(
527 "the cargo feature `{}` has been removed in the {} release\n\n",
528 feature_name, feature.version
529 );
530 if self.is_local {
531 drop(writeln!(
532 msg,
533 "Remove the feature from Cargo.toml to remove this error."
534 ));
535 } else {
536 drop(writeln!(
537 msg,
538 "This package cannot be used with this version of Cargo, \
539 as the unstable feature `{}` is no longer supported.",
540 feature_name
541 ));
542 }
543 drop(writeln!(msg, "{}", see_docs()));
544 bail!(msg);
545 }
3181ef24
AC
546 }
547
548 *slot = true;
549
550 Ok(())
551 }
552
553 pub fn activated(&self) -> &[String] {
554 &self.activated
555 }
556
557 pub fn require(&self, feature: &Feature) -> CargoResult<()> {
f26fc37d 558 if feature.is_enabled(self) {
2c99f654
EH
559 return Ok(());
560 }
561 let feature_name = feature.name.replace("_", "-");
562 let mut msg = format!(
563 "feature `{}` is required\n\
564 \n\
565 The package requires the Cargo feature called `{}`, but \
566 that feature is not stabilized in this version of Cargo ({}).\n\
567 ",
568 feature_name,
569 feature_name,
570 crate::version(),
571 );
572
573 if self.nightly_features_allowed {
574 if self.is_local {
575 drop(writeln!(
576 msg,
577 "Consider adding `cargo-features = [\"{}\"]` \
578 to the top of Cargo.toml (above the [package] table) \
579 to tell Cargo you are opting in to use this unstable feature.",
580 feature_name
581 ));
3181ef24 582 } else {
2c99f654
EH
583 drop(writeln!(
584 msg,
585 "Consider trying a more recent nightly release."
586 ));
3181ef24 587 }
2c99f654
EH
588 } else {
589 drop(writeln!(
590 msg,
591 "Consider trying a newer version of Cargo \
592 (this may require the nightly release)."
593 ));
3181ef24 594 }
2c99f654
EH
595 drop(writeln!(
596 msg,
597 "See https://doc.rust-lang.org/nightly/cargo/{} for more information \
598 about the status of this feature.",
599 feature.docs
600 ));
601
602 bail!("{}", msg);
3181ef24 603 }
2d1af7b5
MG
604
605 pub fn is_enabled(&self, feature: &Feature) -> bool {
606 feature.is_enabled(self)
607 }
3181ef24
AC
608}
609
05d37ae2 610macro_rules! unstable_cli_options {
611 (
612 $(
613 $(#[$meta:meta])?
6f75160a
EH
614 $element: ident: $ty: ty = ($help: expr ),
615 )*
05d37ae2 616 ) => {
617 /// A parsed representation of all unstable flags that Cargo accepts.
618 ///
619 /// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
620 /// gating unstable functionality to Cargo. These flags are only available on
621 /// the nightly channel of Cargo.
622 #[derive(Default, Debug, Deserialize)]
623 #[serde(default, rename_all = "kebab-case")]
624 pub struct CliUnstable {
625 $(
626 $(#[$meta])?
627 pub $element: $ty
628 ),*
629 }
630 impl CliUnstable {
631 pub fn help() -> Vec<(&'static str, &'static str)> {
632 let fields = vec![$((stringify!($element), $help)),*];
633 fields
634 }
635 }
636 }
637}
638
639unstable_cli_options!(
dc35abf1 640 // Permanently unstable features:
05d37ae2 641 allow_features: Option<BTreeSet<String>> = ("Allow *only* the listed unstable features"),
7248f4b7 642 print_im_a_teapot: bool = (HIDDEN),
4a90ab69 643
dc35abf1
JG
644 // All other unstable features.
645 // Please keep this list lexiographically ordered.
05d37ae2 646 advanced_env: bool = (HIDDEN),
647 avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"),
648 binary_dep_depinfo: bool = ("Track changes to dependency artifacts"),
7248f4b7 649 bindeps: bool = ("Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates"),
78314caf 650 #[serde(deserialize_with = "deserialize_build_std")]
05d37ae2 651 build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"),
652 build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
653 config_include: bool = ("Enable the `include` key in config files"),
05d37ae2 654 credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
c08a666a 655 #[serde(deserialize_with = "deserialize_check_cfg")]
d16631fd 656 check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"),
05d37ae2 657 doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"),
658 doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
659 dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
05d37ae2 660 features: Option<Vec<String>> = (HIDDEN),
661 jobserver_per_rustc: bool = (HIDDEN),
662 minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
663 mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"),
664 multitarget: bool = ("Allow passing multiple `--target` flags to the cargo subcommand selected"),
05d37ae2 665 no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
666 panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
0a603fdf 667 host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"),
a9faf490 668 sparse_registry: bool = ("Support plain-HTTP-based crate registries"),
e24bf922 669 target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
05d37ae2 670 rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"),
671 separate_nightlies: bool = (HIDDEN),
672 terminal_width: Option<Option<usize>> = ("Provide a terminal width to rustc for error truncation"),
05d37ae2 673 unstable_options: bool = ("Allow the usage of unstable options"),
9fb78cf6
WC
674 // TODO(wcrichto): move scrape example configuration into Cargo.toml before stabilization
675 // See: https://github.com/rust-lang/cargo/pull/9525#discussion_r728470927
676 rustdoc_scrape_examples: Option<String> = ("Allow rustdoc to scrape examples from reverse-dependencies for documentation"),
6f75160a 677 skip_rustdoc_fingerprint: bool = (HIDDEN),
05d37ae2 678);
f26fc37d 679
00615fc5
EH
680const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
681 enabled when used on an interactive console.\n\
682 See https://doc.rust-lang.org/cargo/reference/config.html#termprogresswhen \
683 for information on controlling the progress bar.";
684
685const STABILIZED_OFFLINE: &str = "Offline mode is now available via the \
686 --offline CLI option";
687
688const STABILIZED_CACHE_MESSAGES: &str = "Message caching is now always enabled.";
689
690const STABILIZED_INSTALL_UPGRADE: &str = "Packages are now always upgraded if \
691 they appear out of date.\n\
692 See https://doc.rust-lang.org/cargo/commands/cargo-install.html for more \
693 information on how upgrading works.";
694
695const STABILIZED_CONFIG_PROFILE: &str = "See \
696 https://doc.rust-lang.org/cargo/reference/config.html#profile for more \
697 information about specifying profiles in config.";
698
699const STABILIZED_CRATE_VERSIONS: &str = "The crate version is now \
700 automatically added to the documentation.";
701
702const STABILIZED_PACKAGE_FEATURES: &str = "Enhanced feature flag behavior is now \
703 available in virtual workspaces, and `member/feature-name` syntax is also \
704 always available. Other extensions require setting `resolver = \"2\"` in \
705 Cargo.toml.\n\
706 See https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags \
707 for more information.";
708
709const STABILIZED_FEATURES: &str = "The new feature resolver is now available \
710 by specifying `resolver = \"2\"` in Cargo.toml.\n\
711 See https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 \
712 for more information.";
713
e7370394
DF
714const STABILIZED_EXTRA_LINK_ARG: &str = "Additional linker arguments are now \
715 supported without passing this flag.";
716
ab38ce0f
JT
717const STABILIZED_CONFIGURABLE_ENV: &str = "The [env] section is now always enabled.";
718
1e0d564f
JG
719const STABILIZED_PATCH_IN_CONFIG: &str = "The patch-in-config feature is now always enabled.";
720
895f5271
EH
721const STABILIZED_NAMED_PROFILES: &str = "The named-profiles feature is now always enabled.\n\
722 See https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#custom-profiles \
723 for more information";
724
997bf3ff
AH
725const STABILIZED_FUTURE_INCOMPAT_REPORT: &str =
726 "The future-incompat-report feature is now always enabled.";
727
43a063c8
EH
728const STABILIZED_WEAK_DEP_FEATURES: &str = "Weak dependency features are now always available.";
729
730const STABILISED_NAMESPACED_FEATURES: &str = "Namespaced features are now always available.";
731
c0669189
JT
732const STABILIZED_TIMINGS: &str = "The -Ztimings option has been stabilized as --timings.";
733
85e89cf3
WL
734const STABILISED_MULTITARGET: &str = "Multiple `--target` options are now always available.";
735
78314caf
AC
736fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
737where
738 D: serde::Deserializer<'de>,
739{
740 let crates = match <Option<Vec<String>>>::deserialize(deserializer)? {
741 Some(list) => list,
742 None => return Ok(None),
743 };
744 let v = crates.join(",");
745 Ok(Some(
746 crate::core::compiler::standard_lib::parse_unstable_flag(Some(&v)),
747 ))
748}
749
c08a666a
U
750fn deserialize_check_cfg<'de, D>(
751 deserializer: D,
752) -> Result<Option<(bool, bool, bool, bool)>, D::Error>
753where
754 D: serde::Deserializer<'de>,
755{
756 use serde::de::Error;
757 let crates = match <Option<Vec<String>>>::deserialize(deserializer)? {
758 Some(list) => list,
759 None => return Ok(None),
760 };
761
762 parse_check_cfg(crates.into_iter()).map_err(D::Error::custom)
763}
764
765fn parse_check_cfg(
766 it: impl Iterator<Item = impl AsRef<str>>,
767) -> CargoResult<Option<(bool, bool, bool, bool)>> {
768 let mut features = false;
769 let mut well_known_names = false;
770 let mut well_known_values = false;
771 let mut output = false;
772
773 for e in it {
774 match e.as_ref() {
775 "features" => features = true,
776 "names" => well_known_names = true,
777 "values" => well_known_values = true,
778 "output" => output = true,
779 _ => bail!("unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs"),
780 }
781 }
782
783 Ok(Some((
784 features,
785 well_known_names,
786 well_known_values,
787 output,
788 )))
789}
790
f26fc37d 791impl CliUnstable {
4b096bea
JN
792 pub fn parse(
793 &mut self,
794 flags: &[String],
795 nightly_features_allowed: bool,
796 ) -> CargoResult<Vec<String>> {
797 if !flags.is_empty() && !nightly_features_allowed {
50db59e0 798 bail!(
35b843ef
EH
799 "the `-Z` flag is only accepted on the nightly channel of Cargo, \
800 but this is the `{}` channel\n\
801 {}",
802 channel(),
803 SEE_CHANNELS
804 );
f26fc37d 805 }
00615fc5 806 let mut warnings = Vec::new();
e7ca0751
JG
807 // We read flags twice, first to get allowed-features (if specified),
808 // and then to read the remaining unstable flags.
f26fc37d 809 for flag in flags {
4a90ab69
JG
810 if flag.starts_with("allow-features=") {
811 self.add(flag, &mut warnings)?;
812 }
813 }
e7ca0751
JG
814 for flag in flags {
815 self.add(flag, &mut warnings)?;
f26fc37d 816 }
00615fc5 817 Ok(warnings)
f26fc37d
AC
818 }
819
00615fc5 820 fn add(&mut self, flag: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
f26fc37d
AC
821 let mut parts = flag.splitn(2, '=');
822 let k = parts.next().unwrap();
823 let v = parts.next();
824
50db59e0 825 fn parse_bool(key: &str, value: Option<&str>) -> CargoResult<bool> {
f26fc37d 826 match value {
1e682848 827 None | Some("yes") => Ok(true),
f26fc37d 828 Some("no") => Ok(false),
50db59e0 829 Some(s) => bail!("flag -Z{} expected `no` or `yes`, found: `{}`", key, s),
f26fc37d
AC
830 }
831 }
832
7caa1612
EH
833 fn parse_features(value: Option<&str>) -> Vec<String> {
834 match value {
835 None => Vec::new(),
4a90ab69 836 Some("") => Vec::new(),
7caa1612
EH
837 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
838 }
839 }
840
50db59e0
EH
841 // Asserts that there is no argument to the flag.
842 fn parse_empty(key: &str, value: Option<&str>) -> CargoResult<bool> {
843 if let Some(v) = value {
844 bail!("flag -Z{} does not take a value, found: `{}`", key, v);
845 }
846 Ok(true)
ea0b5ca9 847 }
50db59e0 848
c46b9a70
EK
849 fn parse_usize_opt(value: Option<&str>) -> CargoResult<Option<usize>> {
850 Ok(match value {
851 Some(value) => match value.parse::<usize>() {
852 Ok(value) => Some(value),
853 Err(e) => bail!("expected a number, found: {}", e),
854 },
855 None => None,
856 })
857 }
858
00615fc5
EH
859 let mut stabilized_warn = |key: &str, version: &str, message: &str| {
860 warnings.push(format!(
861 "flag `-Z {}` has been stabilized in the {} release, \
862 and is no longer necessary\n{}",
863 key,
864 version,
865 indented_lines(message)
866 ));
867 };
868
869 // Use this if the behavior now requires another mechanism to enable.
870 let stabilized_err = |key: &str, version: &str, message: &str| {
871 Err(anyhow::format_err!(
872 "flag `-Z {}` has been stabilized in the {} release\n{}",
873 key,
874 version,
875 indented_lines(message)
876 ))
877 };
878
e7ca0751
JG
879 if let Some(allowed) = &self.allow_features {
880 if k != "allow-features" && !allowed.contains(k) {
881 bail!(
be333390 882 "the feature `{}` is not in the list of allowed features: [{}]",
e7ca0751 883 k,
be333390 884 iter_join(allowed, ", ")
e7ca0751
JG
885 );
886 }
887 }
888
f26fc37d 889 match k {
50db59e0 890 "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
4a90ab69 891 "allow-features" => self.allow_features = Some(parse_features(v).into_iter().collect()),
50db59e0
EH
892 "unstable-options" => self.unstable_options = parse_empty(k, v)?,
893 "no-index-update" => self.no_index_update = parse_empty(k, v)?,
894 "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
895 "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
50db59e0 896 "advanced-env" => self.advanced_env = parse_empty(k, v)?,
e7eda2f9 897 "config-include" => self.config_include = parse_empty(k, v)?,
c08a666a
U
898 "check-cfg" => {
899 self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))?
900 }
50db59e0 901 "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
30df7317 902 // can also be set in .cargo/config or with and ENV
50db59e0 903 "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
895f5271 904 "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES),
50db59e0 905 "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
7248f4b7 906 "bindeps" => self.bindeps = parse_empty(k, v)?,
1f14fa31
EH
907 "build-std" => {
908 self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
909 }
1faf5b9f 910 "build-std-features" => self.build_std_features = Some(parse_features(v)),
50db59e0 911 "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
b4c4028f 912 "doctest-in-workspace" => self.doctest_in_workspace = parse_empty(k, v)?,
50db59e0 913 "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
494b3c0a 914 "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
0a603fdf 915 "host-config" => self.host_config = parse_empty(k, v)?,
e24bf922 916 "target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
00615fc5
EH
917 "features" => {
918 // For now this is still allowed (there are still some
919 // unstable options like "compare"). This should be removed at
920 // some point, and migrate to a new -Z flag for any future
921 // things.
922 let feats = parse_features(v);
592b01c7 923 let stab_is_not_empty = feats.iter().any(|feat| {
a255d96a
DE
924 matches!(
925 feat.as_str(),
926 "build_dep" | "host_dep" | "dev_dep" | "itarget" | "all"
927 )
928 });
592b01c7 929 if stab_is_not_empty || feats.is_empty() {
00615fc5
EH
930 // Make this stabilized_err once -Zfeature support is removed.
931 stabilized_warn(k, "1.51", STABILIZED_FEATURES);
932 }
933 self.features = Some(feats);
934 }
fdfdb3dd 935 "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
85e89cf3 936 "multitarget" => stabilized_warn(k, "1.64", STABILISED_MULTITARGET),
e0f9643b 937 "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
6d740aa2 938 "terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
a9faf490 939 "sparse-registry" => self.sparse_registry = parse_empty(k, v)?,
43a063c8
EH
940 "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES),
941 "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES),
cc6df1d7 942 "credential-process" => self.credential_process = parse_empty(k, v)?,
8713cd7e
JN
943 "rustdoc-scrape-examples" => {
944 if let Some(s) = v {
945 self.rustdoc_scrape_examples = Some(s.to_string())
946 } else {
947 bail!(
948 r#"-Z rustdoc-scrape-examples must take "all" or "examples" as an argument"#
949 )
950 }
951 }
6f75160a 952 "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?,
00615fc5
EH
953 "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS),
954 "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?,
955 "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES),
956 "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE),
957 "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE),
958 "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS),
959 "package-features" => stabilized_warn(k, "1.51", STABILIZED_PACKAGE_FEATURES),
e7370394 960 "extra-link-arg" => stabilized_warn(k, "1.56", STABILIZED_EXTRA_LINK_ARG),
ab38ce0f 961 "configurable-env" => stabilized_warn(k, "1.56", STABILIZED_CONFIGURABLE_ENV),
1e0d564f 962 "patch-in-config" => stabilized_warn(k, "1.56", STABILIZED_PATCH_IN_CONFIG),
997bf3ff
AH
963 "future-incompat-report" => {
964 stabilized_warn(k, "1.59.0", STABILIZED_FUTURE_INCOMPAT_REPORT)
965 }
c0669189 966 "timings" => stabilized_warn(k, "1.60", STABILIZED_TIMINGS),
50db59e0 967 _ => bail!("unknown `-Z` flag specified: {}", k),
f26fc37d
AC
968 }
969
970 Ok(())
971 }
35b843ef 972
96a56422
EH
973 /// Generates an error if `-Z unstable-options` was not used for a new,
974 /// unstable command-line flag.
35b843ef
EH
975 pub fn fail_if_stable_opt(&self, flag: &str, issue: u32) -> CargoResult<()> {
976 if !self.unstable_options {
977 let see = format!(
978 "See https://github.com/rust-lang/cargo/issues/{} for more \
979 information about the `{}` flag.",
980 issue, flag
981 );
4b096bea
JN
982 // NOTE: a `config` isn't available here, check the channel directly
983 let channel = channel();
984 if channel == "nightly" || channel == "dev" {
50db59e0 985 bail!(
35b843ef
EH
986 "the `{}` flag is unstable, pass `-Z unstable-options` to enable it\n\
987 {}",
988 flag,
989 see
990 );
991 } else {
50db59e0 992 bail!(
35b843ef
EH
993 "the `{}` flag is unstable, and only available on the nightly channel \
994 of Cargo, but this is the `{}` channel\n\
995 {}\n\
996 {}",
997 flag,
4b096bea 998 channel,
35b843ef
EH
999 SEE_CHANNELS,
1000 see
1001 );
1002 }
1003 }
1004 Ok(())
1005 }
96a56422
EH
1006
1007 /// Generates an error if `-Z unstable-options` was not used for a new,
1008 /// unstable subcommand.
1009 pub fn fail_if_stable_command(
1010 &self,
1011 config: &Config,
1012 command: &str,
1013 issue: u32,
1014 ) -> CargoResult<()> {
1015 if self.unstable_options {
1016 return Ok(());
1017 }
1018 let see = format!(
1019 "See https://github.com/rust-lang/cargo/issues/{} for more \
1020 information about the `cargo {}` command.",
1021 issue, command
1022 );
1023 if config.nightly_features_allowed {
1024 bail!(
1025 "the `cargo {}` command is unstable, pass `-Z unstable-options` to enable it\n\
1026 {}",
1027 command,
1028 see
1029 );
1030 } else {
1031 bail!(
1032 "the `cargo {}` command is unstable, and only available on the \
1033 nightly channel of Cargo, but this is the `{}` channel\n\
1034 {}\n\
1035 {}",
1036 command,
1037 channel(),
1038 SEE_CHANNELS,
1039 see
1040 );
1041 }
1042 }
f26fc37d
AC
1043}
1044
35b843ef
EH
1045/// Returns the current release channel ("stable", "beta", "nightly", "dev").
1046pub fn channel() -> String {
1bdb89d9
OS
1047 if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
1048 return override_channel;
1049 }
ac3cac44
OS
1050 if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
1051 if staging == "1" {
1052 return "dev".to_string();
1053 }
1054 }
04ddd4d0 1055 crate::version()
295ea6d9 1056 .release_channel
ac3cac44 1057 .unwrap_or_else(|| String::from("dev"))
3181ef24 1058}