]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/features.rs
Auto merge of #9105 - Swatinem:rustdoc-run-cwd, r=alexcrichton
[cargo.git] / src / cargo / core / features.rs
1 //! Support for nightly features in Cargo itself.
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
9 //! Cargo, and the good news is that it shouldn't be too hard! First determine
10 //! how the feature should be gated:
11 //!
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`
47 //!
48 //! `-Z unstable-options` is intended to force the user to opt-in to new CLI
49 //! flags, options, and new subcommands.
50 //!
51 //! The steps to add a new command-line option are:
52 //!
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.
58 //!
59 //! ## `-Z` options
60 //!
61 //! The steps to add a new `-Z` option are:
62 //!
63 //! 1. Add the option to the [`CliUnstable`] struct below. Flags can take an
64 //! optional value if you want.
65 //! 2. Update the [`CliUnstable::add`] function to parse the flag.
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.
71 //! 4. Update the `-Z help` documentation in the `main` function.
72 //!
73 //! ## Stabilization
74 //!
75 //! For the stabilization process, see
76 //! <https://doc.crates.io/contrib/process/unstable.html#stabilization>.
77 //!
78 //! The steps for stabilizing are roughly:
79 //!
80 //! 1. Update the feature to be stable, based on the kind of feature:
81 //! 1. `cargo-features`: Change the feature to `stable` in the `features!`
82 //! macro below.
83 //! 2. `-Z unstable-options`: Find the call to `fail_if_stable_opt` and
84 //! remove it. Be sure to update the man pages if necessary.
85 //! 3. `-Z` flag: Change the parsing code in [`CliUnstable::add`] to call
86 //! `stabilized_warn` or `stabilized_err`. Remove it from the `-Z help`
87 //! docs in the `main` function. Remove the `(unstable)` note in the
88 //! clap help text if necessary.
89 //! 2. Remove `masquerade_as_nightly_cargo` from any tests, and remove
90 //! `cargo-features` from `Cargo.toml` test files if any.
91 //! 3. Remove the docs from unstable.md and update the redirect at the bottom
92 //! of that page. Update the rest of the documentation to add the new
93 //! feature.
94
95 use std::cell::Cell;
96 use std::env;
97 use std::fmt;
98 use std::str::FromStr;
99
100 use anyhow::{bail, Error};
101 use serde::{Deserialize, Serialize};
102
103 use crate::util::errors::CargoResult;
104 use crate::util::indented_lines;
105
106 pub const SEE_CHANNELS: &str =
107 "See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information \
108 about Rust release channels.";
109
110 /// The edition of the compiler (RFC 2052)
111 #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
112 pub enum Edition {
113 /// The 2015 edition
114 Edition2015,
115 /// The 2018 edition
116 Edition2018,
117 /// The 2021 edition
118 Edition2021,
119 }
120
121 impl Edition {
122 pub(crate) fn first_version(&self) -> Option<semver::Version> {
123 use Edition::*;
124 match self {
125 Edition2015 => None,
126 Edition2018 => Some(semver::Version::new(1, 31, 0)),
127 Edition2021 => Some(semver::Version::new(1, 62, 0)),
128 }
129 }
130 }
131
132 impl fmt::Display for Edition {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 match *self {
135 Edition::Edition2015 => f.write_str("2015"),
136 Edition::Edition2018 => f.write_str("2018"),
137 Edition::Edition2021 => f.write_str("2021"),
138 }
139 }
140 }
141 impl FromStr for Edition {
142 type Err = Error;
143 fn from_str(s: &str) -> Result<Self, Error> {
144 match s {
145 "2015" => Ok(Edition::Edition2015),
146 "2018" => Ok(Edition::Edition2018),
147 "2021" => Ok(Edition::Edition2021),
148 s if s.parse().map_or(false, |y: u16| y > 2021 && y < 2050) => bail!(
149 "this version of Cargo is older than the `{}` edition, \
150 and only supports `2015`, `2018`, and `2021` editions.",
151 s
152 ),
153 s => bail!(
154 "supported edition values are `2015`, `2018`, or `2021`, \
155 but `{}` is unknown",
156 s
157 ),
158 }
159 }
160 }
161
162 #[derive(PartialEq)]
163 enum Status {
164 Stable,
165 Unstable,
166 Removed,
167 }
168
169 macro_rules! features {
170 (
171 $(($stab:ident, $feature:ident, $version:expr, $docs:expr),)*
172 ) => (
173 #[derive(Default, Clone, Debug)]
174 pub struct Features {
175 $($feature: bool,)*
176 activated: Vec<String>,
177 }
178
179 impl Feature {
180 $(
181 pub fn $feature() -> &'static Feature {
182 fn get(features: &Features) -> bool {
183 stab!($stab) == Status::Stable || features.$feature
184 }
185 static FEAT: Feature = Feature {
186 name: stringify!($feature),
187 stability: stab!($stab),
188 version: $version,
189 docs: $docs,
190 get,
191 };
192 &FEAT
193 }
194 )*
195
196 fn is_enabled(&self, features: &Features) -> bool {
197 (self.get)(features)
198 }
199 }
200
201 impl Features {
202 fn status(&mut self, feature: &str) -> Option<(&mut bool, &'static Feature)> {
203 if feature.contains("_") {
204 return None
205 }
206 let feature = feature.replace("-", "_");
207 $(
208 if feature == stringify!($feature) {
209 return Some((&mut self.$feature, Feature::$feature()))
210 }
211 )*
212 None
213 }
214 }
215 )
216 }
217
218 macro_rules! stab {
219 (stable) => {
220 Status::Stable
221 };
222 (unstable) => {
223 Status::Unstable
224 };
225 (removed) => {
226 Status::Removed
227 };
228 }
229
230 // A listing of all features in Cargo.
231 //
232 // "look here"
233 //
234 // This is the macro that lists all stable and unstable features in Cargo.
235 // You'll want to add to this macro whenever you add a feature to Cargo, also
236 // following the directions above.
237 //
238 // Note that all feature names here are valid Rust identifiers, but the `_`
239 // character is translated to `-` when specified in the `cargo-features`
240 // manifest entry in `Cargo.toml`.
241 features! {
242 // A dummy feature that doesn't actually gate anything, but it's used in
243 // testing to ensure that we can enable stable features.
244 (stable, test_dummy_stable, "1.0", ""),
245
246 // A dummy feature that gates the usage of the `im-a-teapot` manifest
247 // entry. This is basically just intended for tests.
248 (unstable, test_dummy_unstable, "", "reference/unstable.html"),
249
250 // Downloading packages from alternative registry indexes.
251 (stable, alternative_registries, "1.34", "reference/registries.html"),
252
253 // Using editions
254 (stable, edition, "1.31", "reference/manifest.html#the-edition-field"),
255
256 // Renaming a package in the manifest via the `package` key
257 (stable, rename_dependency, "1.31", "reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml"),
258
259 // Whether a lock file is published with this crate
260 (removed, publish_lockfile, "", PUBLISH_LOCKFILE_REMOVED),
261
262 // Overriding profiles for dependencies.
263 (stable, profile_overrides, "1.41", "reference/profiles.html#overrides"),
264
265 // "default-run" manifest option,
266 (stable, default_run, "1.37", "reference/manifest.html#the-default-run-field"),
267
268 // Declarative build scripts.
269 (unstable, metabuild, "", "reference/unstable.html#metabuild"),
270
271 // Specifying the 'public' attribute on dependencies
272 (unstable, public_dependency, "", "reference/unstable.html#public-dependency"),
273
274 // Allow to specify profiles other than 'dev', 'release', 'test', etc.
275 (unstable, named_profiles, "", "reference/unstable.html#custom-named-profiles"),
276
277 // Opt-in new-resolver behavior.
278 (stable, resolver, "1.51", "reference/resolver.html#resolver-versions"),
279
280 // Allow to specify whether binaries should be stripped.
281 (unstable, strip, "", "reference/unstable.html#profile-strip-option"),
282
283 // Specifying a minimal 'rust-version' attribute for crates
284 (unstable, rust_version, "", "reference/unstable.html#rust-version"),
285 }
286
287 const PUBLISH_LOCKFILE_REMOVED: &str = "The publish-lockfile key in Cargo.toml \
288 has been removed. The Cargo.lock file is always included when a package is \
289 published if the package contains a binary target. `cargo install` requires \
290 the `--locked` flag to use the Cargo.lock file.\n\
291 See https://doc.rust-lang.org/cargo/commands/cargo-package.html and \
292 https://doc.rust-lang.org/cargo/commands/cargo-install.html for more \
293 information.";
294
295 pub struct Feature {
296 name: &'static str,
297 stability: Status,
298 version: &'static str,
299 docs: &'static str,
300 get: fn(&Features) -> bool,
301 }
302
303 impl Features {
304 pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
305 let mut ret = Features::default();
306 for feature in features {
307 ret.add(feature, warnings)?;
308 ret.activated.push(feature.to_string());
309 }
310 Ok(ret)
311 }
312
313 fn add(&mut self, feature_name: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
314 let (slot, feature) = match self.status(feature_name) {
315 Some(p) => p,
316 None => bail!("unknown cargo feature `{}`", feature_name),
317 };
318
319 if *slot {
320 bail!(
321 "the cargo feature `{}` has already been activated",
322 feature_name
323 );
324 }
325
326 let see_docs = || {
327 let url_channel = match channel().as_str() {
328 "dev" | "nightly" => "nightly/",
329 "beta" => "beta/",
330 _ => "",
331 };
332 format!(
333 "See https://doc.rust-lang.org/{}cargo/{} for more information \
334 about using this feature.",
335 url_channel, feature.docs
336 )
337 };
338
339 match feature.stability {
340 Status::Stable => {
341 let warning = format!(
342 "the cargo feature `{}` has been stabilized in the {} \
343 release and is no longer necessary to be listed in the \
344 manifest\n {}",
345 feature_name,
346 feature.version,
347 see_docs()
348 );
349 warnings.push(warning);
350 }
351 Status::Unstable if !nightly_features_allowed() => bail!(
352 "the cargo feature `{}` requires a nightly version of \
353 Cargo, but this is the `{}` channel\n\
354 {}\n{}",
355 feature_name,
356 channel(),
357 SEE_CHANNELS,
358 see_docs()
359 ),
360 Status::Unstable => {}
361 Status::Removed => bail!(
362 "the cargo feature `{}` has been removed\n\
363 Remove the feature from Cargo.toml to remove this error.\n\
364 {}",
365 feature_name,
366 feature.docs
367 ),
368 }
369
370 *slot = true;
371
372 Ok(())
373 }
374
375 pub fn activated(&self) -> &[String] {
376 &self.activated
377 }
378
379 pub fn require(&self, feature: &Feature) -> CargoResult<()> {
380 if feature.is_enabled(self) {
381 Ok(())
382 } else {
383 let feature = feature.name.replace("_", "-");
384 let mut msg = format!("feature `{}` is required", feature);
385
386 if nightly_features_allowed() {
387 let s = format!(
388 "\n\nconsider adding `cargo-features = [\"{0}\"]` \
389 to the manifest",
390 feature
391 );
392 msg.push_str(&s);
393 } else {
394 let s = format!(
395 "\n\n\
396 this Cargo does not support nightly features, but if you\n\
397 switch to nightly channel you can add\n\
398 `cargo-features = [\"{}\"]` to enable this feature",
399 feature
400 );
401 msg.push_str(&s);
402 }
403 bail!("{}", msg);
404 }
405 }
406
407 pub fn is_enabled(&self, feature: &Feature) -> bool {
408 feature.is_enabled(self)
409 }
410 }
411
412 /// A parsed representation of all unstable flags that Cargo accepts.
413 ///
414 /// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
415 /// gating unstable functionality to Cargo. These flags are only available on
416 /// the nightly channel of Cargo.
417 #[derive(Default, Debug, Deserialize)]
418 #[serde(default, rename_all = "kebab-case")]
419 pub struct CliUnstable {
420 pub print_im_a_teapot: bool,
421 pub unstable_options: bool,
422 pub no_index_update: bool,
423 pub avoid_dev_deps: bool,
424 pub minimal_versions: bool,
425 pub advanced_env: bool,
426 pub config_include: bool,
427 pub dual_proc_macros: bool,
428 pub mtime_on_use: bool,
429 pub named_profiles: bool,
430 pub binary_dep_depinfo: bool,
431 #[serde(deserialize_with = "deserialize_build_std")]
432 pub build_std: Option<Vec<String>>,
433 pub build_std_features: Option<Vec<String>>,
434 pub timings: Option<Vec<String>>,
435 pub doctest_xcompile: bool,
436 pub doctest_in_workspace: bool,
437 pub panic_abort_tests: bool,
438 pub jobserver_per_rustc: bool,
439 pub features: Option<Vec<String>>,
440 pub separate_nightlies: bool,
441 pub multitarget: bool,
442 pub rustdoc_map: bool,
443 pub terminal_width: Option<Option<usize>>,
444 pub namespaced_features: bool,
445 pub weak_dep_features: bool,
446 pub extra_link_arg: bool,
447 pub credential_process: bool,
448 pub configurable_env: bool,
449 }
450
451 const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
452 enabled when used on an interactive console.\n\
453 See https://doc.rust-lang.org/cargo/reference/config.html#termprogresswhen \
454 for information on controlling the progress bar.";
455
456 const STABILIZED_OFFLINE: &str = "Offline mode is now available via the \
457 --offline CLI option";
458
459 const STABILIZED_CACHE_MESSAGES: &str = "Message caching is now always enabled.";
460
461 const STABILIZED_INSTALL_UPGRADE: &str = "Packages are now always upgraded if \
462 they appear out of date.\n\
463 See https://doc.rust-lang.org/cargo/commands/cargo-install.html for more \
464 information on how upgrading works.";
465
466 const STABILIZED_CONFIG_PROFILE: &str = "See \
467 https://doc.rust-lang.org/cargo/reference/config.html#profile for more \
468 information about specifying profiles in config.";
469
470 const STABILIZED_CRATE_VERSIONS: &str = "The crate version is now \
471 automatically added to the documentation.";
472
473 const STABILIZED_PACKAGE_FEATURES: &str = "Enhanced feature flag behavior is now \
474 available in virtual workspaces, and `member/feature-name` syntax is also \
475 always available. Other extensions require setting `resolver = \"2\"` in \
476 Cargo.toml.\n\
477 See https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags \
478 for more information.";
479
480 const STABILIZED_FEATURES: &str = "The new feature resolver is now available \
481 by specifying `resolver = \"2\"` in Cargo.toml.\n\
482 See https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 \
483 for more information.";
484
485 fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
486 where
487 D: serde::Deserializer<'de>,
488 {
489 let crates = match <Option<Vec<String>>>::deserialize(deserializer)? {
490 Some(list) => list,
491 None => return Ok(None),
492 };
493 let v = crates.join(",");
494 Ok(Some(
495 crate::core::compiler::standard_lib::parse_unstable_flag(Some(&v)),
496 ))
497 }
498
499 impl CliUnstable {
500 pub fn parse(&mut self, flags: &[String]) -> CargoResult<Vec<String>> {
501 if !flags.is_empty() && !nightly_features_allowed() {
502 bail!(
503 "the `-Z` flag is only accepted on the nightly channel of Cargo, \
504 but this is the `{}` channel\n\
505 {}",
506 channel(),
507 SEE_CHANNELS
508 );
509 }
510 let mut warnings = Vec::new();
511 for flag in flags {
512 self.add(flag, &mut warnings)?;
513 }
514 Ok(warnings)
515 }
516
517 fn add(&mut self, flag: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
518 let mut parts = flag.splitn(2, '=');
519 let k = parts.next().unwrap();
520 let v = parts.next();
521
522 fn parse_bool(key: &str, value: Option<&str>) -> CargoResult<bool> {
523 match value {
524 None | Some("yes") => Ok(true),
525 Some("no") => Ok(false),
526 Some(s) => bail!("flag -Z{} expected `no` or `yes`, found: `{}`", key, s),
527 }
528 }
529
530 fn parse_timings(value: Option<&str>) -> Vec<String> {
531 match value {
532 None => vec!["html".to_string(), "info".to_string()],
533 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
534 }
535 }
536
537 fn parse_features(value: Option<&str>) -> Vec<String> {
538 match value {
539 None => Vec::new(),
540 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
541 }
542 }
543
544 // Asserts that there is no argument to the flag.
545 fn parse_empty(key: &str, value: Option<&str>) -> CargoResult<bool> {
546 if let Some(v) = value {
547 bail!("flag -Z{} does not take a value, found: `{}`", key, v);
548 }
549 Ok(true)
550 }
551
552 fn parse_usize_opt(value: Option<&str>) -> CargoResult<Option<usize>> {
553 Ok(match value {
554 Some(value) => match value.parse::<usize>() {
555 Ok(value) => Some(value),
556 Err(e) => bail!("expected a number, found: {}", e),
557 },
558 None => None,
559 })
560 }
561
562 let mut stabilized_warn = |key: &str, version: &str, message: &str| {
563 warnings.push(format!(
564 "flag `-Z {}` has been stabilized in the {} release, \
565 and is no longer necessary\n{}",
566 key,
567 version,
568 indented_lines(message)
569 ));
570 };
571
572 // Use this if the behavior now requires another mechanism to enable.
573 let stabilized_err = |key: &str, version: &str, message: &str| {
574 Err(anyhow::format_err!(
575 "flag `-Z {}` has been stabilized in the {} release\n{}",
576 key,
577 version,
578 indented_lines(message)
579 ))
580 };
581
582 match k {
583 "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
584 "unstable-options" => self.unstable_options = parse_empty(k, v)?,
585 "no-index-update" => self.no_index_update = parse_empty(k, v)?,
586 "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
587 "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
588 "advanced-env" => self.advanced_env = parse_empty(k, v)?,
589 "config-include" => self.config_include = parse_empty(k, v)?,
590 "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
591 // can also be set in .cargo/config or with and ENV
592 "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
593 "named-profiles" => self.named_profiles = parse_empty(k, v)?,
594 "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
595 "build-std" => {
596 self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
597 }
598 "build-std-features" => self.build_std_features = Some(parse_features(v)),
599 "timings" => self.timings = Some(parse_timings(v)),
600 "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
601 "doctest-in-workspace" => self.doctest_in_workspace = parse_empty(k, v)?,
602 "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
603 "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
604 "configurable-env" => self.configurable_env = parse_empty(k, v)?,
605 "features" => {
606 // For now this is still allowed (there are still some
607 // unstable options like "compare"). This should be removed at
608 // some point, and migrate to a new -Z flag for any future
609 // things.
610 let feats = parse_features(v);
611 let stab: Vec<_> = feats
612 .iter()
613 .filter(|feat| {
614 matches!(
615 feat.as_str(),
616 "build_dep" | "host_dep" | "dev_dep" | "itarget" | "all"
617 )
618 })
619 .collect();
620 if !stab.is_empty() || feats.is_empty() {
621 // Make this stabilized_err once -Zfeature support is removed.
622 stabilized_warn(k, "1.51", STABILIZED_FEATURES);
623 }
624 self.features = Some(feats);
625 }
626 "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
627 "multitarget" => self.multitarget = parse_empty(k, v)?,
628 "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
629 "terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
630 "namespaced-features" => self.namespaced_features = parse_empty(k, v)?,
631 "weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?,
632 "extra-link-arg" => self.extra_link_arg = parse_empty(k, v)?,
633 "credential-process" => self.credential_process = parse_empty(k, v)?,
634 "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS),
635 "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?,
636 "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES),
637 "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE),
638 "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE),
639 "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS),
640 "package-features" => stabilized_warn(k, "1.51", STABILIZED_PACKAGE_FEATURES),
641 _ => bail!("unknown `-Z` flag specified: {}", k),
642 }
643
644 Ok(())
645 }
646
647 /// Generates an error if `-Z unstable-options` was not used.
648 /// Intended to be used when a user passes a command-line flag that
649 /// requires `-Z unstable-options`.
650 pub fn fail_if_stable_opt(&self, flag: &str, issue: u32) -> CargoResult<()> {
651 if !self.unstable_options {
652 let see = format!(
653 "See https://github.com/rust-lang/cargo/issues/{} for more \
654 information about the `{}` flag.",
655 issue, flag
656 );
657 if nightly_features_allowed() {
658 bail!(
659 "the `{}` flag is unstable, pass `-Z unstable-options` to enable it\n\
660 {}",
661 flag,
662 see
663 );
664 } else {
665 bail!(
666 "the `{}` flag is unstable, and only available on the nightly channel \
667 of Cargo, but this is the `{}` channel\n\
668 {}\n\
669 {}",
670 flag,
671 channel(),
672 SEE_CHANNELS,
673 see
674 );
675 }
676 }
677 Ok(())
678 }
679 }
680
681 /// Returns the current release channel ("stable", "beta", "nightly", "dev").
682 pub fn channel() -> String {
683 if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
684 return override_channel;
685 }
686 if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
687 if staging == "1" {
688 return "dev".to_string();
689 }
690 }
691 crate::version()
692 .cfg_info
693 .map(|c| c.release_channel)
694 .unwrap_or_else(|| String::from("dev"))
695 }
696
697 thread_local!(
698 static NIGHTLY_FEATURES_ALLOWED: Cell<bool> = Cell::new(false);
699 static ENABLE_NIGHTLY_FEATURES: Cell<bool> = Cell::new(false);
700 );
701
702 /// This is a little complicated.
703 /// This should return false if:
704 /// - this is an artifact of the rustc distribution process for "stable" or for "beta"
705 /// - this is an `#[test]` that does not opt in with `enable_nightly_features`
706 /// - this is a integration test that uses `ProcessBuilder`
707 /// that does not opt in with `masquerade_as_nightly_cargo`
708 /// This should return true if:
709 /// - this is an artifact of the rustc distribution process for "nightly"
710 /// - this is being used in the rustc distribution process internally
711 /// - this is a cargo executable that was built from source
712 /// - this is an `#[test]` that called `enable_nightly_features`
713 /// - this is a integration test that uses `ProcessBuilder`
714 /// that called `masquerade_as_nightly_cargo`
715 pub fn nightly_features_allowed() -> bool {
716 if ENABLE_NIGHTLY_FEATURES.with(|c| c.get()) {
717 return true;
718 }
719 match &channel()[..] {
720 "nightly" | "dev" => NIGHTLY_FEATURES_ALLOWED.with(|c| c.get()),
721 _ => false,
722 }
723 }
724
725 /// Allows nightly features to be enabled for this thread, but only if the
726 /// development channel is nightly or dev.
727 ///
728 /// Used by cargo main to ensure that a cargo build from source has nightly features
729 pub fn maybe_allow_nightly_features() {
730 NIGHTLY_FEATURES_ALLOWED.with(|c| c.set(true));
731 }
732
733 /// Forcibly enables nightly features for this thread.
734 ///
735 /// Used by tests to allow the use of nightly features.
736 pub fn enable_nightly_features() {
737 ENABLE_NIGHTLY_FEATURES.with(|c| c.set(true));
738 }