|
6 | 6 | //!
|
7 | 7 | //! # Examples
|
8 | 8 | //!
|
9 |
| -//! Set a `cfg` flag in `build.rs` if the running compiler was determined to be |
10 |
| -//! at least version `1.13.0`: |
| 9 | +//! * Set a `cfg` flag in `build.rs` if the running compiler was determined to |
| 10 | +//! be at least version `1.13.0`: |
11 | 11 | //!
|
12 |
| -//! ```rust |
13 |
| -//! extern crate version_check as rustc; |
| 12 | +//! ```rust |
| 13 | +//! extern crate version_check as rustc; |
14 | 14 | //!
|
15 |
| -//! if rustc::is_min_version("1.13.0").unwrap_or(false) { |
16 |
| -//! println!("cargo:rustc-cfg=question_mark_operator"); |
17 |
| -//! } |
18 |
| -//! ``` |
| 15 | +//! if rustc::is_min_version("1.13.0").unwrap_or(false) { |
| 16 | +//! println!("cargo:rustc-cfg=question_mark_operator"); |
| 17 | +//! } |
| 18 | +//! ``` |
19 | 19 | //!
|
20 |
| -//! See [`is_max_version`] or [`is_exact_version`] to check if the compiler |
21 |
| -//! is _at most_ or _exactly_ a certain version. |
| 20 | +//! See [`is_max_version`] or [`is_exact_version`] to check if the compiler |
| 21 | +//! is _at most_ or _exactly_ a certain version. |
22 | 22 | //!
|
23 |
| -//! Check that the running compiler was released on or after `2018-12-18`: |
| 23 | +//! * Check that the running compiler was released on or after `2018-12-18`: |
24 | 24 | //!
|
25 |
| -//! ```rust |
26 |
| -//! extern crate version_check as rustc; |
| 25 | +//! ```rust |
| 26 | +//! extern crate version_check as rustc; |
27 | 27 | //!
|
28 |
| -//! match rustc::is_min_date("2018-12-18") { |
29 |
| -//! Some(true) => "Yep! It's recent!", |
30 |
| -//! Some(false) => "No, it's older.", |
31 |
| -//! None => "Couldn't determine the rustc version." |
32 |
| -//! }; |
33 |
| -//! ``` |
| 28 | +//! match rustc::is_min_date("2018-12-18") { |
| 29 | +//! Some(true) => "Yep! It's recent!", |
| 30 | +//! Some(false) => "No, it's older.", |
| 31 | +//! None => "Couldn't determine the rustc version." |
| 32 | +//! }; |
| 33 | +//! ``` |
34 | 34 | //!
|
35 |
| -//! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was |
36 |
| -//! released _prior to_ or _exactly on_ a certain date. |
| 35 | +//! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was |
| 36 | +//! released _prior to_ or _exactly on_ a certain date. |
37 | 37 | //!
|
38 |
| -//! Check that the running compiler supports feature flags: |
| 38 | +//! * Check that the running compiler supports feature flags: |
39 | 39 | //!
|
40 |
| -//! ```rust |
41 |
| -//! extern crate version_check as rustc; |
| 40 | +//! ```rust |
| 41 | +//! extern crate version_check as rustc; |
42 | 42 | //!
|
43 |
| -//! match rustc::is_feature_flaggable() { |
44 |
| -//! Some(true) => "Yes! It's a dev or nightly release!", |
45 |
| -//! Some(false) => "No, it's stable or beta.", |
46 |
| -//! None => "Couldn't determine the rustc version." |
47 |
| -//! }; |
48 |
| -//! ``` |
| 43 | +//! match rustc::is_feature_flaggable() { |
| 44 | +//! Some(true) => "Yes! It's a dev or nightly release!", |
| 45 | +//! Some(false) => "No, it's stable or beta.", |
| 46 | +//! None => "Couldn't determine the rustc version." |
| 47 | +//! }; |
| 48 | +//! ``` |
49 | 49 | //!
|
50 |
| -//! Check that the running compiler is on the stable channel: |
| 50 | +//! * Check that the running compiler supports a specific feature: |
51 | 51 | //!
|
52 |
| -//! ```rust |
53 |
| -//! extern crate version_check as rustc; |
| 52 | +//! ```rust |
| 53 | +//! extern crate version_check as rustc; |
54 | 54 | //!
|
55 |
| -//! match rustc::Channel::read() { |
56 |
| -//! Some(c) if c.is_stable() => format!("Yes! It's stable."), |
57 |
| -//! Some(c) => format!("No, the channel {} is not stable.", c), |
58 |
| -//! None => format!("Couldn't determine the rustc version.") |
59 |
| -//! }; |
60 |
| -//! ``` |
| 55 | +//! if let Some(true) = rustc::supports_feature("doc_cfg") { |
| 56 | +//! println!("cargo:rustc-cfg=has_doc_cfg"); |
| 57 | +//! } |
| 58 | +//! ``` |
| 59 | +//! |
| 60 | +//! * Check that the running compiler is on the stable channel: |
| 61 | +//! |
| 62 | +//! ```rust |
| 63 | +//! extern crate version_check as rustc; |
| 64 | +//! |
| 65 | +//! match rustc::Channel::read() { |
| 66 | +//! Some(c) if c.is_stable() => format!("Yes! It's stable."), |
| 67 | +//! Some(c) => format!("No, the channel {} is not stable.", c), |
| 68 | +//! None => format!("Couldn't determine the rustc version.") |
| 69 | +//! }; |
| 70 | +//! ``` |
61 | 71 | //!
|
62 | 72 | //! To interact with the version, release date, and release channel as structs,
|
63 | 73 | //! use [`Version`], [`Date`], and [`Channel`], respectively. The [`triple()`]
|
@@ -248,12 +258,64 @@ pub fn is_exact_version(version: &str) -> Option<bool> {
|
248 | 258 | ///
|
249 | 259 | /// In other words, if the channel is either "nightly" or "dev".
|
250 | 260 | ///
|
| 261 | +/// Note that support for specific `rustc` features can be enabled or disabled |
| 262 | +/// via the `allow-features` compiler flag, which this function _does not_ |
| 263 | +/// check. That is, this function _does not_ check whether a _specific_ feature |
| 264 | +/// is supported, but instead whether features are supported at all. To check |
| 265 | +/// for support for a specific feature, use [`supports_feature()`]. |
| 266 | +/// |
251 | 267 | /// If the version could not be determined, returns `None`. Otherwise returns
|
252 | 268 | /// `true` if the running version supports feature flags and `false` otherwise.
|
253 | 269 | pub fn is_feature_flaggable() -> Option<bool> {
|
254 | 270 | Channel::read().map(|c| c.supports_features())
|
255 | 271 | }
|
256 | 272 |
|
| 273 | +/// Checks whether the running or installed `rustc` supports `feature`. |
| 274 | +/// |
| 275 | +/// Returns _true_ _iff_ [`is_feature_flaggable()`] returns `true` _and_ the |
| 276 | +/// feature is not disabled via exclusion in `allow-features` via `RUSTFLAGS` or |
| 277 | +/// `CARGO_ENCODED_RUSTFLAGS`. If the version could not be determined, returns |
| 278 | +/// `None`. |
| 279 | +/// |
| 280 | +/// # Example |
| 281 | +/// |
| 282 | +/// ```rust |
| 283 | +/// use version_check as rustc; |
| 284 | +/// |
| 285 | +/// if let Some(true) = rustc::supports_feature("doc_cfg") { |
| 286 | +/// println!("cargo:rustc-cfg=has_doc_cfg"); |
| 287 | +/// } |
| 288 | +/// ``` |
| 289 | +pub fn supports_feature(feature: &str) -> Option<bool> { |
| 290 | + match is_feature_flaggable() { |
| 291 | + Some(true) => { /* continue */ } |
| 292 | + Some(false) => return Some(false), |
| 293 | + None => return None, |
| 294 | + } |
| 295 | + |
| 296 | + let env_flags = env::var_os("CARGO_ENCODED_RUSTFLAGS") |
| 297 | + .map(|flags| (flags, '\x1f')) |
| 298 | + .or_else(|| env::var_os("RUSTFLAGS").map(|flags| (flags, ' '))); |
| 299 | + |
| 300 | + if let Some((flags, delim)) = env_flags { |
| 301 | + const ALLOW_FEATURES: &'static str = "allow-features="; |
| 302 | + |
| 303 | + let rustflags = flags.to_string_lossy(); |
| 304 | + let allow_features = rustflags.split(delim) |
| 305 | + .map(|flag| flag.trim_left_matches("-Z").trim()) |
| 306 | + .filter(|flag| flag.starts_with(ALLOW_FEATURES)) |
| 307 | + .map(|flag| &flag[ALLOW_FEATURES.len()..]); |
| 308 | + |
| 309 | + if let Some(allow_features) = allow_features.last() { |
| 310 | + return Some(allow_features.split(',').any(|f| f.trim() == feature)); |
| 311 | + } |
| 312 | + } |
| 313 | + |
| 314 | + // If there are no `RUSTFLAGS` or `CARGO_ENCODED_RUSTFLAGS` or they don't |
| 315 | + // contain an `allow-features` flag, assume compiler allows all features. |
| 316 | + Some(true) |
| 317 | +} |
| 318 | + |
257 | 319 | #[cfg(test)]
|
258 | 320 | mod tests {
|
259 | 321 | use std::{env, fs};
|
|
0 commit comments