]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/features.rs
Stabilize `edition` key and add `cargo new --edition`
[cargo.git] / src / cargo / core / features.rs
CommitLineData
3181ef24
AC
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! 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//!
24//! ```rust,ignore
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
40//! how to use your new feature. When the feature is stabilized, be sure
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
84130089
AC
53use failure::Error;
54
3181ef24
AC
55use util::errors::CargoResult;
56
3bbe93ce 57/// The edition of the compiler (RFC 2052)
1e682848 58#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
3bbe93ce
KN
59pub enum Edition {
60 /// The 2015 edition
61 Edition2015,
62 /// The 2018 edition
63 Edition2018,
1d82d2b3
MG
64}
65
3bbe93ce 66impl fmt::Display for Edition {
2d1af7b5
MG
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match *self {
3bbe93ce
KN
69 Edition::Edition2015 => f.write_str("2015"),
70 Edition::Edition2018 => f.write_str("2018"),
2d1af7b5
MG
71 }
72 }
73}
3bbe93ce 74impl FromStr for Edition {
84130089
AC
75 type Err = Error;
76 fn from_str(s: &str) -> Result<Self, Error> {
1d82d2b3 77 match s {
3bbe93ce
KN
78 "2015" => Ok(Edition::Edition2015),
79 "2018" => Ok(Edition::Edition2018),
84130089
AC
80 s => {
81 bail!("supported edition values are `2015` or `2018`, but `{}` \
82 is unknown", s)
83 }
1d82d2b3
MG
84 }
85 }
86}
87
3d029039 88#[derive(PartialEq)]
3181ef24
AC
89enum Status {
90 Stable,
91 Unstable,
92}
93
94macro_rules! features {
95 (
96 pub struct Features {
97 $([$stab:ident] $feature:ident: bool,)*
98 }
99 ) => (
100 #[derive(Default, Clone, Debug)]
101 pub struct Features {
102 $($feature: bool,)*
103 activated: Vec<String>,
104 }
105
106 impl Feature {
107 $(
108 pub fn $feature() -> &'static Feature {
f26fc37d 109 fn get(features: &Features) -> bool {
3d029039 110 stab!($stab) == Status::Stable || features.$feature
3181ef24
AC
111 }
112 static FEAT: Feature = Feature {
113 name: stringify!($feature),
676edacf 114 get,
3181ef24
AC
115 };
116 &FEAT
117 }
118 )*
f26fc37d
AC
119
120 fn is_enabled(&self, features: &Features) -> bool {
121 (self.get)(features)
122 }
3181ef24
AC
123 }
124
125 impl Features {
126 fn status(&mut self, feature: &str) -> Option<(&mut bool, Status)> {
127 if feature.contains("_") {
128 return None
129 }
130 let feature = feature.replace("-", "_");
131 $(
132 if feature == stringify!($feature) {
133 return Some((&mut self.$feature, stab!($stab)))
134 }
135 )*
136 None
137 }
138 }
139 )
140}
141
142macro_rules! stab {
a4947c2b
EH
143 (stable) => {
144 Status::Stable
145 };
146 (unstable) => {
147 Status::Unstable
148 };
3181ef24
AC
149}
150
151/// A listing of all features in Cargo
152///
153/// "look here"
154///
155/// This is the macro that lists all stable and unstable features in Cargo.
156/// You'll want to add to this macro whenever you add a feature to Cargo, also
157/// following the directions above.
158///
159/// Note that all feature names here are valid Rust identifiers, but the `_`
160/// character is translated to `-` when specified in the `cargo-features`
161/// manifest entry in `Cargo.toml`.
162features! {
163 pub struct Features {
164
165 // A dummy feature that doesn't actually gate anything, but it's used in
166 // testing to ensure that we can enable stable features.
167 [stable] test_dummy_stable: bool,
168
169 // A dummy feature that gates the usage of the `im-a-teapot` manifest
170 // entry. This is basically just intended for tests.
171 [unstable] test_dummy_unstable: bool,
d89cd903
WB
172
173 // Downloading packages from alternative registry indexes.
174 [unstable] alternative_registries: bool,
fa5be237 175
3bbe93ce 176 // Using editions
3d029039 177 [stable] edition: bool,
79942fea
AC
178
179 // Renaming a package in the manifest via the `package` key
180 [unstable] rename_dependency: bool,
a4a3302d
AC
181
182 // Whether a lock file is published with this crate
183 [unstable] publish_lockfile: bool,
575d6e81
EH
184
185 // Overriding profiles for dependencies.
186 [unstable] profile_overrides: bool,
0b6f4206
DO
187
188 // Separating the namespaces for features and dependencies
189 [unstable] namespaced_features: bool,
c955c60e
RJ
190
191 // "default-run" manifest option,
192 [unstable] default_run: bool,
2be857af
EH
193
194 // Declarative build scripts.
195 [unstable] metabuild: bool,
3181ef24
AC
196 }
197}
198
199pub struct Feature {
200 name: &'static str,
f26fc37d 201 get: fn(&Features) -> bool,
3181ef24
AC
202}
203
204impl Features {
1e682848 205 pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
3181ef24
AC
206 let mut ret = Features::default();
207 for feature in features {
208 ret.add(feature, warnings)?;
209 ret.activated.push(feature.to_string());
210 }
211 Ok(ret)
212 }
213
214 fn add(&mut self, feature: &str, warnings: &mut Vec<String>) -> CargoResult<()> {
215 let (slot, status) = match self.status(feature) {
216 Some(p) => p,
217 None => bail!("unknown cargo feature `{}`", feature),
218 };
219
220 if *slot {
71ef41a9 221 bail!("the cargo feature `{}` has already been activated", feature);
3181ef24
AC
222 }
223
224 match status {
225 Status::Stable => {
1e682848
AC
226 let warning = format!(
227 "the cargo feature `{}` is now stable \
228 and is no longer necessary to be listed \
229 in the manifest",
230 feature
231 );
3181ef24
AC
232 warnings.push(warning);
233 }
1e682848
AC
234 Status::Unstable if !nightly_features_allowed() => bail!(
235 "the cargo feature `{}` requires a nightly version of \
236 Cargo, but this is the `{}` channel",
237 feature,
238 channel()
239 ),
3181ef24
AC
240 Status::Unstable => {}
241 }
242
243 *slot = true;
244
245 Ok(())
246 }
247
248 pub fn activated(&self) -> &[String] {
249 &self.activated
250 }
251
252 pub fn require(&self, feature: &Feature) -> CargoResult<()> {
f26fc37d 253 if feature.is_enabled(self) {
3181ef24
AC
254 Ok(())
255 } else {
256 let feature = feature.name.replace("_", "-");
257 let mut msg = format!("feature `{}` is required", feature);
258
259 if nightly_features_allowed() {
1e682848
AC
260 let s = format!(
261 "\n\nconsider adding `cargo-features = [\"{0}\"]` \
262 to the manifest",
263 feature
264 );
3181ef24
AC
265 msg.push_str(&s);
266 } else {
1e682848
AC
267 let s = format!(
268 "\n\n\
269 this Cargo does not support nightly features, but if you\n\
270 switch to nightly channel you can add\n\
271 `cargo-features = [\"{}\"]` to enable this feature",
272 feature
273 );
3181ef24
AC
274 msg.push_str(&s);
275 }
276 bail!("{}", msg);
277 }
278 }
2d1af7b5
MG
279
280 pub fn is_enabled(&self, feature: &Feature) -> bool {
281 feature.is_enabled(self)
282 }
3181ef24
AC
283}
284
71ef41a9 285/// A parsed representation of all unstable flags that Cargo accepts.
f26fc37d
AC
286///
287/// Cargo, like `rustc`, accepts a suite of `-Z` flags which are intended for
288/// gating unstable functionality to Cargo. These flags are only available on
289/// the nightly channel of Cargo.
290///
291/// This struct doesn't have quite the same convenience macro that the features
292/// have above, but the procedure should still be relatively stable for adding a
293/// new unstable flag:
294///
295/// 1. First, add a field to this `CliUnstable` structure. All flags are allowed
296/// to have a value as the `-Z` flags are either of the form `-Z foo` or
297/// `-Z foo=bar`, and it's up to you how to parse `bar`.
298///
299/// 2. Add an arm to the match statement in `CliUnstable::add` below to match on
300/// your new flag. The key (`k`) is what you're matching on and the value is
301/// in `v`.
302///
303/// 3. (optional) Add a new parsing function to parse your datatype. As of now
304/// there's an example for `bool`, but more can be added!
305///
306/// 4. In Cargo use `config.cli_unstable()` to get a reference to this structure
307/// and then test for your flag or your value and act accordingly.
308///
309/// If you have any trouble with this, please let us know!
310#[derive(Default, Debug)]
311pub struct CliUnstable {
312 pub print_im_a_teapot: bool,
0f82507e 313 pub unstable_options: bool,
ec5f78f9 314 pub offline: bool,
b83ef97e 315 pub no_index_update: bool,
df5f7d68 316 pub avoid_dev_deps: bool,
9a098922 317 pub minimal_versions: bool,
d369f97c 318 pub package_features: bool,
154c787c 319 pub advanced_env: bool,
2f7b5225 320 pub config_profile: bool,
6ce90874 321 pub compile_progress: bool,
f26fc37d
AC
322}
323
324impl CliUnstable {
325 pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
23591fe5 326 if !flags.is_empty() && !nightly_features_allowed() {
f26fc37d
AC
327 bail!("the `-Z` flag is only accepted on the nightly channel of Cargo")
328 }
329 for flag in flags {
330 self.add(flag)?;
331 }
332 Ok(())
333 }
334
335 fn add(&mut self, flag: &str) -> CargoResult<()> {
336 let mut parts = flag.splitn(2, '=');
337 let k = parts.next().unwrap();
338 let v = parts.next();
339
340 fn parse_bool(value: Option<&str>) -> CargoResult<bool> {
341 match value {
1e682848 342 None | Some("yes") => Ok(true),
f26fc37d
AC
343 Some("no") => Ok(false),
344 Some(s) => bail!("expected `no` or `yes`, found: {}", s),
345 }
346 }
347
348 match k {
349 "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(v)?,
0f82507e 350 "unstable-options" => self.unstable_options = true,
ec5f78f9 351 "offline" => self.offline = true,
b83ef97e 352 "no-index-update" => self.no_index_update = true,
df5f7d68 353 "avoid-dev-deps" => self.avoid_dev_deps = true,
9a098922 354 "minimal-versions" => self.minimal_versions = true,
d369f97c 355 "package-features" => self.package_features = true,
154c787c 356 "advanced-env" => self.advanced_env = true,
2f7b5225 357 "config-profile" => self.config_profile = true,
6ce90874 358 "compile-progress" => self.compile_progress = true,
f26fc37d
AC
359 _ => bail!("unknown `-Z` flag specified: {}", k),
360 }
361
362 Ok(())
363 }
364}
365
3181ef24 366fn channel() -> String {
1bdb89d9
OS
367 if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
368 return override_channel;
369 }
ac3cac44
OS
370 if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
371 if staging == "1" {
372 return "dev".to_string();
373 }
374 }
ac3cac44
OS
375 ::version()
376 .cfg_info
377 .map(|c| c.release_channel)
378 .unwrap_or_else(|| String::from("dev"))
3181ef24
AC
379}
380
75bb1906
E
381thread_local!(
382 static NIGHTLY_FEATURES_ALLOWED: Cell<bool> = Cell::new(false);
383 static ENABLE_NIGHTLY_FEATURES: Cell<bool> = Cell::new(false);
384);
385
386/// This is a little complicated.
387/// This should return false if:
388/// - this is an artifact of the rustc distribution process for "stable" or for "beta"
389/// - this is an `#[test]` that does not opt in with `enable_nightly_features`
390/// - this is a integration test that uses `ProcessBuilder`
391/// that does not opt in with `masquerade_as_nightly_cargo`
392/// This should return true if:
393/// - this is an artifact of the rustc distribution process for "nightly"
394/// - this is being used in the rustc distribution process internally
395/// - this is a cargo executable that was built from source
396/// - this is an `#[test]` that called `enable_nightly_features`
397/// - this is a integration test that uses `ProcessBuilder`
398/// that called `masquerade_as_nightly_cargo`
d1372315 399pub fn nightly_features_allowed() -> bool {
75bb1906
E
400 if ENABLE_NIGHTLY_FEATURES.with(|c| c.get()) {
401 return true
402 }
403 match &channel()[..] {
404 "nightly" | "dev" => NIGHTLY_FEATURES_ALLOWED.with(|c| c.get()),
3181ef24
AC
405 _ => false,
406 }
407}
75bb1906
E
408
409/// Allows nightly features to be enabled for this thread, but only if the
410/// development channel is nightly or dev.
411///
412/// Used by cargo main to ensure that a cargo build from source has nightly features
413pub fn maybe_allow_nightly_features() {
414 NIGHTLY_FEATURES_ALLOWED.with(|c| c.set(true));
415}
416
417/// Forcibly enables nightly features for this thread.
418///
419/// Used by tests to allow the use of nightly features.
420pub fn enable_nightly_features() {
421 ENABLE_NIGHTLY_FEATURES.with(|c| c.set(true));
422}