]>
Commit | Line | Data |
---|---|---|
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 | 96 | use std::collections::BTreeSet; |
3181ef24 | 97 | use std::env; |
2c99f654 | 98 | use std::fmt::{self, Write}; |
1d82d2b3 | 99 | use std::str::FromStr; |
3181ef24 | 100 | |
3a18c89a | 101 | use anyhow::{bail, Error}; |
88810035 | 102 | use cargo_util::ProcessBuilder; |
9ed82b57 | 103 | use serde::{Deserialize, Serialize}; |
84130089 | 104 | |
df0c64eb | 105 | use crate::core::resolver::ResolveBehavior; |
04ddd4d0 | 106 | use crate::util::errors::CargoResult; |
ba584a95 | 107 | use crate::util::{indented_lines, iter_join}; |
4b096bea | 108 | use crate::Config; |
3181ef24 | 109 | |
05d37ae2 | 110 | pub const HIDDEN: &str = ""; |
35b843ef EH |
111 | pub 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 |
117 | pub 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 | 153 | impl 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 | 256 | impl 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 | 265 | impl 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 |
287 | enum Status { |
288 | Stable, | |
289 | Unstable, | |
7bbef3de | 290 | Removed, |
3181ef24 AC |
291 | } |
292 | ||
293 | macro_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 | ||
344 | macro_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 | 367 | features! { |
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 | ||
431 | pub 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 | ||
439 | impl 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 | 610 | macro_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 | ||
639 | unstable_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 |
680 | const 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 | ||
685 | const STABILIZED_OFFLINE: &str = "Offline mode is now available via the \ | |
686 | --offline CLI option"; | |
687 | ||
688 | const STABILIZED_CACHE_MESSAGES: &str = "Message caching is now always enabled."; | |
689 | ||
690 | const 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 | ||
695 | const 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 | ||
699 | const STABILIZED_CRATE_VERSIONS: &str = "The crate version is now \ | |
700 | automatically added to the documentation."; | |
701 | ||
702 | const 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 | ||
709 | const 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 |
714 | const STABILIZED_EXTRA_LINK_ARG: &str = "Additional linker arguments are now \ |
715 | supported without passing this flag."; | |
716 | ||
ab38ce0f JT |
717 | const STABILIZED_CONFIGURABLE_ENV: &str = "The [env] section is now always enabled."; |
718 | ||
1e0d564f JG |
719 | const STABILIZED_PATCH_IN_CONFIG: &str = "The patch-in-config feature is now always enabled."; |
720 | ||
895f5271 EH |
721 | const 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 |
725 | const STABILIZED_FUTURE_INCOMPAT_REPORT: &str = |
726 | "The future-incompat-report feature is now always enabled."; | |
727 | ||
43a063c8 EH |
728 | const STABILIZED_WEAK_DEP_FEATURES: &str = "Weak dependency features are now always available."; |
729 | ||
730 | const STABILISED_NAMESPACED_FEATURES: &str = "Namespaced features are now always available."; | |
731 | ||
c0669189 JT |
732 | const STABILIZED_TIMINGS: &str = "The -Ztimings option has been stabilized as --timings."; |
733 | ||
85e89cf3 WL |
734 | const STABILISED_MULTITARGET: &str = "Multiple `--target` options are now always available."; |
735 | ||
78314caf AC |
736 | fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error> |
737 | where | |
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 |
750 | fn deserialize_check_cfg<'de, D>( |
751 | deserializer: D, | |
752 | ) -> Result<Option<(bool, bool, bool, bool)>, D::Error> | |
753 | where | |
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 | ||
765 | fn 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 | 791 | impl 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"). |
1046 | pub 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 | } |