]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/features.rs
Auto merge of #7857 - ehuss:fix-build-script-dupe, r=alexcrichton
[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
9//! Cargo, and the good news is that it shouldn't be too hard! To do this you'll
10//! want to follow these steps:
11//!
12//! 1. Add your feature. Do this by searching for "look here" in this file and
13//! expanding the macro invocation that lists all features with your new
14//! feature.
15//!
16//! 2. Find the appropriate place to place the feature gate in Cargo itself. If
17//! you're extending the manifest format you'll likely just want to modify
18//! the `Manifest::feature_gate` function, but otherwise you may wish to
19//! place the feature gate elsewhere in Cargo.
20//!
71ef41a9 21//! 3. To actually perform the feature gate, you'll want to have code that looks
3181ef24
AC
22//! like:
23//!
ca176eed 24//! ```rust,compile_fail
3181ef24
AC
25//! use core::{Feature, Features};
26//!
27//! let feature = Feature::launch_into_space();
28//! package.manifest().features().require(feature).chain_err(|| {
29//! "launching Cargo into space right now is unstable and may result in \
30//! unintended damage to your codebase, use with caution"
31//! })?;
32//! ```
33//!
d89cd903 34//! Notably you'll notice the `require` function called with your `Feature`, and
3181ef24
AC
35//! then you use `chain_err` to tack on more context for why the feature was
36//! required when the feature isn't activated.
37//!
740525f2
EH
38//! 4. Update the unstable documentation at
39//! `src/doc/src/reference/unstable.md` to include a short description of
f7c91ba6 40//! how to use your new feature. When the feature is stabilized, be sure
740525f2
EH
41//! that the Cargo Guide or Reference is updated to fully document the
42//! feature and remove the entry from the Unstable section.
43//!
3181ef24
AC
44//! And hopefully that's it! Bear with us though that this is, at the time of
45//! this writing, a very new feature in Cargo. If the process differs from this
46//! we'll be sure to update this documentation!
47
75bb1906 48use std::cell::Cell;
3181ef24 49use std::env;
2d1af7b5 50use std::fmt;
1d82d2b3 51use std::str::FromStr;
3181ef24 52
3a18c89a 53use anyhow::{bail, Error};
9ed82b57 54use serde::{Deserialize, Serialize};
84130089 55
04ddd4d0 56use crate::util::errors::CargoResult;
3181ef24 57
35b843ef
EH
58pub const SEE_CHANNELS: &str =
59 "See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information \
60 about Rust release channels.";
61
3bbe93ce 62/// The edition of the compiler (RFC 2052)
1e682848 63#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
3bbe93ce
KN
64pub enum Edition {
65 /// The 2015 edition
66 Edition2015,
67 /// The 2018 edition
68 Edition2018,
1d82d2b3
MG
69}
70
3bbe93ce 71impl fmt::Display for Edition {
b8b7faee 72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2d1af7b5 73 match *self {
3bbe93ce
KN
74 Edition::Edition2015 => f.write_str("2015"),
75 Edition::Edition2018 => f.write_str("2018"),
2d1af7b5
MG
76 }
77 }
78}
3bbe93ce 79impl FromStr for Edition {
84130089
AC
80 type Err = Error;
81 fn from_str(s: &str) -> Result<Self, Error> {
1d82d2b3 82 match s {
3bbe93ce
KN
83 "2015" => Ok(Edition::Edition2015),
84 "2018" => Ok(Edition::Edition2018),
50db59e0 85 s => bail!(
fecb7246
AC
86 "supported edition values are `2015` or `2018`, but `{}` \
87 is unknown",
88 s
89 ),
1d82d2b3
MG
90 }
91 }
92}
93
3d029039 94#[derive(PartialEq)]
3181ef24
AC
95enum Status {
96 Stable,
97 Unstable,
98}
99
100macro_rules! features {
101 (
102 pub struct Features {
103 $([$stab:ident] $feature:ident: bool,)*
104 }
105 ) => (
106 #[derive(Default, Clone, Debug)]
107 pub struct Features {
108 $($feature: bool,)*
109 activated: Vec<String>,
110 }
111
112 impl Feature {
113 $(
114 pub fn $feature() -> &'static Feature {
f26fc37d 115 fn get(features: &Features) -> bool {
3d029039 116 stab!($stab) == Status::Stable || features.$feature
3181ef24
AC
117 }
118 static FEAT: Feature = Feature {
119 name: stringify!($feature),
676edacf 120 get,
3181ef24
AC
121 };
122 &FEAT
123 }
124 )*
f26fc37d
AC
125
126 fn is_enabled(&self, features: &Features) -> bool {
127 (self.get)(features)
128 }
3181ef24
AC
129 }
130
131 impl Features {
132 fn status(&mut self, feature: &str) -> Option<(&mut bool, Status)> {
133 if feature.contains("_") {
134 return None
135 }
136 let feature = feature.replace("-", "_");
137 $(
138 if feature == stringify!($feature) {
139 return Some((&mut self.$feature, stab!($stab)))
140 }
141 )*
142 None
143 }
144 }
145 )
146}
147
148macro_rules! stab {
a4947c2b
EH
149 (stable) => {
150 Status::Stable
151 };
152 (unstable) => {
153 Status::Unstable
154 };
3181ef24
AC
155}
156
f5723e51
AR
157// A listing of all features in Cargo.
158//
159// "look here"
160//
161// This is the macro that lists all stable and unstable features in Cargo.
162// You'll want to add to this macro whenever you add a feature to Cargo, also
163// following the directions above.
164//
165// Note that all feature names here are valid Rust identifiers, but the `_`
166// character is translated to `-` when specified in the `cargo-features`
167// manifest entry in `Cargo.toml`.
3181ef24
AC
168features! {
169 pub struct Features {
170
171 // A dummy feature that doesn't actually gate anything, but it's used in
172 // testing to ensure that we can enable stable features.
173 [stable] test_dummy_stable: bool,
174
175 // A dummy feature that gates the usage of the `im-a-teapot` manifest
176 // entry. This is basically just intended for tests.
177 [unstable] test_dummy_unstable: bool,
d89cd903
WB
178
179 // Downloading packages from alternative registry indexes.
737382d7 180 [stable] alternative_registries: bool,
fa5be237 181
3bbe93ce 182 // Using editions
3d029039 183 [stable] edition: bool,
79942fea
AC
184
185 // Renaming a package in the manifest via the `package` key
1533a049 186 [stable] rename_dependency: bool,
a4a3302d
AC
187
188 // Whether a lock file is published with this crate
34307c61 189 // This is deprecated, and will likely be removed in a future version.
a4a3302d 190 [unstable] publish_lockfile: bool,
575d6e81
EH
191
192 // Overriding profiles for dependencies.
dda81d31 193 [stable] profile_overrides: bool,
0b6f4206
DO
194
195 // Separating the namespaces for features and dependencies
196 [unstable] namespaced_features: bool,
c955c60e
RJ
197
198 // "default-run" manifest option,
7d7fe679 199 [stable] default_run: bool,
2be857af
EH
200
201 // Declarative build scripts.
202 [unstable] metabuild: bool,
87f1a4b2
AH
203
204 // Specifying the 'public' attribute on dependencies
205 [unstable] public_dependency: bool,
756ab7d5
DA
206
207 // Allow to specify profiles other than 'dev', 'release', 'test', etc.
208 [unstable] named_profiles: bool,
3181ef24
AC
209 }
210}
211
212pub struct Feature {
213 name: &'static str,
f26fc37d 214 get: fn(&Features) -> bool,
3181ef24
AC
215}
216
217impl Features {
1e682848 218 pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
3181ef24
AC
219 let mut ret = Features::default();
220 for feature in features {
221 ret.add(feature, warnings)?;
222 ret.activated.push(feature.to_string());
223 }
224 Ok(ret)
225 }
226
227 fn add(&mut self, feature: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
228 let (slot, status) = match self.status(feature) {
229 Some(p) => p,
50db59e0 230 None => bail!("unknown cargo feature `{}`", feature),
3181ef24
AC
231 };
232
233 if *slot {
50db59e0 234 bail!("the cargo feature `{}` has already been activated", feature);
3181ef24
AC
235 }
236
237 match status {
238 Status::Stable => {
1e682848
AC
239 let warning = format!(
240 "the cargo feature `{}` is now stable \
241 and is no longer necessary to be listed \
242 in the manifest",
243 feature
244 );
3181ef24
AC
245 warnings.push(warning);
246 }
50db59e0 247 Status::Unstable if !nightly_features_allowed() => bail!(
1e682848 248 "the cargo feature `{}` requires a nightly version of \
35b843ef
EH
249 Cargo, but this is the `{}` channel\n\
250 {}",
1e682848 251 feature,
35b843ef
EH
252 channel(),
253 SEE_CHANNELS
1e682848 254 ),
3181ef24
AC
255 Status::Unstable => {}
256 }
257
258 *slot = true;
259
260 Ok(())
261 }
262
263 pub fn activated(&self) -> &[String] {
264 &self.activated
265 }
266
267 pub fn require(&self, feature: &Feature) -> CargoResult<()> {
f26fc37d 268 if feature.is_enabled(self) {
3181ef24
AC
269 Ok(())
270 } else {
271 let feature = feature.name.replace("_", "-");
272 let mut msg = format!("feature `{}` is required", feature);
273
274 if nightly_features_allowed() {
1e682848
AC
275 let s = format!(
276 "\n\nconsider adding `cargo-features = [\"{0}\"]` \
277 to the manifest",
278 feature
279 );
3181ef24
AC
280 msg.push_str(&s);
281 } else {
1e682848
AC
282 let s = format!(
283 "\n\n\
284 this Cargo does not support nightly features, but if you\n\
285 switch to nightly channel you can add\n\
286 `cargo-features = [\"{}\"]` to enable this feature",
287 feature
288 );
3181ef24
AC
289 msg.push_str(&s);
290 }
50db59e0 291 bail!("{}", msg);
3181ef24
AC
292 }
293 }
2d1af7b5
MG
294
295 pub fn is_enabled(&self, feature: &Feature) -> bool {
296 feature.is_enabled(self)
297 }
3181ef24
AC
298}
299
71ef41a9 300/// A parsed representation of all unstable flags that Cargo accepts.
f26fc37d
AC
301///
302/// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
303/// gating unstable functionality to Cargo. These flags are only available on
304/// the nightly channel of Cargo.
305///
306/// This struct doesn't have quite the same convenience macro that the features
307/// have above, but the procedure should still be relatively stable for adding a
308/// new unstable flag:
309///
310/// 1. First, add a field to this `CliUnstable` structure. All flags are allowed
311/// to have a value as the `-Z` flags are either of the form `-Z foo` or
312/// `-Z foo=bar`, and it's up to you how to parse `bar`.
313///
314/// 2. Add an arm to the match statement in `CliUnstable::add` below to match on
315/// your new flag. The key (`k`) is what you're matching on and the value is
316/// in `v`.
317///
318/// 3. (optional) Add a new parsing function to parse your datatype. As of now
319/// there's an example for `bool`, but more can be added!
320///
321/// 4. In Cargo use `config.cli_unstable()` to get a reference to this structure
322/// and then test for your flag or your value and act accordingly.
323///
324/// If you have any trouble with this, please let us know!
325#[derive(Default, Debug)]
326pub struct CliUnstable {
327 pub print_im_a_teapot: bool,
0f82507e 328 pub unstable_options: bool,
b83ef97e 329 pub no_index_update: bool,
df5f7d68 330 pub avoid_dev_deps: bool,
9a098922 331 pub minimal_versions: bool,
d369f97c 332 pub package_features: bool,
154c787c 333 pub advanced_env: bool,
e7eda2f9 334 pub config_include: bool,
6814c203 335 pub dual_proc_macros: bool,
5f6ede29 336 pub mtime_on_use: bool,
756ab7d5 337 pub named_profiles: bool,
c6e626b3 338 pub binary_dep_depinfo: bool,
1f14fa31 339 pub build_std: Option<Vec<String>>,
06644845 340 pub timings: Option<Vec<String>>,
a5235b7b 341 pub doctest_xcompile: bool,
f37f3aea 342 pub panic_abort_tests: bool,
494b3c0a 343 pub jobserver_per_rustc: bool,
f26fc37d
AC
344}
345
346impl CliUnstable {
347 pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
23591fe5 348 if !flags.is_empty() && !nightly_features_allowed() {
50db59e0 349 bail!(
35b843ef
EH
350 "the `-Z` flag is only accepted on the nightly channel of Cargo, \
351 but this is the `{}` channel\n\
352 {}",
353 channel(),
354 SEE_CHANNELS
355 );
f26fc37d
AC
356 }
357 for flag in flags {
358 self.add(flag)?;
359 }
360 Ok(())
361 }
362
363 fn add(&mut self, flag: &str) -> CargoResult<()> {
364 let mut parts = flag.splitn(2, '=');
365 let k = parts.next().unwrap();
366 let v = parts.next();
367
50db59e0 368 fn parse_bool(key: &str, value: Option<&str>) -> CargoResult<bool> {
f26fc37d 369 match value {
1e682848 370 None | Some("yes") => Ok(true),
f26fc37d 371 Some("no") => Ok(false),
50db59e0 372 Some(s) => bail!("flag -Z{} expected `no` or `yes`, found: `{}`", key, s),
f26fc37d
AC
373 }
374 }
375
06644845
EH
376 fn parse_timings(value: Option<&str>) -> Vec<String> {
377 match value {
378 None => vec!["html".to_string(), "info".to_string()],
379 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
380 }
381 }
382
50db59e0
EH
383 // Asserts that there is no argument to the flag.
384 fn parse_empty(key: &str, value: Option<&str>) -> CargoResult<bool> {
385 if let Some(v) = value {
386 bail!("flag -Z{} does not take a value, found: `{}`", key, v);
387 }
388 Ok(true)
389 };
390
f26fc37d 391 match k {
50db59e0
EH
392 "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
393 "unstable-options" => self.unstable_options = parse_empty(k, v)?,
394 "no-index-update" => self.no_index_update = parse_empty(k, v)?,
395 "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
396 "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
397 "package-features" => self.package_features = parse_empty(k, v)?,
398 "advanced-env" => self.advanced_env = parse_empty(k, v)?,
e7eda2f9 399 "config-include" => self.config_include = parse_empty(k, v)?,
50db59e0 400 "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
30df7317 401 // can also be set in .cargo/config or with and ENV
50db59e0 402 "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
50db59e0
EH
403 "named-profiles" => self.named_profiles = parse_empty(k, v)?,
404 "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
1f14fa31
EH
405 "build-std" => {
406 self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
407 }
06644845 408 "timings" => self.timings = Some(parse_timings(v)),
50db59e0
EH
409 "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
410 "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
494b3c0a 411 "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
50db59e0 412 _ => bail!("unknown `-Z` flag specified: {}", k),
f26fc37d
AC
413 }
414
415 Ok(())
416 }
35b843ef
EH
417
418 /// Generates an error if `-Z unstable-options` was not used.
419 /// Intended to be used when a user passes a command-line flag that
420 /// requires `-Z unstable-options`.
421 pub fn fail_if_stable_opt(&self, flag: &str, issue: u32) -> CargoResult<()> {
422 if !self.unstable_options {
423 let see = format!(
424 "See https://github.com/rust-lang/cargo/issues/{} for more \
425 information about the `{}` flag.",
426 issue, flag
427 );
428 if nightly_features_allowed() {
50db59e0 429 bail!(
35b843ef
EH
430 "the `{}` flag is unstable, pass `-Z unstable-options` to enable it\n\
431 {}",
432 flag,
433 see
434 );
435 } else {
50db59e0 436 bail!(
35b843ef
EH
437 "the `{}` flag is unstable, and only available on the nightly channel \
438 of Cargo, but this is the `{}` channel\n\
439 {}\n\
440 {}",
441 flag,
442 channel(),
443 SEE_CHANNELS,
444 see
445 );
446 }
447 }
448 Ok(())
449 }
f26fc37d
AC
450}
451
35b843ef
EH
452/// Returns the current release channel ("stable", "beta", "nightly", "dev").
453pub fn channel() -> String {
1bdb89d9
OS
454 if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
455 return override_channel;
456 }
ac3cac44
OS
457 if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
458 if staging == "1" {
459 return "dev".to_string();
460 }
461 }
04ddd4d0 462 crate::version()
ac3cac44
OS
463 .cfg_info
464 .map(|c| c.release_channel)
465 .unwrap_or_else(|| String::from("dev"))
3181ef24
AC
466}
467
75bb1906
E
468thread_local!(
469 static NIGHTLY_FEATURES_ALLOWED: Cell<bool> = Cell::new(false);
470 static ENABLE_NIGHTLY_FEATURES: Cell<bool> = Cell::new(false);
471);
472
473/// This is a little complicated.
474/// This should return false if:
475/// - this is an artifact of the rustc distribution process for "stable" or for "beta"
476/// - this is an `#[test]` that does not opt in with `enable_nightly_features`
477/// - this is a integration test that uses `ProcessBuilder`
478/// that does not opt in with `masquerade_as_nightly_cargo`
479/// This should return true if:
480/// - this is an artifact of the rustc distribution process for "nightly"
481/// - this is being used in the rustc distribution process internally
482/// - this is a cargo executable that was built from source
483/// - this is an `#[test]` that called `enable_nightly_features`
484/// - this is a integration test that uses `ProcessBuilder`
485/// that called `masquerade_as_nightly_cargo`
d1372315 486pub fn nightly_features_allowed() -> bool {
75bb1906 487 if ENABLE_NIGHTLY_FEATURES.with(|c| c.get()) {
fecb7246 488 return true;
75bb1906 489 }
fecb7246 490 match &channel()[..] {
75bb1906 491 "nightly" | "dev" => NIGHTLY_FEATURES_ALLOWED.with(|c| c.get()),
3181ef24
AC
492 _ => false,
493 }
494}
75bb1906
E
495
496/// Allows nightly features to be enabled for this thread, but only if the
497/// development channel is nightly or dev.
498///
499/// Used by cargo main to ensure that a cargo build from source has nightly features
500pub fn maybe_allow_nightly_features() {
501 NIGHTLY_FEATURES_ALLOWED.with(|c| c.set(true));
502}
503
504/// Forcibly enables nightly features for this thread.
505///
506/// Used by tests to allow the use of nightly features.
507pub fn enable_nightly_features() {
508 ENABLE_NIGHTLY_FEATURES.with(|c| c.set(true));
509}