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