| 1 | //! Platform definition used by Cargo. |
| 2 | //! |
| 3 | //! This defines a [`Platform`] type which is used in Cargo to specify a target platform. |
| 4 | //! There are two kinds, a named target like `x86_64-apple-darwin`, and a "cfg expression" |
| 5 | //! like `cfg(any(target_os = "macos", target_os = "ios"))`. |
| 6 | //! |
| 7 | //! See `examples/matches.rs` for an example of how to match against a `Platform`. |
| 8 | //! |
| 9 | //! > This crate is maintained by the Cargo team for use by the wider |
| 10 | //! > ecosystem. This crate follows semver compatibility for its APIs. |
| 11 | //! |
| 12 | //! [`Platform`]: enum.Platform.html |
| 13 | |
| 14 | use std::fmt; |
| 15 | use std::str::FromStr; |
| 16 | |
| 17 | mod cfg; |
| 18 | mod error; |
| 19 | |
| 20 | pub use cfg::{Cfg, CfgExpr}; |
| 21 | pub use error::{ParseError, ParseErrorKind}; |
| 22 | |
| 23 | /// Platform definition. |
| 24 | #[derive (Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] |
| 25 | pub enum Platform { |
| 26 | /// A named platform, like `x86_64-apple-darwin`. |
| 27 | Name(String), |
| 28 | /// A cfg expression, like `cfg(windows)`. |
| 29 | Cfg(CfgExpr), |
| 30 | } |
| 31 | |
| 32 | impl Platform { |
| 33 | /// Returns whether the Platform matches the given target and cfg. |
| 34 | /// |
| 35 | /// The named target and cfg values should be obtained from `rustc`. |
| 36 | pub fn matches(&self, name: &str, cfg: &[Cfg]) -> bool { |
| 37 | match *self { |
| 38 | Platform::Name(ref p) => p == name, |
| 39 | Platform::Cfg(ref p) => p.matches(cfg), |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | fn validate_named_platform(name: &str) -> Result<(), ParseError> { |
| 44 | if let Some(ch) = name |
| 45 | .chars() |
| 46 | .find(|&c| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.' )) |
| 47 | { |
| 48 | if name.chars().any(|c| c == '(' ) { |
| 49 | return Err(ParseError::new( |
| 50 | name, |
| 51 | ParseErrorKind::InvalidTarget( |
| 52 | "unexpected `(` character, cfg expressions must start with `cfg(`" |
| 53 | .to_string(), |
| 54 | ), |
| 55 | )); |
| 56 | } |
| 57 | return Err(ParseError::new( |
| 58 | name, |
| 59 | ParseErrorKind::InvalidTarget(format!( |
| 60 | "unexpected character {} in target name" , |
| 61 | ch |
| 62 | )), |
| 63 | )); |
| 64 | } |
| 65 | Ok(()) |
| 66 | } |
| 67 | |
| 68 | pub fn check_cfg_attributes(&self, warnings: &mut Vec<String>) { |
| 69 | fn check_cfg_expr(expr: &CfgExpr, warnings: &mut Vec<String>) { |
| 70 | match *expr { |
| 71 | CfgExpr::Not(ref e) => check_cfg_expr(e, warnings), |
| 72 | CfgExpr::All(ref e) | CfgExpr::Any(ref e) => { |
| 73 | for e in e { |
| 74 | check_cfg_expr(e, warnings); |
| 75 | } |
| 76 | } |
| 77 | CfgExpr::Value(ref e) => match e { |
| 78 | Cfg::Name(name) => match name.as_str() { |
| 79 | "test" | "debug_assertions" | "proc_macro" => |
| 80 | warnings.push(format!( |
| 81 | "Found ` {}` in `target.'cfg(...)'.dependencies`. \ |
| 82 | This value is not supported for selecting dependencies \ |
| 83 | and will not work as expected. \ |
| 84 | To learn more visit \ |
| 85 | https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies" , |
| 86 | name |
| 87 | )), |
| 88 | _ => (), |
| 89 | }, |
| 90 | Cfg::KeyPair(name, _) => if name.as_str() == "feature" { |
| 91 | warnings.push(String::from( |
| 92 | "Found `feature = ...` in `target.'cfg(...)'.dependencies`. \ |
| 93 | This key is not supported for selecting dependencies \ |
| 94 | and will not work as expected. \ |
| 95 | Use the [features] section instead: \ |
| 96 | https://doc.rust-lang.org/cargo/reference/features.html" |
| 97 | )) |
| 98 | }, |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | if let Platform::Cfg(cfg) = self { |
| 104 | check_cfg_expr(cfg, warnings); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | impl serde::Serialize for Platform { |
| 110 | fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> |
| 111 | where |
| 112 | S: serde::Serializer, |
| 113 | { |
| 114 | self.to_string().serialize(serializer:s) |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | impl<'de> serde::Deserialize<'de> for Platform { |
| 119 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 120 | where |
| 121 | D: serde::Deserializer<'de>, |
| 122 | { |
| 123 | let s: String = String::deserialize(deserializer)?; |
| 124 | FromStr::from_str(&s).map_err(op:serde::de::Error::custom) |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | impl FromStr for Platform { |
| 129 | type Err = ParseError; |
| 130 | |
| 131 | fn from_str(s: &str) -> Result<Platform, ParseError> { |
| 132 | if let Some(s: &str) = s.strip_prefix("cfg(" ).and_then(|s: &str| s.strip_suffix(')' )) { |
| 133 | s.parse().map(op:Platform::Cfg) |
| 134 | } else { |
| 135 | Platform::validate_named_platform(name:s)?; |
| 136 | Ok(Platform::Name(s.to_string())) |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | impl fmt::Display for Platform { |
| 142 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 143 | match *self { |
| 144 | Platform::Name(ref n: &String) => n.fmt(f), |
| 145 | Platform::Cfg(ref e: &CfgExpr) => write!(f, "cfg( {})" , e), |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |