Extract Platform to a separate crate.

This commit is contained in:
Eric Huss 2019-09-05 09:02:27 -07:00
parent cdf7f6380a
commit 57c96c194a
18 changed files with 589 additions and 265 deletions

View File

@ -21,6 +21,7 @@ path = "src/cargo/lib.rs"
[dependencies]
atty = "0.2"
bytesize = "1.0"
cargo-platform = { path = "crates/cargo-platform", version = "0.1" }
crates-io = { path = "crates/crates-io", version = "0.28" }
crossbeam-utils = "0.6"
crypto-hash = "0.3.1"

View File

@ -61,6 +61,8 @@ jobs:
displayName: "Check rustfmt (crates-io)"
- bash: cd crates/resolver-tests && cargo fmt --all -- --check
displayName: "Check rustfmt (resolver-tests)"
- bash: cd crates/cargo-platform && cargo fmt --all -- --check
displayName: "Check rustfmt (cargo-platform)"
variables:
TOOLCHAIN: stable

View File

@ -29,3 +29,6 @@ steps:
- bash: 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"

View 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'] }

View 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()
}

View File

@ -1,15 +1,9 @@
use crate::error::{ParseError, ParseErrorKind::*};
use std::fmt;
use std::iter;
use std::str::{self, FromStr};
use crate::util::CargoResult;
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum Cfg {
Name(String),
KeyPair(String, String),
}
/// A cfg expression.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum CfgExpr {
Not(Box<CfgExpr>),
@ -18,6 +12,15 @@ pub enum CfgExpr {
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)]
enum Token<'a> {
LeftParen,
@ -28,23 +31,27 @@ enum Token<'a> {
String(&'a str),
}
#[derive(Clone)]
struct Tokenizer<'a> {
s: iter::Peekable<str::CharIndices<'a>>,
orig: &'a str,
}
struct Parser<'a> {
t: iter::Peekable<Tokenizer<'a>>,
t: Tokenizer<'a>,
}
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 e = p.cfg()?;
if p.t.next().is_some() {
failure::bail!("malformed cfg value or key/value pair: `{}`", s)
if let Some(rest) = p.rest() {
return Err(ParseError::new(
p.t.orig,
UnterminatedExpression(rest.to_string()),
));
}
Ok(e)
}
@ -85,16 +92,16 @@ impl 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 e = p.expr()?;
if p.t.next().is_some() {
failure::bail!(
"can only have one cfg-expression, consider using all() or \
any() explicitly"
)
if let Some(rest) = p.rest() {
return Err(ParseError::new(
p.t.orig,
UnterminatedExpression(rest.to_string()),
));
}
Ok(e)
}
@ -131,14 +138,13 @@ impl<'a> Parser<'a> {
t: Tokenizer {
s: s.char_indices().peekable(),
orig: s,
}
.peekable(),
},
}
}
fn expr(&mut self) -> CargoResult<CfgExpr> {
match self.t.peek() {
Some(&Ok(Token::Ident(op @ "all"))) | Some(&Ok(Token::Ident(op @ "any"))) => {
fn expr(&mut self) -> Result<CfgExpr, ParseError> {
match self.peek() {
Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
self.t.next();
let mut e = Vec::new();
self.eat(&Token::LeftParen)?;
@ -155,31 +161,41 @@ impl<'a> Parser<'a> {
Ok(CfgExpr::Any(e))
}
}
Some(&Ok(Token::Ident("not"))) => {
Some(Ok(Token::Ident("not"))) => {
self.t.next();
self.eat(&Token::LeftParen)?;
let e = self.expr()?;
self.eat(&Token::RightParen)?;
Ok(CfgExpr::Not(Box::new(e)))
}
Some(&Ok(..)) => self.cfg().map(CfgExpr::Value),
Some(&Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
None => failure::bail!(
"expected start of a cfg expression, \
found nothing"
),
Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
None => Err(ParseError::new(
self.t.orig,
IncompleteExpr("start of a cfg expression"),
)),
}
}
fn cfg(&mut self) -> CargoResult<Cfg> {
fn cfg(&mut self) -> Result<Cfg, ParseError> {
match self.t.next() {
Some(Ok(Token::Ident(name))) => {
let e = if self.r#try(&Token::Equals) {
let val = match self.t.next() {
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),
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())
} else {
@ -187,35 +203,66 @@ impl<'a> Parser<'a> {
};
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),
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 {
match self.t.peek() {
Some(&Ok(ref t)) if token == t => {}
match self.peek() {
Some(Ok(ref t)) if token == t => {}
_ => return false,
}
self.t.next();
true
}
fn eat(&mut self, token: &Token<'a>) -> CargoResult<()> {
fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
match self.t.next() {
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),
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> {
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 {
match self.s.next() {
Some((_, ' ')) => {}
@ -229,7 +276,7 @@ impl<'a> Iterator for Tokenizer<'a> {
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) => {
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..])));
}
Some((_, ch)) => {
return Some(Err(failure::format_err!(
"unexpected character in \
cfg `{}`, expected parens, \
a comma, an identifier, or \
a string",
ch
)));
return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
}
None => return None,
}
@ -265,7 +306,7 @@ fn is_ident_rest(ch: char) -> bool {
}
impl<'a> Token<'a> {
fn classify(&self) -> &str {
fn classify(&self) -> &'static str {
match *self {
Token::LeftParen => "`(`",
Token::RightParen => "`)`",

View 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(),
}
}
}

View 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),
}
}
}

View 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
View 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()

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::str;
use cargo_platform::Cfg;
use log::debug;
use crate::core::compiler::unit::UnitInterner;
@ -10,7 +11,7 @@ use crate::core::profiles::Profiles;
use crate::core::{Dependency, Workspace};
use crate::core::{PackageId, PackageSet};
use crate::util::errors::CargoResult;
use crate::util::{profile, Cfg, Config, Rustc};
use crate::util::{profile, Config, Rustc};
mod target_info;
pub use self::target_info::{FileFlavor, TargetInfo};

View File

@ -6,8 +6,8 @@ use std::str::{self, FromStr};
use crate::core::compiler::Kind;
use crate::core::TargetKind;
use crate::util::CfgExpr;
use crate::util::{CargoResult, CargoResultExt, Cfg, Config, ProcessBuilder, Rustc};
use crate::util::{CargoResult, CargoResultExt, Config, ProcessBuilder, Rustc};
use cargo_platform::{Cfg, CfgExpr};
/// Information about the platform target gleaned from querying rustc.
///
@ -171,7 +171,7 @@ impl TargetInfo {
};
let cfg = lines
.map(Cfg::from_str)
.map(|line| Ok(Cfg::from_str(line)?))
.collect::<CargoResult<Vec<_>>>()
.chain_err(|| {
format!(

View File

@ -3,13 +3,12 @@ use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use cargo_platform::CfgExpr;
use semver::Version;
use super::BuildContext;
use crate::core::{Edition, InternedString, Package, PackageId, Target};
use crate::util::{
self, join_paths, process, rustc::Rustc, CargoResult, CfgExpr, Config, ProcessBuilder,
};
use crate::util::{self, join_paths, process, rustc::Rustc, CargoResult, Config, ProcessBuilder};
pub struct Doctest {
/// The package being doc-tested.

View File

@ -1,3 +1,4 @@
use cargo_platform::Cfg;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::{BTreeSet, HashSet};
use std::path::{Path, PathBuf};
@ -8,7 +9,6 @@ use crate::core::compiler::job_queue::JobState;
use crate::core::PackageId;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::machine_message::{self, Message};
use crate::util::Cfg;
use crate::util::{self, internal, paths, profile};
use super::job::{Freshness, Job, Work};

View File

@ -1,17 +1,15 @@
use std::fmt;
use std::rc::Rc;
use std::str::FromStr;
use cargo_platform::Platform;
use log::trace;
use semver::ReqParseError;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
use std::rc::Rc;
use crate::core::interning::InternedString;
use crate::core::{PackageId, SourceId, Summary};
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.
/// Cheap to copy.
@ -48,12 +46,6 @@ struct Inner {
platform: Option<Platform>,
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
pub enum Platform {
Name(String),
Cfg(CfgExpr),
}
#[derive(Serialize)]
struct SerializedDependency<'a> {
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),
}
}
}

View File

@ -1,7 +1,6 @@
use std::time::Duration;
pub use self::canonical_url::CanonicalUrl;
pub use self::cfg::{Cfg, CfgExpr};
pub use self::config::{homedir, Config, ConfigValue};
pub use self::dependency_queue::DependencyQueue;
pub use self::diagnostic_server::RustfixDiagnosticServer;
@ -30,7 +29,6 @@ pub use self::workspace::{
};
mod canonical_url;
mod cfg;
pub mod command_prelude;
pub mod config;
mod dependency_queue;

View File

@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str;
use cargo_platform::Platform;
use failure::bail;
use log::{debug, trace};
use semver::{self, VersionReq};
@ -13,7 +14,7 @@ use serde::ser;
use serde::{Deserialize, Serialize};
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::profiles::Profiles;
use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};

View File

@ -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::rustc_host;
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]
fn cfg_easy() {
let p = project()
@ -311,10 +173,7 @@ fn bad_target_spec() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
failed to parse `4` as a cfg expression
Caused by:
unexpected character in cfg `4`, [..]
failed to parse `4` as a cfg expression: unexpected character `4` in cfg, [..]
",
)
.run();
@ -345,10 +204,7 @@ fn bad_target_spec2() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
failed to parse `bar =` as a cfg expression
Caused by:
expected a string, found nothing
failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended
",
)
.run();
@ -576,7 +432,8 @@ command was: `[..]compiler[..]--crate-type [..]`
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();