extern crate serde_json;
extern crate phf;
use std::env;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::ffi::OsString;
use std::borrow::Cow;
use std::borrow::Cow::Borrowed as B;
pub mod changelog;
#[derive(Debug)]
pub enum Error {
TargetUnset,
TargetNotFound,
InvalidSpec,
Io(::std::io::Error)
}
impl ::std::fmt::Display for Error {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Error::Io(ref e) => <::std::io::Error as ::std::fmt::Display>::fmt(e, fmt),
ref e => fmt.write_str(std::error::Error::description(e)),
}
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::TargetUnset => "TARGET environment variable is not set or is not valid utf-8",
Error::TargetNotFound => "The requested target was not found",
Error::InvalidSpec => "Custom target JSON file was not valid",
Error::Io(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&::std::error::Error> {
match *self {
Error::Io(ref e) => Some(e),
_ => None
}
}
}
include!(concat!(env!("OUT_DIR"), "/builtins.rs"));
#[derive(Clone, Debug)]
pub struct TargetInfo {
arch: Cow<'static, str>,
vendor: Cow<'static, str>,
os: Cow<'static, str>,
env: Cow<'static, str>,
endian: Cow<'static, str>,
pointer_width: Cow<'static, str>,
}
impl TargetInfo {
pub fn new() -> Result<TargetInfo, Error> {
env::var("TARGET").map_err(|_| Error::TargetUnset).and_then(|s| TargetInfo::from_str(&s))
}
pub fn from_str(s: &str) -> Result<TargetInfo, Error> {
fn load_json(path: &Path) -> Result<TargetInfo, Error> {
use serde_json as s;
let f = try!(File::open(path).map_err(|e| Error::Io(e)));
let json: s::Value = try!(s::from_reader(f).map_err(|e| match e {
s::Error::Io(e) => Error::Io(e),
_ => Error::InvalidSpec,
}));
let req = |name: &str|
json.find(name).and_then(|a| a.as_str()).ok_or(Error::InvalidSpec);
Ok(TargetInfo {
arch: Cow::Owned(try!(req("arch")).into()),
os: Cow::Owned(try!(req("os")).into()),
vendor: Cow::Owned(
json.find("vendor").and_then(|s| s.as_str()).unwrap_or("unknown").into()
),
env: Cow::Owned(json.find("env").and_then(|s| s.as_str()).unwrap_or("").into()),
endian: Cow::Owned(try!(req("target-endian")).into()),
pointer_width: Cow::Owned(try!(req("target-pointer-width")).into()),
})
}
if let Some(t) = TargetInfo::load_specific(s) {
return Ok(t);
}
let path = Path::new(s);
if path.is_file() {
return load_json(&path);
}
let path = {
let mut target = String::from(s);
target.push_str(".json");
PathBuf::from(target)
};
let target_path = env::var_os("RUST_TARGET_PATH")
.unwrap_or(OsString::new());
for dir in env::split_paths(&target_path) {
let p = dir.join(&path);
if p.is_file() {
return load_json(&p);
}
}
Err(Error::TargetNotFound)
}
fn load_specific(s: &str) -> Option<TargetInfo> {
BUILTINS.get(s).cloned()
}
}
impl TargetInfo {
pub fn target_arch(&self) -> &str {
&*self.arch
}
pub fn target_vendor(&self) -> &str {
&*self.vendor
}
pub fn target_os(&self) -> &str {
&*self.os
}
pub fn target_env(&self) -> &str {
&*self.env
}
pub fn target_endian(&self) -> &str {
&*self.endian
}
pub fn target_pointer_width(&self) -> &str {
&*self.pointer_width
}
}
#[cfg(test)]
mod tests {
#[test]
fn correct_archs() {
macro_rules! check_arch {
($expected: expr, $bit: expr, $end: expr, $($str: expr),+) => {
$(
if let Ok(ti) = super::TargetInfo::from_str($str) {
assert_eq!(ti.target_arch(), $expected);
assert_eq!(ti.target_endian(), $end);
assert_eq!(ti.target_pointer_width(), $bit);
}
)+
}
}
check_arch!("x86_64", "64", "little"
, "x86_64-unknown-linux-gnu"
, "x86_64-unknown-linux-musl"
, "x86_64-unknown-freebsd"
, "x86_64-unknown-dragonfly"
, "x86_64-unknown-bitrig"
, "x86_64-unknown-openbsd"
, "x86_64-unknown-netbsd"
, "x86_64-rumprun-netbsd"
, "x86_64-apple-darwin"
, "x86_64-apple-ios"
, "x86_64-sun-solaris"
, "x86_64-pc-windows-gnu"
, "x86_64-pc-windows-msvc");
check_arch!("x86", "32", "little"
, "i586-unknown-linux-gnu"
, "i686-unknown-linux-musl"
, "i686-linux-android"
, "i686-unknown-freebsd"
, "i686-unknown-dragonfly"
, "i686-apple-darwin"
, "i686-pc-windows-gnu"
, "i686-pc-windows-msvc"
, "i586-pc-windows-msvc"
, "i386-apple-ios");
check_arch!("mips", "32", "big"
, "mips-unknown-linux-musl"
, "mips-unknown-linux-gnu");
check_arch!("mips", "32", "little"
, "mipsel-unknown-linux-musl"
, "mipsel-unknown-linux-gnu");
check_arch!("aarch64", "64", "little"
, "aarch64-unknown-linux-gnu"
, "aarch64-linux-android"
, "aarch64-apple-ios");
check_arch!("arm", "32", "little"
, "arm-unknown-linux-gnueabi"
, "arm-unknown-linux-gnueabihf"
, "arm-linux-androideabi"
, "armv7-linux-androideabi"
, "armv7-apple-ios");
check_arch!("powerpc", "32", "big", "powerpc-unknown-linux-gnu");
check_arch!("powerpc64", "64", "big"
, "powerpc64-unknown-linux-gnu");
check_arch!("powerpc64", "64", "little"
, "powerpc64le-unknown-linux-gnu");
}
#[test]
fn correct_vendors() {
macro_rules! check_vnd {
($expected: expr, $($str: expr),+) => {
$(
if let Ok(ti) = super::TargetInfo::from_str($str) {
assert_eq!(ti.target_vendor(), $expected);
}
)+
}
}
check_vnd!("unknown", "x86_64-unknown-linux-gnu"
, "x86_64-unknown-linux-musl"
, "x86_64-unknown-freebsd"
, "x86_64-unknown-dragonfly"
, "x86_64-unknown-bitrig"
, "x86_64-unknown-openbsd"
, "x86_64-unknown-netbsd"
, "i686-unknown-linux-gnu"
, "i586-unknown-linux-gnu"
, "i686-unknown-linux-musl"
, "i686-unknown-freebsd"
, "i686-unknown-dragonfly"
, "mips-unknown-linux-musl"
, "mips-unknown-linux-gnu"
, "mipsel-unknown-linux-musl"
, "mipsel-unknown-linux-gnu"
, "aarch64-unknown-linux-gnu"
, "arm-unknown-linux-gnueabi"
, "arm-unknown-linux-gnueabihf"
, "armv7-unknown-linux-gnueabihf"
, "powerpc-unknown-linux-gnu"
, "powerpc64-unknown-linux-gnu"
, "powerpc64le-unknown-linux-gnu"
, "i686-linux-android"
, "aarch64-linux-android"
, "arm-linux-androideabi"
, "armv7-linux-androideabi");
check_vnd!("apple", "x86_64-apple-darwin"
, "x86_64-apple-ios"
, "i686-apple-darwin"
, "i386-apple-ios"
, "aarch64-apple-ios"
, "armv7-apple-ios"
, "armv7s-apple-ios");
check_vnd!("pc", "x86_64-pc-windows-gnu"
, "x86_64-pc-windows-msvc"
, "i686-pc-windows-gnu"
, "i686-pc-windows-msvc"
, "i586-pc-windows-msvc");
check_vnd!("rumprun", "x86_64-rumprun-netbsd");
check_vnd!("sun", "x86_64-sun-solaris");
}
#[test]
fn correct_os() {
macro_rules! check_os {
($expected: expr, $($str: expr),+) => {
$(
if let Ok(ti) = super::TargetInfo::from_str($str) {
assert_eq!(ti.target_os(), $expected);
}
)+
}
}
check_os!("linux", "x86_64-unknown-linux-gnu"
, "x86_64-unknown-linux-musl"
, "i686-unknown-linux-gnu"
, "i586-unknown-linux-gnu"
, "i686-unknown-linux-musl"
, "mips-unknown-linux-musl"
, "mips-unknown-linux-gnu"
, "mipsel-unknown-linux-musl"
, "mipsel-unknown-linux-gnu"
, "aarch64-unknown-linux-gnu"
, "arm-unknown-linux-gnueabi"
, "arm-unknown-linux-gnueabihf"
, "armv7-unknown-linux-gnueabihf"
, "powerpc-unknown-linux-gnu"
, "powerpc64-unknown-linux-gnu"
, "powerpc64le-unknown-linux-gnu");
check_os!("android", "i686-linux-android"
, "aarch64-linux-android"
, "arm-linux-androideabi"
, "armv7-linux-androideabi");
check_os!("windows", "x86_64-pc-windows-gnu"
, "x86_64-pc-windows-msvc"
, "i686-pc-windows-gnu"
, "i686-pc-windows-msvc"
, "i586-pc-windows-msvc");
check_os!("freebsd", "x86_64-unknown-freebsd"
, "i686-unknown-freebsd");
check_os!("dragonfly", "x86_64-unknown-dragonfly"
, "i686-unknown-dragonfly");
check_os!("bitrig", "x86_64-unknown-bitrig");
check_os!("openbsd", "x86_64-unknown-openbsd");
check_os!("netbsd", "x86_64-unknown-netbsd"
, "x86_64-rumprun-netbsd");
check_os!("solaris", "x86_64-sun-solaris");
check_os!("macos", "x86_64-apple-darwin"
, "i686-apple-darwin");
check_os!("ios", "x86_64-apple-ios"
, "i386-apple-ios"
, "aarch64-apple-ios"
, "armv7-apple-ios"
, "armv7s-apple-ios");
}
#[test]
fn correct_env() {
macro_rules! check_env {
($expected: expr, $($str: expr),+) => {
$(
if let Ok(ti) = super::TargetInfo::from_str($str) {
assert_eq!(ti.target_env(), $expected);
}
)+
}
}
check_env!("gnu", "x86_64-unknown-linux-gnu"
, "i686-unknown-linux-gnu"
, "i586-unknown-linux-gnu"
, "mips-unknown-linux-gnu"
, "mipsel-unknown-linux-gnu"
, "aarch64-unknown-linux-gnu"
, "arm-unknown-linux-gnueabi"
, "arm-unknown-linux-gnueabihf"
, "armv7-unknown-linux-gnueabihf"
, "powerpc-unknown-linux-gnu"
, "powerpc64-unknown-linux-gnu"
, "powerpc64le-unknown-linux-gnu"
, "x86_64-pc-windows-gnu"
, "i686-pc-windows-gnu");
check_env!("musl", "x86_64-unknown-linux-musl"
, "i686-unknown-linux-musl"
, "mips-unknown-linux-musl"
, "mipsel-unknown-linux-musl");
check_env!("msvc", "x86_64-pc-windows-msvc"
, "i686-pc-windows-msvc"
, "i586-pc-windows-msvc");
check_env!("", "i686-linux-android"
, "aarch64-linux-android"
, "arm-linux-androideabi"
, "armv7-linux-androideabi"
, "x86_64-unknown-freebsd"
, "i686-unknown-freebsd"
, "x86_64-unknown-dragonfly"
, "i686-unknown-dragonfly"
, "x86_64-unknown-bitrig"
, "x86_64-unknown-openbsd"
, "x86_64-unknown-netbsd"
, "x86_64-rumprun-netbsd"
, "x86_64-sun-solaris"
, "x86_64-apple-darwin"
, "i686-apple-darwin"
, "x86_64-apple-ios"
, "i386-apple-ios"
, "aarch64-apple-ios"
, "armv7-apple-ios"
, "armv7s-apple-ios"
);
}
#[test]
fn external_work() {
use std::env;
env::set_var("TARGET", "src/my-great-target.json");
let target = super::TargetInfo::new().unwrap();
external_is_correct(&target);
}
#[test]
fn external_search_work() {
use std::env;
env::set_var("RUST_TARGET_PATH", "");
super::TargetInfo::from_str("my-great-target").err().unwrap();
env::set_var("RUST_TARGET_PATH", "/usr/");
super::TargetInfo::from_str("my-great-target").err().unwrap();
env::set_var("RUST_TARGET_PATH", "/usr/:src/");
let target = super::TargetInfo::from_str("my-great-target").unwrap();
external_is_correct(&target);
}
fn external_is_correct(ti: &super::TargetInfo) {
assert_eq!(ti.target_arch(), "x86_64");
assert_eq!(ti.target_endian(), "little");
assert_eq!(ti.target_pointer_width(), "42");
assert_eq!(ti.target_os(), "nux");
assert_eq!(ti.target_vendor(), "unknown");
assert_eq!(ti.target_env(), "");
}
}