mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
Auto merge of #7375 - ehuss:extract-platform, r=alexcrichton
Extract Platform to a separate crate. This moves the `Platform`, `Cfg`, `CfgExpr` types to a new crate named "cargo-platform". The intent here is to give users of `cargo_metadata` a way of parsing and inspecting cargo's platform values. Along the way, I rewrote the error handling to remove `failure`, and to slightly improve the output. I'm having doubts whether or not this is a good idea. As you can see from the `examples/matches.rs` example, it is nontrivial to use this (which also misses cargo's config values and environment variables). I don't know if anyone will actually use this. If this doesn't seem to have value, I would suggest closing it. I've also included a sample script, `publish.py`, for publishing cargo itself. I suspect it will need tweaking, but I figure it would be a start and open for feedback.
This commit is contained in:
commit
7ab477866e
@ -21,6 +21,7 @@ path = "src/cargo/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
bytesize = "1.0"
|
bytesize = "1.0"
|
||||||
|
cargo-platform = { path = "crates/cargo-platform", version = "0.1" }
|
||||||
crates-io = { path = "crates/crates-io", version = "0.28" }
|
crates-io = { path = "crates/crates-io", version = "0.28" }
|
||||||
crossbeam-utils = "0.6"
|
crossbeam-utils = "0.6"
|
||||||
crypto-hash = "0.3.1"
|
crypto-hash = "0.3.1"
|
||||||
|
@ -61,6 +61,8 @@ jobs:
|
|||||||
displayName: "Check rustfmt (crates-io)"
|
displayName: "Check rustfmt (crates-io)"
|
||||||
- bash: cd crates/resolver-tests && cargo fmt --all -- --check
|
- bash: cd crates/resolver-tests && cargo fmt --all -- --check
|
||||||
displayName: "Check rustfmt (resolver-tests)"
|
displayName: "Check rustfmt (resolver-tests)"
|
||||||
|
- bash: cd crates/cargo-platform && cargo fmt --all -- --check
|
||||||
|
displayName: "Check rustfmt (cargo-platform)"
|
||||||
variables:
|
variables:
|
||||||
TOOLCHAIN: stable
|
TOOLCHAIN: stable
|
||||||
|
|
||||||
|
@ -29,3 +29,6 @@ steps:
|
|||||||
|
|
||||||
- bash: cargo test -p cargo-test-support
|
- bash: cargo test -p cargo-test-support
|
||||||
displayName: "cargo test -p cargo-test-support"
|
displayName: "cargo test -p cargo-test-support"
|
||||||
|
|
||||||
|
- bash: cargo test -p cargo-platform
|
||||||
|
displayName: "cargo test -p cargo-platform"
|
||||||
|
13
crates/cargo-platform/Cargo.toml
Normal file
13
crates/cargo-platform/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "cargo-platform"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Cargo Project Developers"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
homepage = "https://github.com/rust-lang/cargo"
|
||||||
|
repository = "https://github.com/rust-lang/cargo"
|
||||||
|
documentation = "https://docs.rs/cargo-platform"
|
||||||
|
description = "Cargo's representation of a target platform."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.82", features = ['derive'] }
|
55
crates/cargo-platform/examples/matches.rs
Normal file
55
crates/cargo-platform/examples/matches.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//! This example demonstrates how to filter a Platform based on the current
|
||||||
|
//! host target.
|
||||||
|
|
||||||
|
use cargo_platform::{Cfg, Platform};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
static EXAMPLES: &[&str] = &[
|
||||||
|
"cfg(windows)",
|
||||||
|
"cfg(unix)",
|
||||||
|
"cfg(target_os=\"macos\")",
|
||||||
|
"cfg(target_os=\"linux\")",
|
||||||
|
"cfg(any(target_arch=\"x86\", target_arch=\"x86_64\"))",
|
||||||
|
];
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let target = get_target();
|
||||||
|
let cfgs = get_cfgs();
|
||||||
|
println!("host target={} cfgs:", target);
|
||||||
|
for cfg in &cfgs {
|
||||||
|
println!(" {}", cfg);
|
||||||
|
}
|
||||||
|
let mut examples: Vec<&str> = EXAMPLES.iter().copied().collect();
|
||||||
|
examples.push(target.as_str());
|
||||||
|
for example in examples {
|
||||||
|
let p = Platform::from_str(example).unwrap();
|
||||||
|
println!("{:?} matches: {:?}", example, p.matches(&target, &cfgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_target() -> String {
|
||||||
|
let output = Command::new("rustc")
|
||||||
|
.arg("-Vv")
|
||||||
|
.output()
|
||||||
|
.expect("rustc failed to run");
|
||||||
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||||
|
for line in stdout.lines() {
|
||||||
|
if line.starts_with("host: ") {
|
||||||
|
return String::from(&line[6..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Failed to find host: {}", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cfgs() -> Vec<Cfg> {
|
||||||
|
let output = Command::new("rustc")
|
||||||
|
.arg("--print=cfg")
|
||||||
|
.output()
|
||||||
|
.expect("rustc failed to run");
|
||||||
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||||
|
stdout
|
||||||
|
.lines()
|
||||||
|
.map(|line| Cfg::from_str(line).unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
@ -1,15 +1,9 @@
|
|||||||
|
use crate::error::{ParseError, ParseErrorKind::*};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
|
|
||||||
use crate::util::CargoResult;
|
/// A cfg expression.
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
|
||||||
pub enum Cfg {
|
|
||||||
Name(String),
|
|
||||||
KeyPair(String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
||||||
pub enum CfgExpr {
|
pub enum CfgExpr {
|
||||||
Not(Box<CfgExpr>),
|
Not(Box<CfgExpr>),
|
||||||
@ -18,6 +12,15 @@ pub enum CfgExpr {
|
|||||||
Value(Cfg),
|
Value(Cfg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A cfg value.
|
||||||
|
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
||||||
|
pub enum Cfg {
|
||||||
|
/// A named cfg value, like `unix`.
|
||||||
|
Name(String),
|
||||||
|
/// A key/value cfg pair, like `target_os = "linux"`.
|
||||||
|
KeyPair(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum Token<'a> {
|
enum Token<'a> {
|
||||||
LeftParen,
|
LeftParen,
|
||||||
@ -28,23 +31,27 @@ enum Token<'a> {
|
|||||||
String(&'a str),
|
String(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Tokenizer<'a> {
|
struct Tokenizer<'a> {
|
||||||
s: iter::Peekable<str::CharIndices<'a>>,
|
s: iter::Peekable<str::CharIndices<'a>>,
|
||||||
orig: &'a str,
|
orig: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Parser<'a> {
|
struct Parser<'a> {
|
||||||
t: iter::Peekable<Tokenizer<'a>>,
|
t: Tokenizer<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Cfg {
|
impl FromStr for Cfg {
|
||||||
type Err = failure::Error;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> CargoResult<Cfg> {
|
fn from_str(s: &str) -> Result<Cfg, Self::Err> {
|
||||||
let mut p = Parser::new(s);
|
let mut p = Parser::new(s);
|
||||||
let e = p.cfg()?;
|
let e = p.cfg()?;
|
||||||
if p.t.next().is_some() {
|
if let Some(rest) = p.rest() {
|
||||||
failure::bail!("malformed cfg value or key/value pair: `{}`", s)
|
return Err(ParseError::new(
|
||||||
|
p.t.orig,
|
||||||
|
UnterminatedExpression(rest.to_string()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(e)
|
Ok(e)
|
||||||
}
|
}
|
||||||
@ -85,16 +92,16 @@ impl CfgExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for CfgExpr {
|
impl FromStr for CfgExpr {
|
||||||
type Err = failure::Error;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> CargoResult<CfgExpr> {
|
fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
|
||||||
let mut p = Parser::new(s);
|
let mut p = Parser::new(s);
|
||||||
let e = p.expr()?;
|
let e = p.expr()?;
|
||||||
if p.t.next().is_some() {
|
if let Some(rest) = p.rest() {
|
||||||
failure::bail!(
|
return Err(ParseError::new(
|
||||||
"can only have one cfg-expression, consider using all() or \
|
p.t.orig,
|
||||||
any() explicitly"
|
UnterminatedExpression(rest.to_string()),
|
||||||
)
|
));
|
||||||
}
|
}
|
||||||
Ok(e)
|
Ok(e)
|
||||||
}
|
}
|
||||||
@ -131,14 +138,13 @@ impl<'a> Parser<'a> {
|
|||||||
t: Tokenizer {
|
t: Tokenizer {
|
||||||
s: s.char_indices().peekable(),
|
s: s.char_indices().peekable(),
|
||||||
orig: s,
|
orig: s,
|
||||||
}
|
},
|
||||||
.peekable(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr(&mut self) -> CargoResult<CfgExpr> {
|
fn expr(&mut self) -> Result<CfgExpr, ParseError> {
|
||||||
match self.t.peek() {
|
match self.peek() {
|
||||||
Some(&Ok(Token::Ident(op @ "all"))) | Some(&Ok(Token::Ident(op @ "any"))) => {
|
Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
|
||||||
self.t.next();
|
self.t.next();
|
||||||
let mut e = Vec::new();
|
let mut e = Vec::new();
|
||||||
self.eat(&Token::LeftParen)?;
|
self.eat(&Token::LeftParen)?;
|
||||||
@ -155,31 +161,41 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(CfgExpr::Any(e))
|
Ok(CfgExpr::Any(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(&Ok(Token::Ident("not"))) => {
|
Some(Ok(Token::Ident("not"))) => {
|
||||||
self.t.next();
|
self.t.next();
|
||||||
self.eat(&Token::LeftParen)?;
|
self.eat(&Token::LeftParen)?;
|
||||||
let e = self.expr()?;
|
let e = self.expr()?;
|
||||||
self.eat(&Token::RightParen)?;
|
self.eat(&Token::RightParen)?;
|
||||||
Ok(CfgExpr::Not(Box::new(e)))
|
Ok(CfgExpr::Not(Box::new(e)))
|
||||||
}
|
}
|
||||||
Some(&Ok(..)) => self.cfg().map(CfgExpr::Value),
|
Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
|
||||||
Some(&Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
|
Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
|
||||||
None => failure::bail!(
|
None => Err(ParseError::new(
|
||||||
"expected start of a cfg expression, \
|
self.t.orig,
|
||||||
found nothing"
|
IncompleteExpr("start of a cfg expression"),
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cfg(&mut self) -> CargoResult<Cfg> {
|
fn cfg(&mut self) -> Result<Cfg, ParseError> {
|
||||||
match self.t.next() {
|
match self.t.next() {
|
||||||
Some(Ok(Token::Ident(name))) => {
|
Some(Ok(Token::Ident(name))) => {
|
||||||
let e = if self.r#try(&Token::Equals) {
|
let e = if self.r#try(&Token::Equals) {
|
||||||
let val = match self.t.next() {
|
let val = match self.t.next() {
|
||||||
Some(Ok(Token::String(s))) => s,
|
Some(Ok(Token::String(s))) => s,
|
||||||
Some(Ok(t)) => failure::bail!("expected a string, found {}", t.classify()),
|
Some(Ok(t)) => {
|
||||||
|
return Err(ParseError::new(
|
||||||
|
self.t.orig,
|
||||||
|
UnexpectedToken {
|
||||||
|
expected: "a string",
|
||||||
|
found: t.classify(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
Some(Err(e)) => return Err(e),
|
Some(Err(e)) => return Err(e),
|
||||||
None => failure::bail!("expected a string, found nothing"),
|
None => {
|
||||||
|
return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Cfg::KeyPair(name.to_string(), val.to_string())
|
Cfg::KeyPair(name.to_string(), val.to_string())
|
||||||
} else {
|
} else {
|
||||||
@ -187,35 +203,66 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
Ok(e)
|
Ok(e)
|
||||||
}
|
}
|
||||||
Some(Ok(t)) => failure::bail!("expected identifier, found {}", t.classify()),
|
Some(Ok(t)) => Err(ParseError::new(
|
||||||
|
self.t.orig,
|
||||||
|
UnexpectedToken {
|
||||||
|
expected: "identifier",
|
||||||
|
found: t.classify(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
Some(Err(e)) => Err(e),
|
Some(Err(e)) => Err(e),
|
||||||
None => failure::bail!("expected identifier, found nothing"),
|
None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Option<Result<Token<'a>, ParseError>> {
|
||||||
|
self.t.clone().next()
|
||||||
|
}
|
||||||
|
|
||||||
fn r#try(&mut self, token: &Token<'a>) -> bool {
|
fn r#try(&mut self, token: &Token<'a>) -> bool {
|
||||||
match self.t.peek() {
|
match self.peek() {
|
||||||
Some(&Ok(ref t)) if token == t => {}
|
Some(Ok(ref t)) if token == t => {}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
self.t.next();
|
self.t.next();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat(&mut self, token: &Token<'a>) -> CargoResult<()> {
|
fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
|
||||||
match self.t.next() {
|
match self.t.next() {
|
||||||
Some(Ok(ref t)) if token == t => Ok(()),
|
Some(Ok(ref t)) if token == t => Ok(()),
|
||||||
Some(Ok(t)) => failure::bail!("expected {}, found {}", token.classify(), t.classify()),
|
Some(Ok(t)) => Err(ParseError::new(
|
||||||
|
self.t.orig,
|
||||||
|
UnexpectedToken {
|
||||||
|
expected: token.classify(),
|
||||||
|
found: t.classify(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
Some(Err(e)) => Err(e),
|
Some(Err(e)) => Err(e),
|
||||||
None => failure::bail!("expected {}, but cfg expr ended", token.classify()),
|
None => Err(ParseError::new(
|
||||||
|
self.t.orig,
|
||||||
|
IncompleteExpr(token.classify()),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the rest of the input from the current location.
|
||||||
|
fn rest(&self) -> Option<&str> {
|
||||||
|
let mut s = self.t.s.clone();
|
||||||
|
loop {
|
||||||
|
match s.next() {
|
||||||
|
Some((_, ' ')) => {}
|
||||||
|
Some((start, _ch)) => return Some(&self.t.orig[start..]),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Tokenizer<'a> {
|
impl<'a> Iterator for Tokenizer<'a> {
|
||||||
type Item = CargoResult<Token<'a>>;
|
type Item = Result<Token<'a>, ParseError>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<CargoResult<Token<'a>>> {
|
fn next(&mut self) -> Option<Result<Token<'a>, ParseError>> {
|
||||||
loop {
|
loop {
|
||||||
match self.s.next() {
|
match self.s.next() {
|
||||||
Some((_, ' ')) => {}
|
Some((_, ' ')) => {}
|
||||||
@ -229,7 +276,7 @@ impl<'a> Iterator for Tokenizer<'a> {
|
|||||||
return Some(Ok(Token::String(&self.orig[start + 1..end])));
|
return Some(Ok(Token::String(&self.orig[start + 1..end])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Some(Err(failure::format_err!("unterminated string in cfg")));
|
return Some(Err(ParseError::new(self.orig, UnterminatedString)));
|
||||||
}
|
}
|
||||||
Some((start, ch)) if is_ident_start(ch) => {
|
Some((start, ch)) if is_ident_start(ch) => {
|
||||||
while let Some(&(end, ch)) = self.s.peek() {
|
while let Some(&(end, ch)) = self.s.peek() {
|
||||||
@ -242,13 +289,7 @@ impl<'a> Iterator for Tokenizer<'a> {
|
|||||||
return Some(Ok(Token::Ident(&self.orig[start..])));
|
return Some(Ok(Token::Ident(&self.orig[start..])));
|
||||||
}
|
}
|
||||||
Some((_, ch)) => {
|
Some((_, ch)) => {
|
||||||
return Some(Err(failure::format_err!(
|
return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
|
||||||
"unexpected character in \
|
|
||||||
cfg `{}`, expected parens, \
|
|
||||||
a comma, an identifier, or \
|
|
||||||
a string",
|
|
||||||
ch
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
None => return None,
|
None => return None,
|
||||||
}
|
}
|
||||||
@ -265,7 +306,7 @@ fn is_ident_rest(ch: char) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Token<'a> {
|
impl<'a> Token<'a> {
|
||||||
fn classify(&self) -> &str {
|
fn classify(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Token::LeftParen => "`(`",
|
Token::LeftParen => "`(`",
|
||||||
Token::RightParen => "`)`",
|
Token::RightParen => "`)`",
|
70
crates/cargo-platform/src/error.rs
Normal file
70
crates/cargo-platform/src/error.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParseError {
|
||||||
|
kind: ParseErrorKind,
|
||||||
|
orig: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ParseErrorKind {
|
||||||
|
UnterminatedString,
|
||||||
|
UnexpectedChar(char),
|
||||||
|
UnexpectedToken {
|
||||||
|
expected: &'static str,
|
||||||
|
found: &'static str,
|
||||||
|
},
|
||||||
|
IncompleteExpr(&'static str),
|
||||||
|
UnterminatedExpression(String),
|
||||||
|
InvalidTarget(String),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"failed to parse `{}` as a cfg expression: {}",
|
||||||
|
self.orig, self.kind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseErrorKind {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use ParseErrorKind::*;
|
||||||
|
match self {
|
||||||
|
UnterminatedString => write!(f, "unterminated string in cfg"),
|
||||||
|
UnexpectedChar(ch) => write!(
|
||||||
|
f,
|
||||||
|
"unexpected character `{}` in cfg, expected parens, a comma, \
|
||||||
|
an identifier, or a string",
|
||||||
|
ch
|
||||||
|
),
|
||||||
|
UnexpectedToken { expected, found } => {
|
||||||
|
write!(f, "expected {}, found {}", expected, found)
|
||||||
|
}
|
||||||
|
IncompleteExpr(expected) => {
|
||||||
|
write!(f, "expected {}, but cfg expression ended", expected)
|
||||||
|
}
|
||||||
|
UnterminatedExpression(s) => {
|
||||||
|
write!(f, "unexpected content `{}` found after cfg expression", s)
|
||||||
|
}
|
||||||
|
InvalidTarget(s) => write!(f, "invalid target specifier: {}", s),
|
||||||
|
__Nonexhaustive => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ParseError {}
|
||||||
|
|
||||||
|
impl ParseError {
|
||||||
|
pub fn new(orig: &str, kind: ParseErrorKind) -> ParseError {
|
||||||
|
ParseError {
|
||||||
|
kind,
|
||||||
|
orig: orig.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
crates/cargo-platform/src/lib.rs
Normal file
106
crates/cargo-platform/src/lib.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//! Platform definition used by Cargo.
|
||||||
|
//!
|
||||||
|
//! This defines a [`Platform`] type which is used in Cargo to specify a target platform.
|
||||||
|
//! There are two kinds, a named target like `x86_64-apple-darwin`, and a "cfg expression"
|
||||||
|
//! like `cfg(any(target_os = "macos", target_os = "ios"))`.
|
||||||
|
//!
|
||||||
|
//! See `examples/matches.rs` for an example of how to match against a `Platform`.
|
||||||
|
//!
|
||||||
|
//! [`Platform`]: enum.Platform.html
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
mod cfg;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub use cfg::{Cfg, CfgExpr};
|
||||||
|
pub use error::{ParseError, ParseErrorKind};
|
||||||
|
|
||||||
|
/// Platform definition.
|
||||||
|
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
||||||
|
pub enum Platform {
|
||||||
|
/// A named platform, like `x86_64-apple-darwin`.
|
||||||
|
Name(String),
|
||||||
|
/// A cfg expression, like `cfg(windows)`.
|
||||||
|
Cfg(CfgExpr),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
match *self {
|
||||||
|
Platform::Name(ref p) => p == name,
|
||||||
|
Platform::Cfg(ref p) => p.matches(cfg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_named_platform(name: &str) -> Result<(), ParseError> {
|
||||||
|
if let Some(ch) = name
|
||||||
|
.chars()
|
||||||
|
.find(|&c| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.'))
|
||||||
|
{
|
||||||
|
if name.chars().any(|c| c == '(') {
|
||||||
|
return Err(ParseError::new(
|
||||||
|
name,
|
||||||
|
ParseErrorKind::InvalidTarget(
|
||||||
|
"unexpected `(` character, cfg expressions must start with `cfg(`"
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return Err(ParseError::new(
|
||||||
|
name,
|
||||||
|
ParseErrorKind::InvalidTarget(format!(
|
||||||
|
"unexpected character {} in target name",
|
||||||
|
ch
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for Platform {
|
||||||
|
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.to_string().serialize(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for Platform {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
FromStr::from_str(&s).map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Platform {
|
||||||
|
type Err = ParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Platform, ParseError> {
|
||||||
|
if s.starts_with("cfg(") && s.ends_with(')') {
|
||||||
|
let s = &s[4..s.len() - 1];
|
||||||
|
s.parse().map(Platform::Cfg)
|
||||||
|
} else {
|
||||||
|
Platform::validate_named_platform(s)?;
|
||||||
|
Ok(Platform::Name(s.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Platform {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Platform::Name(ref n) => n.fmt(f),
|
||||||
|
Platform::Cfg(ref e) => write!(f, "cfg({})", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
178
crates/cargo-platform/tests/test_cfg.rs
Normal file
178
crates/cargo-platform/tests/test_cfg.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
use cargo_platform::{Cfg, CfgExpr, Platform};
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
macro_rules! c {
|
||||||
|
($a:ident) => {
|
||||||
|
Cfg::Name(stringify!($a).to_string())
|
||||||
|
};
|
||||||
|
($a:ident = $e:expr) => {
|
||||||
|
Cfg::KeyPair(stringify!($a).to_string(), $e.to_string())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! e {
|
||||||
|
(any($($t:tt),*)) => (CfgExpr::Any(vec![$(e!($t)),*]));
|
||||||
|
(all($($t:tt),*)) => (CfgExpr::All(vec![$(e!($t)),*]));
|
||||||
|
(not($($t:tt)*)) => (CfgExpr::Not(Box::new(e!($($t)*))));
|
||||||
|
(($($t:tt)*)) => (e!($($t)*));
|
||||||
|
($($t:tt)*) => (CfgExpr::Value(c!($($t)*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn good<T>(s: &str, expected: T)
|
||||||
|
where
|
||||||
|
T: FromStr + PartialEq + fmt::Debug,
|
||||||
|
T::Err: fmt::Display,
|
||||||
|
{
|
||||||
|
let c = match T::from_str(s) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => panic!("failed to parse `{}`: {}", s, e),
|
||||||
|
};
|
||||||
|
assert_eq!(c, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bad<T>(s: &str, err: &str)
|
||||||
|
where
|
||||||
|
T: FromStr + fmt::Display,
|
||||||
|
T::Err: fmt::Display,
|
||||||
|
{
|
||||||
|
let e = match T::from_str(s) {
|
||||||
|
Ok(cfg) => panic!("expected `{}` to not parse but got {}", s, cfg),
|
||||||
|
Err(e) => e.to_string(),
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
e.contains(err),
|
||||||
|
"when parsing `{}`,\n\"{}\" not contained \
|
||||||
|
inside: {}",
|
||||||
|
s,
|
||||||
|
err,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_syntax() {
|
||||||
|
good("foo", c!(foo));
|
||||||
|
good("_bar", c!(_bar));
|
||||||
|
good(" foo", c!(foo));
|
||||||
|
good(" foo ", c!(foo));
|
||||||
|
good(" foo = \"bar\"", c!(foo = "bar"));
|
||||||
|
good("foo=\"\"", c!(foo = ""));
|
||||||
|
good(" foo=\"3\" ", c!(foo = "3"));
|
||||||
|
good("foo = \"3 e\"", c!(foo = "3 e"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_syntax_bad() {
|
||||||
|
bad::<Cfg>("", "but cfg expression ended");
|
||||||
|
bad::<Cfg>(" ", "but cfg expression ended");
|
||||||
|
bad::<Cfg>("\t", "unexpected character");
|
||||||
|
bad::<Cfg>("7", "unexpected character");
|
||||||
|
bad::<Cfg>("=", "expected identifier");
|
||||||
|
bad::<Cfg>(",", "expected identifier");
|
||||||
|
bad::<Cfg>("(", "expected identifier");
|
||||||
|
bad::<Cfg>("foo (", "unexpected content `(` found after cfg expression");
|
||||||
|
bad::<Cfg>("bar =", "expected a string");
|
||||||
|
bad::<Cfg>("bar = \"", "unterminated string");
|
||||||
|
bad::<Cfg>(
|
||||||
|
"foo, bar",
|
||||||
|
"unexpected content `, bar` found after cfg expression",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_expr() {
|
||||||
|
good("foo", e!(foo));
|
||||||
|
good("_bar", e!(_bar));
|
||||||
|
good(" foo", e!(foo));
|
||||||
|
good(" foo ", e!(foo));
|
||||||
|
good(" foo = \"bar\"", e!(foo = "bar"));
|
||||||
|
good("foo=\"\"", e!(foo = ""));
|
||||||
|
good(" foo=\"3\" ", e!(foo = "3"));
|
||||||
|
good("foo = \"3 e\"", e!(foo = "3 e"));
|
||||||
|
|
||||||
|
good("all()", e!(all()));
|
||||||
|
good("all(a)", e!(all(a)));
|
||||||
|
good("all(a, b)", e!(all(a, b)));
|
||||||
|
good("all(a, )", e!(all(a)));
|
||||||
|
good("not(a = \"b\")", e!(not(a = "b")));
|
||||||
|
good("not(all(a))", e!(not(all(a))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_expr_bad() {
|
||||||
|
bad::<CfgExpr>(" ", "but cfg expression ended");
|
||||||
|
bad::<CfgExpr>(" all", "expected `(`");
|
||||||
|
bad::<CfgExpr>("all(a", "expected `)`");
|
||||||
|
bad::<CfgExpr>("not", "expected `(`");
|
||||||
|
bad::<CfgExpr>("not(a", "expected `)`");
|
||||||
|
bad::<CfgExpr>("a = ", "expected a string");
|
||||||
|
bad::<CfgExpr>("all(not())", "expected identifier");
|
||||||
|
bad::<CfgExpr>(
|
||||||
|
"foo(a)",
|
||||||
|
"unexpected content `(a)` found after cfg expression",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_matches() {
|
||||||
|
assert!(e!(foo).matches(&[c!(bar), c!(foo), c!(baz)]));
|
||||||
|
assert!(e!(any(foo)).matches(&[c!(bar), c!(foo), c!(baz)]));
|
||||||
|
assert!(e!(any(foo, bar)).matches(&[c!(bar)]));
|
||||||
|
assert!(e!(any(foo, bar)).matches(&[c!(foo)]));
|
||||||
|
assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)]));
|
||||||
|
assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)]));
|
||||||
|
assert!(e!(not(foo)).matches(&[c!(bar)]));
|
||||||
|
assert!(e!(not(foo)).matches(&[]));
|
||||||
|
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(bar)]));
|
||||||
|
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo), c!(bar)]));
|
||||||
|
|
||||||
|
assert!(!e!(foo).matches(&[]));
|
||||||
|
assert!(!e!(foo).matches(&[c!(bar)]));
|
||||||
|
assert!(!e!(foo).matches(&[c!(fo)]));
|
||||||
|
assert!(!e!(any(foo)).matches(&[]));
|
||||||
|
assert!(!e!(any(foo)).matches(&[c!(bar)]));
|
||||||
|
assert!(!e!(any(foo)).matches(&[c!(bar), c!(baz)]));
|
||||||
|
assert!(!e!(all(foo)).matches(&[c!(bar), c!(baz)]));
|
||||||
|
assert!(!e!(all(foo, bar)).matches(&[c!(bar)]));
|
||||||
|
assert!(!e!(all(foo, bar)).matches(&[c!(foo)]));
|
||||||
|
assert!(!e!(all(foo, bar)).matches(&[]));
|
||||||
|
assert!(!e!(not(bar)).matches(&[c!(bar)]));
|
||||||
|
assert!(!e!(not(bar)).matches(&[c!(baz), c!(bar)]));
|
||||||
|
assert!(!e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_target_name() {
|
||||||
|
bad::<Platform>(
|
||||||
|
"any(cfg(unix), cfg(windows))",
|
||||||
|
"failed to parse `any(cfg(unix), cfg(windows))` as a cfg expression: \
|
||||||
|
invalid target specifier: unexpected `(` character, \
|
||||||
|
cfg expressions must start with `cfg(`",
|
||||||
|
);
|
||||||
|
bad::<Platform>(
|
||||||
|
"!foo",
|
||||||
|
"failed to parse `!foo` as a cfg expression: \
|
||||||
|
invalid target specifier: unexpected character ! in target name",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip_platform() {
|
||||||
|
fn rt(s: &str) {
|
||||||
|
let p = Platform::from_str(s).unwrap();
|
||||||
|
let s2 = p.to_string();
|
||||||
|
let p2 = Platform::from_str(&s2).unwrap();
|
||||||
|
assert_eq!(p, p2);
|
||||||
|
}
|
||||||
|
rt("x86_64-apple-darwin");
|
||||||
|
rt("foo");
|
||||||
|
rt("cfg(windows)");
|
||||||
|
rt("cfg(target_os = \"windows\")");
|
||||||
|
rt(
|
||||||
|
"cfg(any(all(any(target_os = \"android\", target_os = \"linux\"), \
|
||||||
|
any(target_arch = \"aarch64\", target_arch = \"arm\", target_arch = \"powerpc64\", \
|
||||||
|
target_arch = \"x86\", target_arch = \"x86_64\")), \
|
||||||
|
all(target_os = \"freebsd\", target_arch = \"x86_64\")))",
|
||||||
|
);
|
||||||
|
}
|
50
publish.py
Executable file
50
publish.py
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# This script is used to publish Cargo to crates.io.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import urllib.request
|
||||||
|
from urllib.error import HTTPError
|
||||||
|
|
||||||
|
|
||||||
|
TO_PUBLISH = [
|
||||||
|
'crates/cargo-platform',
|
||||||
|
'crates/crates-io',
|
||||||
|
'.',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def already_published(name, version):
|
||||||
|
try:
|
||||||
|
urllib.request.urlopen('https://crates.io/api/v1/crates/%s/%s/download' % (name, version))
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.code == 404:
|
||||||
|
return False
|
||||||
|
raise
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_publish(path):
|
||||||
|
content = open(os.path.join(path, 'Cargo.toml')).read()
|
||||||
|
name = re.search('^name = "([^"]+)"', content, re.M).group(1)
|
||||||
|
version = re.search('^version = "([^"]+)"', content, re.M).group(1)
|
||||||
|
if already_published(name, version):
|
||||||
|
print('%s %s is already published, skipping' % (name, version))
|
||||||
|
return
|
||||||
|
subprocess.check_call(['cargo', 'publish', '--no-verify'], cwd=path)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print('Doing dry run first...')
|
||||||
|
for path in TO_PUBLISH:
|
||||||
|
subprocess.check_call(['cargo', 'publish', '--no-verify', '--dry-run'], cwd=path)
|
||||||
|
print('Starting publish...')
|
||||||
|
for path in TO_PUBLISH:
|
||||||
|
maybe_publish(path)
|
||||||
|
print('Publish complete!')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use cargo_platform::Cfg;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::core::compiler::unit::UnitInterner;
|
use crate::core::compiler::unit::UnitInterner;
|
||||||
@ -10,7 +11,7 @@ use crate::core::profiles::Profiles;
|
|||||||
use crate::core::{Dependency, Workspace};
|
use crate::core::{Dependency, Workspace};
|
||||||
use crate::core::{PackageId, PackageSet};
|
use crate::core::{PackageId, PackageSet};
|
||||||
use crate::util::errors::CargoResult;
|
use crate::util::errors::CargoResult;
|
||||||
use crate::util::{profile, Cfg, Config, Rustc};
|
use crate::util::{profile, Config, Rustc};
|
||||||
|
|
||||||
mod target_info;
|
mod target_info;
|
||||||
pub use self::target_info::{FileFlavor, TargetInfo};
|
pub use self::target_info::{FileFlavor, TargetInfo};
|
||||||
|
@ -6,8 +6,8 @@ use std::str::{self, FromStr};
|
|||||||
|
|
||||||
use crate::core::compiler::Kind;
|
use crate::core::compiler::Kind;
|
||||||
use crate::core::TargetKind;
|
use crate::core::TargetKind;
|
||||||
use crate::util::CfgExpr;
|
use crate::util::{CargoResult, CargoResultExt, Config, ProcessBuilder, Rustc};
|
||||||
use crate::util::{CargoResult, CargoResultExt, Cfg, Config, ProcessBuilder, Rustc};
|
use cargo_platform::{Cfg, CfgExpr};
|
||||||
|
|
||||||
/// Information about the platform target gleaned from querying rustc.
|
/// Information about the platform target gleaned from querying rustc.
|
||||||
///
|
///
|
||||||
@ -171,7 +171,7 @@ impl TargetInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cfg = lines
|
let cfg = lines
|
||||||
.map(Cfg::from_str)
|
.map(|line| Ok(Cfg::from_str(line)?))
|
||||||
.collect::<CargoResult<Vec<_>>>()
|
.collect::<CargoResult<Vec<_>>>()
|
||||||
.chain_err(|| {
|
.chain_err(|| {
|
||||||
format!(
|
format!(
|
||||||
|
@ -3,13 +3,12 @@ use std::env;
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use cargo_platform::CfgExpr;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
use super::BuildContext;
|
use super::BuildContext;
|
||||||
use crate::core::{Edition, InternedString, Package, PackageId, Target};
|
use crate::core::{Edition, InternedString, Package, PackageId, Target};
|
||||||
use crate::util::{
|
use crate::util::{self, join_paths, process, rustc::Rustc, CargoResult, Config, ProcessBuilder};
|
||||||
self, join_paths, process, rustc::Rustc, CargoResult, CfgExpr, Config, ProcessBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Doctest {
|
pub struct Doctest {
|
||||||
/// The package being doc-tested.
|
/// The package being doc-tested.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use cargo_platform::Cfg;
|
||||||
use std::collections::hash_map::{Entry, HashMap};
|
use std::collections::hash_map::{Entry, HashMap};
|
||||||
use std::collections::{BTreeSet, HashSet};
|
use std::collections::{BTreeSet, HashSet};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -8,7 +9,6 @@ use crate::core::compiler::job_queue::JobState;
|
|||||||
use crate::core::PackageId;
|
use crate::core::PackageId;
|
||||||
use crate::util::errors::{CargoResult, CargoResultExt};
|
use crate::util::errors::{CargoResult, CargoResultExt};
|
||||||
use crate::util::machine_message::{self, Message};
|
use crate::util::machine_message::{self, Message};
|
||||||
use crate::util::Cfg;
|
|
||||||
use crate::util::{self, internal, paths, profile};
|
use crate::util::{self, internal, paths, profile};
|
||||||
|
|
||||||
use super::job::{Freshness, Job, Work};
|
use super::job::{Freshness, Job, Work};
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
use std::fmt;
|
use cargo_platform::Platform;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use semver::ReqParseError;
|
use semver::ReqParseError;
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde::ser;
|
use serde::ser;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::core::interning::InternedString;
|
use crate::core::interning::InternedString;
|
||||||
use crate::core::{PackageId, SourceId, Summary};
|
use crate::core::{PackageId, SourceId, Summary};
|
||||||
use crate::util::errors::{CargoResult, CargoResultExt};
|
use crate::util::errors::{CargoResult, CargoResultExt};
|
||||||
use crate::util::{Cfg, CfgExpr, Config};
|
use crate::util::Config;
|
||||||
|
|
||||||
/// Information about a dependency requested by a Cargo manifest.
|
/// Information about a dependency requested by a Cargo manifest.
|
||||||
/// Cheap to copy.
|
/// Cheap to copy.
|
||||||
@ -48,12 +46,6 @@ struct Inner {
|
|||||||
platform: Option<Platform>,
|
platform: Option<Platform>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
|
|
||||||
pub enum Platform {
|
|
||||||
Name(String),
|
|
||||||
Cfg(CfgExpr),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct SerializedDependency<'a> {
|
struct SerializedDependency<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
@ -459,46 +451,3 @@ impl Dependency {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Platform {
|
|
||||||
pub fn matches(&self, name: &str, cfg: &[Cfg]) -> bool {
|
|
||||||
match *self {
|
|
||||||
Platform::Name(ref p) => p == name,
|
|
||||||
Platform::Cfg(ref p) => p.matches(cfg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ser::Serialize for Platform {
|
|
||||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: ser::Serializer,
|
|
||||||
{
|
|
||||||
self.to_string().serialize(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Platform {
|
|
||||||
type Err = failure::Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> CargoResult<Platform> {
|
|
||||||
if s.starts_with("cfg(") && s.ends_with(')') {
|
|
||||||
let s = &s[4..s.len() - 1];
|
|
||||||
let p = s.parse().map(Platform::Cfg).chain_err(|| {
|
|
||||||
failure::format_err!("failed to parse `{}` as a cfg expression", s)
|
|
||||||
})?;
|
|
||||||
Ok(p)
|
|
||||||
} else {
|
|
||||||
Ok(Platform::Name(s.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Platform {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Platform::Name(ref n) => n.fmt(f),
|
|
||||||
Platform::Cfg(ref e) => write!(f, "cfg({})", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub use self::canonical_url::CanonicalUrl;
|
pub use self::canonical_url::CanonicalUrl;
|
||||||
pub use self::cfg::{Cfg, CfgExpr};
|
|
||||||
pub use self::config::{homedir, Config, ConfigValue};
|
pub use self::config::{homedir, Config, ConfigValue};
|
||||||
pub use self::dependency_queue::DependencyQueue;
|
pub use self::dependency_queue::DependencyQueue;
|
||||||
pub use self::diagnostic_server::RustfixDiagnosticServer;
|
pub use self::diagnostic_server::RustfixDiagnosticServer;
|
||||||
@ -30,7 +29,6 @@ pub use self::workspace::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod canonical_url;
|
mod canonical_url;
|
||||||
mod cfg;
|
|
||||||
pub mod command_prelude;
|
pub mod command_prelude;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
|
@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use cargo_platform::Platform;
|
||||||
use failure::bail;
|
use failure::bail;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use semver::{self, VersionReq};
|
use semver::{self, VersionReq};
|
||||||
@ -13,7 +14,7 @@ use serde::ser;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::core::dependency::{Kind, Platform};
|
use crate::core::dependency::Kind;
|
||||||
use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
|
use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
|
||||||
use crate::core::profiles::Profiles;
|
use crate::core::profiles::Profiles;
|
||||||
use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
|
use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
|
||||||
|
@ -1,145 +1,7 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use cargo::util::{Cfg, CfgExpr};
|
|
||||||
use cargo_test_support::registry::Package;
|
use cargo_test_support::registry::Package;
|
||||||
use cargo_test_support::rustc_host;
|
use cargo_test_support::rustc_host;
|
||||||
use cargo_test_support::{basic_manifest, project};
|
use cargo_test_support::{basic_manifest, project};
|
||||||
|
|
||||||
macro_rules! c {
|
|
||||||
($a:ident) => {
|
|
||||||
Cfg::Name(stringify!($a).to_string())
|
|
||||||
};
|
|
||||||
($a:ident = $e:expr) => {
|
|
||||||
Cfg::KeyPair(stringify!($a).to_string(), $e.to_string())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! e {
|
|
||||||
(any($($t:tt),*)) => (CfgExpr::Any(vec![$(e!($t)),*]));
|
|
||||||
(all($($t:tt),*)) => (CfgExpr::All(vec![$(e!($t)),*]));
|
|
||||||
(not($($t:tt)*)) => (CfgExpr::Not(Box::new(e!($($t)*))));
|
|
||||||
(($($t:tt)*)) => (e!($($t)*));
|
|
||||||
($($t:tt)*) => (CfgExpr::Value(c!($($t)*)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn good<T>(s: &str, expected: T)
|
|
||||||
where
|
|
||||||
T: FromStr + PartialEq + fmt::Debug,
|
|
||||||
T::Err: fmt::Display,
|
|
||||||
{
|
|
||||||
let c = match T::from_str(s) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => panic!("failed to parse `{}`: {}", s, e),
|
|
||||||
};
|
|
||||||
assert_eq!(c, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bad<T>(s: &str, err: &str)
|
|
||||||
where
|
|
||||||
T: FromStr + fmt::Display,
|
|
||||||
T::Err: fmt::Display,
|
|
||||||
{
|
|
||||||
let e = match T::from_str(s) {
|
|
||||||
Ok(cfg) => panic!("expected `{}` to not parse but got {}", s, cfg),
|
|
||||||
Err(e) => e.to_string(),
|
|
||||||
};
|
|
||||||
assert!(
|
|
||||||
e.contains(err),
|
|
||||||
"when parsing `{}`,\n\"{}\" not contained \
|
|
||||||
inside: {}",
|
|
||||||
s,
|
|
||||||
err,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn cfg_syntax() {
|
|
||||||
good("foo", c!(foo));
|
|
||||||
good("_bar", c!(_bar));
|
|
||||||
good(" foo", c!(foo));
|
|
||||||
good(" foo ", c!(foo));
|
|
||||||
good(" foo = \"bar\"", c!(foo = "bar"));
|
|
||||||
good("foo=\"\"", c!(foo = ""));
|
|
||||||
good(" foo=\"3\" ", c!(foo = "3"));
|
|
||||||
good("foo = \"3 e\"", c!(foo = "3 e"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn cfg_syntax_bad() {
|
|
||||||
bad::<Cfg>("", "found nothing");
|
|
||||||
bad::<Cfg>(" ", "found nothing");
|
|
||||||
bad::<Cfg>("\t", "unexpected character");
|
|
||||||
bad::<Cfg>("7", "unexpected character");
|
|
||||||
bad::<Cfg>("=", "expected identifier");
|
|
||||||
bad::<Cfg>(",", "expected identifier");
|
|
||||||
bad::<Cfg>("(", "expected identifier");
|
|
||||||
bad::<Cfg>("foo (", "malformed cfg value");
|
|
||||||
bad::<Cfg>("bar =", "expected a string");
|
|
||||||
bad::<Cfg>("bar = \"", "unterminated string");
|
|
||||||
bad::<Cfg>("foo, bar", "malformed cfg value");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn cfg_expr() {
|
|
||||||
good("foo", e!(foo));
|
|
||||||
good("_bar", e!(_bar));
|
|
||||||
good(" foo", e!(foo));
|
|
||||||
good(" foo ", e!(foo));
|
|
||||||
good(" foo = \"bar\"", e!(foo = "bar"));
|
|
||||||
good("foo=\"\"", e!(foo = ""));
|
|
||||||
good(" foo=\"3\" ", e!(foo = "3"));
|
|
||||||
good("foo = \"3 e\"", e!(foo = "3 e"));
|
|
||||||
|
|
||||||
good("all()", e!(all()));
|
|
||||||
good("all(a)", e!(all(a)));
|
|
||||||
good("all(a, b)", e!(all(a, b)));
|
|
||||||
good("all(a, )", e!(all(a)));
|
|
||||||
good("not(a = \"b\")", e!(not(a = "b")));
|
|
||||||
good("not(all(a))", e!(not(all(a))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn cfg_expr_bad() {
|
|
||||||
bad::<CfgExpr>(" ", "found nothing");
|
|
||||||
bad::<CfgExpr>(" all", "expected `(`");
|
|
||||||
bad::<CfgExpr>("all(a", "expected `)`");
|
|
||||||
bad::<CfgExpr>("not", "expected `(`");
|
|
||||||
bad::<CfgExpr>("not(a", "expected `)`");
|
|
||||||
bad::<CfgExpr>("a = ", "expected a string");
|
|
||||||
bad::<CfgExpr>("all(not())", "expected identifier");
|
|
||||||
bad::<CfgExpr>("foo(a)", "consider using all() or any() explicitly");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn cfg_matches() {
|
|
||||||
assert!(e!(foo).matches(&[c!(bar), c!(foo), c!(baz)]));
|
|
||||||
assert!(e!(any(foo)).matches(&[c!(bar), c!(foo), c!(baz)]));
|
|
||||||
assert!(e!(any(foo, bar)).matches(&[c!(bar)]));
|
|
||||||
assert!(e!(any(foo, bar)).matches(&[c!(foo)]));
|
|
||||||
assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)]));
|
|
||||||
assert!(e!(all(foo, bar)).matches(&[c!(foo), c!(bar)]));
|
|
||||||
assert!(e!(not(foo)).matches(&[c!(bar)]));
|
|
||||||
assert!(e!(not(foo)).matches(&[]));
|
|
||||||
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(bar)]));
|
|
||||||
assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo), c!(bar)]));
|
|
||||||
|
|
||||||
assert!(!e!(foo).matches(&[]));
|
|
||||||
assert!(!e!(foo).matches(&[c!(bar)]));
|
|
||||||
assert!(!e!(foo).matches(&[c!(fo)]));
|
|
||||||
assert!(!e!(any(foo)).matches(&[]));
|
|
||||||
assert!(!e!(any(foo)).matches(&[c!(bar)]));
|
|
||||||
assert!(!e!(any(foo)).matches(&[c!(bar), c!(baz)]));
|
|
||||||
assert!(!e!(all(foo)).matches(&[c!(bar), c!(baz)]));
|
|
||||||
assert!(!e!(all(foo, bar)).matches(&[c!(bar)]));
|
|
||||||
assert!(!e!(all(foo, bar)).matches(&[c!(foo)]));
|
|
||||||
assert!(!e!(all(foo, bar)).matches(&[]));
|
|
||||||
assert!(!e!(not(bar)).matches(&[c!(bar)]));
|
|
||||||
assert!(!e!(not(bar)).matches(&[c!(baz), c!(bar)]));
|
|
||||||
assert!(!e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo)]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn cfg_easy() {
|
fn cfg_easy() {
|
||||||
let p = project()
|
let p = project()
|
||||||
@ -311,10 +173,7 @@ fn bad_target_spec() {
|
|||||||
[ERROR] failed to parse manifest at `[..]`
|
[ERROR] failed to parse manifest at `[..]`
|
||||||
|
|
||||||
Caused by:
|
Caused by:
|
||||||
failed to parse `4` as a cfg expression
|
failed to parse `4` as a cfg expression: unexpected character `4` in cfg, [..]
|
||||||
|
|
||||||
Caused by:
|
|
||||||
unexpected character in cfg `4`, [..]
|
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
@ -345,10 +204,7 @@ fn bad_target_spec2() {
|
|||||||
[ERROR] failed to parse manifest at `[..]`
|
[ERROR] failed to parse manifest at `[..]`
|
||||||
|
|
||||||
Caused by:
|
Caused by:
|
||||||
failed to parse `bar =` as a cfg expression
|
failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended
|
||||||
|
|
||||||
Caused by:
|
|
||||||
expected a string, found nothing
|
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
@ -576,7 +432,8 @@ command was: `[..]compiler[..]--crate-type [..]`
|
|||||||
|
|
||||||
|
|
||||||
Caused by:
|
Caused by:
|
||||||
unexpected character in cfg `1`, expected parens, a comma, an identifier, or a string
|
failed to parse `123` as a cfg expression: unexpected character `1` in cfg, \
|
||||||
|
expected parens, a comma, an identifier, or a string
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user