Skip to content

Add cfg_version support #15533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" }
cargo-credential-libsecret = { version = "0.4.15", path = "credential/cargo-credential-libsecret" }
cargo-credential-macos-keychain = { version = "0.4.15", path = "credential/cargo-credential-macos-keychain" }
cargo-credential-wincred = { version = "0.4.15", path = "credential/cargo-credential-wincred" }
cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" }
cargo-platform = { path = "crates/cargo-platform", version = "0.4.0" }
cargo-test-macro = { version = "0.4.4", path = "crates/cargo-test-macro" }
cargo-test-support = { version = "0.7.5", path = "crates/cargo-test-support" }
cargo-util = { version = "0.2.22", path = "crates/cargo-util" }
Expand Down
6 changes: 5 additions & 1 deletion crates/cargo-platform/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-platform"
version = "0.3.0"
version = "0.4.0"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
Expand All @@ -10,7 +10,11 @@ documentation = "https://docs.rs/cargo-platform"
description = "Cargo's representation of a target platform."

[dependencies]
semver.workspace = true
serde.workspace = true

[dev-dependencies]
snapbox.workspace = true

[lints]
workspace = true
6 changes: 5 additions & 1 deletion crates/cargo-platform/examples/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ fn main() {
examples.push(target.as_str());
for example in examples {
let p = Platform::from_str(example).unwrap();
println!("{:?} matches: {:?}", example, p.matches(&target, &cfgs));
println!(
"{:?} matches: {:?}",
example,
p.matches(&target, &cfgs, &semver::Version::new(0, 0, 0))
);
}
}

Expand Down
82 changes: 76 additions & 6 deletions crates/cargo-platform/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::{ParseError, ParseErrorKind::*};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter;
Expand All @@ -15,13 +16,59 @@ pub enum CfgExpr {
False,
}

// Major version restricted to `1`.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub struct CfgRustVersion {
pub minor: u64,
pub patch: Option<u64>,
}

impl CfgRustVersion {
pub fn parse(version: &str) -> Option<Self> {
let minor_patch = version.strip_prefix("1.")?;
let (minor, patch) = match minor_patch.split_once('.') {
Some((minor, patch)) => (minor.parse().ok()?, Some(patch.parse().ok()?)),
None => (minor_patch.parse().ok()?, None),
};
Some(Self { minor, patch })
}

pub fn matches(&self, rustc_version: &semver::Version) -> bool {
match self.minor.cmp(&rustc_version.minor) {
Ordering::Less => true,
Ordering::Equal => match self.patch {
Some(patch) => {
if rustc_version.pre.as_str() == "nightly" {
false
} else {
patch <= rustc_version.patch
}
}
None => true,
},
Ordering::Greater => false,
}
}
}

impl fmt::Display for CfgRustVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.patch {
Some(patch) => write!(f, "version(\"1.{}.{patch}\")", self.minor),
None => write!(f, "version(\"1.{}\")", self.minor),
}
}
}

/// A cfg value.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum Cfg {
/// A named cfg value, like `unix`.
Name(Ident),
/// A key/value cfg pair, like `target_os = "linux"`.
KeyPair(Ident, String),
/// A Rust version cfg value, like `version("1.23.4")` or `version("1.23")`.
Version(CfgRustVersion),
}

/// A identifier
Expand Down Expand Up @@ -124,30 +171,34 @@ impl fmt::Display for Cfg {
match *self {
Cfg::Name(ref s) => s.fmt(f),
Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v),
Cfg::Version(ref cfg_rust_version) => cfg_rust_version.fmt(f),
}
}
}

impl CfgExpr {
/// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
pub fn matches_key(key: &str, target_cfg: &[Cfg], rustc_version: &semver::Version) -> bool {
if key.starts_with("cfg(") && key.ends_with(')') {
let cfg = &key[4..key.len() - 1];

CfgExpr::from_str(cfg)
.ok()
.map(|ce| ce.matches(target_cfg))
.map(|ce| ce.matches(target_cfg, rustc_version))
.unwrap_or(false)
} else {
false
}
}

pub fn matches(&self, cfg: &[Cfg]) -> bool {
pub fn matches(&self, cfg: &[Cfg], rustc_version: &semver::Version) -> bool {
match *self {
CfgExpr::Not(ref e) => !e.matches(cfg),
CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)),
CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)),
CfgExpr::Not(ref e) => !e.matches(cfg, rustc_version),
CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg, rustc_version)),
CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg, rustc_version)),
CfgExpr::Value(Cfg::Version(ref cfg_rust_version)) => {
cfg_rust_version.matches(rustc_version)
}
CfgExpr::Value(ref e) => cfg.contains(e),
CfgExpr::True => true,
CfgExpr::False => false,
Expand Down Expand Up @@ -275,6 +326,25 @@ impl<'a> Parser<'a> {
},
val.to_string(),
)
} else if name == "version" {
self.eat(&Token::LeftParen)?;
let token = self
.t
.next()
.ok_or_else(|| ParseError::new(self.t.orig, InvalidVersion))??;
let Token::String(version_str) = token else {
return Err(ParseError::new(
self.t.orig,
UnexpectedToken {
expected: "a string",
found: token.classify(),
},
));
};
self.eat(&Token::RightParen)?;
let version = CfgRustVersion::parse(version_str)
.ok_or_else(|| ParseError::new(self.t.orig, InvalidVersion))?;
Cfg::Version(version)
} else {
Cfg::Name(Ident {
name: name.to_string(),
Expand Down
5 changes: 5 additions & 0 deletions crates/cargo-platform/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum ParseErrorKind {
IncompleteExpr(&'static str),
UnterminatedExpression(String),
InvalidTarget(String),
InvalidVersion,
}

impl fmt::Display for ParseError {
Expand Down Expand Up @@ -51,6 +52,10 @@ impl fmt::Display for ParseErrorKind {
write!(f, "unexpected content `{}` found after cfg expression", s)
}
InvalidTarget(s) => write!(f, "invalid target specifier: {}", s),
InvalidVersion => write!(
f,
"invalid Rust cfg version, expected format `version(\"1.23.4\")` or `version(\"1.23\")`"
),
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions crates/cargo-platform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod cfg;
mod error;

use cfg::KEYWORDS;
pub use cfg::{Cfg, CfgExpr, Ident};
pub use cfg::{Cfg, CfgExpr, CfgRustVersion, Ident};
pub use error::{ParseError, ParseErrorKind};

/// Platform definition.
Expand All @@ -34,10 +34,10 @@ impl Platform {
/// Returns whether the Platform matches the given target and cfg.
///
/// The named target and cfg values should be obtained from `rustc`.
pub fn matches(&self, name: &str, cfg: &[Cfg]) -> bool {
pub fn matches(&self, name: &str, cfg: &[Cfg], rustc_version: &semver::Version) -> bool {
match *self {
Platform::Name(ref p) => p == name,
Platform::Cfg(ref p) => p.matches(cfg),
Platform::Cfg(ref p) => p.matches(cfg, rustc_version),
}
}

Expand Down Expand Up @@ -97,6 +97,7 @@ impl Platform {
https://doc.rust-lang.org/cargo/reference/features.html"
))
},
Cfg::Version(..) => {},
}
CfgExpr::True | CfgExpr::False => {},
}
Expand Down Expand Up @@ -130,6 +131,7 @@ impl Platform {
));
}
}
Cfg::Version(..) => {}
},
}
}
Expand Down
Loading