1use std::cell::Cell;
49use std::env;
50use std::fmt;
51use std::str::FromStr;
52
53use anyhow::{bail, Error};
54use serde::{Deserialize, Serialize};
55
56use crate::util::errors::CargoResult;
57
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
62#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
64pub enum Edition {
65 Edition2015,
67 Edition2018,
69}
70
71impl fmt::Display for Edition {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match *self {
74 Edition::Edition2015 => f.write_str("2015"),
75 Edition::Edition2018 => f.write_str("2018"),
76 }
77 }
78}
79impl FromStr for Edition {
80 type Err = Error;
81 fn from_str(s: &str) -> Result<Self, Error> {
82 match s {
83 "2015" => Ok(Edition::Edition2015),
84 "2018" => Ok(Edition::Edition2018),
85 s => bail!(
86 "supported edition values are `2015` or `2018`, but `{}` \
87 is unknown",
88 s
89 ),
90 }
91 }
92}
93
94#[derive(PartialEq)]
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 {
115 fn get(features: &Features) -> bool {
116 stab!($stab) == Status::Stable || features.$feature
117 }
118 static FEAT: Feature = Feature {
119 name: stringify!($feature),
120 get,
121 };
122 &FEAT
123 }
124 )*
125
126 fn is_enabled(&self, features: &Features) -> bool {
127 (self.get)(features)
128 }
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 {
149 (stable) => {
150 Status::Stable
151 };
152 (unstable) => {
153 Status::Unstable
154 };
155}
156
157features! {
169 pub struct Features {
170
171 [stable] test_dummy_stable: bool,
174
175 [unstable] test_dummy_unstable: bool,
178
179 [stable] alternative_registries: bool,
181
182 [stable] edition: bool,
184
185 [stable] rename_dependency: bool,
187
188 [unstable] publish_lockfile: bool,
191
192 [stable] profile_overrides: bool,
194
195 [unstable] namespaced_features: bool,
197
198 [stable] default_run: bool,
200
201 [unstable] metabuild: bool,
203
204 [unstable] public_dependency: bool,
206
207 [unstable] named_profiles: bool,
209 }
210}
211
212pub struct Feature {
213 name: &'static str,
214 get: fn(&Features) -> bool,
215}
216
217impl Features {
218 pub fn new(features: &[String], warnings: &mut Vec<String>) -> CargoResult<Features> {
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,
230 None => bail!("unknown cargo feature `{}`", feature),
231 };
232
233 if *slot {
234 bail!("the cargo feature `{}` has already been activated", feature);
235 }
236
237 match status {
238 Status::Stable => {
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 );
245 warnings.push(warning);
246 }
247 Status::Unstable if !nightly_features_allowed() => bail!(
248 "the cargo feature `{}` requires a nightly version of \
249 Cargo, but this is the `{}` channel\n\
250 {}",
251 feature,
252 channel(),
253 SEE_CHANNELS
254 ),
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<()> {
268 if feature.is_enabled(self) {
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() {
275 let s = format!(
276 "\n\nconsider adding `cargo-features = [\"{0}\"]` \
277 to the manifest",
278 feature
279 );
280 msg.push_str(&s);
281 } else {
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 );
289 msg.push_str(&s);
290 }
291 bail!("{}", msg);
292 }
293 }
294
295 pub fn is_enabled(&self, feature: &Feature) -> bool {
296 feature.is_enabled(self)
297 }
298}
299
300#[derive(Default, Debug)]
326pub struct CliUnstable {
327 pub print_im_a_teapot: bool,
328 pub unstable_options: bool,
329 pub no_index_update: bool,
330 pub avoid_dev_deps: bool,
331 pub minimal_versions: bool,
332 pub package_features: bool,
333 pub advanced_env: bool,
334 pub config_include: bool,
335 pub dual_proc_macros: bool,
336 pub mtime_on_use: bool,
337 pub named_profiles: bool,
338 pub binary_dep_depinfo: bool,
339 pub build_std: Option<Vec<String>>,
340 pub timings: Option<Vec<String>>,
341 pub doctest_xcompile: bool,
342 pub panic_abort_tests: bool,
343 pub jobserver_per_rustc: bool,
344 pub features: Option<Vec<String>>,
345 pub crate_versions: bool,
346}
347
348impl CliUnstable {
349 pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
350 if !flags.is_empty() && !nightly_features_allowed() {
351 bail!(
352 "the `-Z` flag is only accepted on the nightly channel of Cargo, \
353 but this is the `{}` channel\n\
354 {}",
355 channel(),
356 SEE_CHANNELS
357 );
358 }
359 for flag in flags {
360 self.add(flag)?;
361 }
362 Ok(())
363 }
364
365 fn add(&mut self, flag: &str) -> CargoResult<()> {
366 let mut parts = flag.splitn(2, '=');
367 let k = parts.next().unwrap();
368 let v = parts.next();
369
370 fn parse_bool(key: &str, value: Option<&str>) -> CargoResult<bool> {
371 match value {
372 None | Some("yes") => Ok(true),
373 Some("no") => Ok(false),
374 Some(s) => bail!("flag -Z{} expected `no` or `yes`, found: `{}`", key, s),
375 }
376 }
377
378 fn parse_timings(value: Option<&str>) -> Vec<String> {
379 match value {
380 None => vec!["html".to_string(), "info".to_string()],
381 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
382 }
383 }
384
385 fn parse_features(value: Option<&str>) -> Vec<String> {
386 match value {
387 None => Vec::new(),
388 Some(v) => v.split(',').map(|s| s.to_string()).collect(),
389 }
390 }
391
392 fn parse_empty(key: &str, value: Option<&str>) -> CargoResult<bool> {
394 if let Some(v) = value {
395 bail!("flag -Z{} does not take a value, found: `{}`", key, v);
396 }
397 Ok(true)
398 };
399
400 match k {
401 "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?,
402 "unstable-options" => self.unstable_options = parse_empty(k, v)?,
403 "no-index-update" => self.no_index_update = parse_empty(k, v)?,
404 "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
405 "minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
406 "package-features" => self.package_features = parse_empty(k, v)?,
407 "advanced-env" => self.advanced_env = parse_empty(k, v)?,
408 "config-include" => self.config_include = parse_empty(k, v)?,
409 "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
410 "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
412 "named-profiles" => self.named_profiles = parse_empty(k, v)?,
413 "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
414 "build-std" => {
415 self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
416 }
417 "timings" => self.timings = Some(parse_timings(v)),
418 "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
419 "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
420 "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
421 "features" => self.features = Some(parse_features(v)),
422 "crate-versions" => self.crate_versions = parse_empty(k, v)?,
423 _ => bail!("unknown `-Z` flag specified: {}", k),
424 }
425
426 Ok(())
427 }
428
429 pub fn fail_if_stable_opt(&self, flag: &str, issue: u32) -> CargoResult<()> {
433 if !self.unstable_options {
434 let see = format!(
435 "See https://github.com/rust-lang/cargo/issues/{} for more \
436 information about the `{}` flag.",
437 issue, flag
438 );
439 if nightly_features_allowed() {
440 bail!(
441 "the `{}` flag is unstable, pass `-Z unstable-options` to enable it\n\
442 {}",
443 flag,
444 see
445 );
446 } else {
447 bail!(
448 "the `{}` flag is unstable, and only available on the nightly channel \
449 of Cargo, but this is the `{}` channel\n\
450 {}\n\
451 {}",
452 flag,
453 channel(),
454 SEE_CHANNELS,
455 see
456 );
457 }
458 }
459 Ok(())
460 }
461}
462
463pub fn channel() -> String {
465 if let Ok(override_channel) = env::var("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS") {
466 return override_channel;
467 }
468 if let Ok(staging) = env::var("RUSTC_BOOTSTRAP") {
469 if staging == "1" {
470 return "dev".to_string();
471 }
472 }
473 crate::version()
474 .cfg_info
475 .map(|c| c.release_channel)
476 .unwrap_or_else(|| String::from("dev"))
477}
478
479thread_local!(
480 static NIGHTLY_FEATURES_ALLOWED: Cell<bool> = Cell::new(false);
481 static ENABLE_NIGHTLY_FEATURES: Cell<bool> = Cell::new(false);
482);
483
484pub fn nightly_features_allowed() -> bool {
498 if ENABLE_NIGHTLY_FEATURES.with(|c| c.get()) {
499 return true;
500 }
501 match &channel()[..] {
502 "nightly" | "dev" => NIGHTLY_FEATURES_ALLOWED.with(|c| c.get()),
503 _ => false,
504 }
505}
506
507pub fn maybe_allow_nightly_features() {
512 NIGHTLY_FEATURES_ALLOWED.with(|c| c.set(true));
513}
514
515pub fn enable_nightly_features() {
519 ENABLE_NIGHTLY_FEATURES.with(|c| c.set(true));
520}