#![deny(missing_docs)]
#![deny(warnings)]
#[macro_use]
extern crate thiserror;
use std::env;
use std::process::Command;
#[cfg_attr(test, derive(Debug))]
pub struct Cfg {
pub target_os: String,
pub target_family: Option<String>,
pub target_arch: String,
pub target_endian: String,
pub target_pointer_width: String,
pub target_env: String,
pub target_vendor: Option<String>,
pub target_has_atomic: Vec<String>,
pub target_feature: Vec<String>,
_extensible: (),
}
#[derive(Debug, Error)]
pub enum Error {
#[error("rustc invocation failed: {0}")]
Rustc(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("`target_os` is missing")]
MissingTargetOs,
#[error("`target_arch` is missing")]
MissingTargetArch,
#[error("`target_endian` is missing")]
MissingTargetEndian,
#[error("`target_pointer_width` is missing")]
MissingTargetPointerWidth,
#[error("`target_env` is missing")]
MissingTargetEnv,
}
impl Cfg {
pub fn of(target: &str) -> Result<Cfg, Error> {
let output = Command::new(env::var("RUSTC").as_ref().map(|s| &**s).unwrap_or("rustc"))
.arg("--target")
.arg(target)
.args(&["--print", "cfg"])
.output()?;
if !output.status.success() {
return Err(Error::Rustc(
String::from_utf8(output.stderr)
.unwrap_or_else(|_| "(output was not valid UTF-8)".to_string()),
));
}
let spec = String::from_utf8(output.stdout)
.map_err(|_| Error::Rustc("Target spec from rustc was not valid UTF-8".to_string()))?;
let mut target_os = None;
let mut target_family = None;
let mut target_arch = None;
let mut target_endian = None;
let mut target_pointer_width = None;
let mut target_env = None;
let mut target_vendor = None;
let mut target_has_atomic = vec![];
let mut target_feature = vec![];
for entry in spec.lines() {
let mut parts = entry.split('=');
if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
match key {
"target_os" => target_os = Some(value.trim_matches('"').to_string()),
"target_family" => target_family = Some(value.trim_matches('"').to_string()),
"target_arch" => target_arch = Some(value.trim_matches('"').to_string()),
"target_endian" => target_endian = Some(value.trim_matches('"').to_string()),
"target_pointer_width" => {
target_pointer_width = Some(value.trim_matches('"').to_string())
}
"target_env" => target_env = Some(value.trim_matches('"').to_string()),
"target_vendor" => target_vendor = Some(value.trim_matches('"').to_string()),
"target_has_atomic" => {
target_has_atomic.push(value.trim_matches('"').to_string())
}
"target_feature" => target_feature.push(value.trim_matches('"').to_string()),
_ => {}
}
}
}
Ok(Cfg {
target_os: target_os.ok_or(Error::MissingTargetOs)?,
target_family,
target_arch: target_arch.ok_or(Error::MissingTargetArch)?,
target_endian: target_endian.ok_or(Error::MissingTargetEndian)?,
target_pointer_width: target_pointer_width.ok_or(Error::MissingTargetPointerWidth)?,
target_env: target_env.ok_or(Error::MissingTargetEnv)?,
target_vendor,
target_has_atomic,
target_feature,
_extensible: (),
})
}
}
#[cfg(test)]
mod test {
use std::process::Command;
use super::Cfg;
#[test]
fn all() {
let output = Command::new("rustc")
.args(&["--print", "target-list"])
.output()
.unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(output.status.success());
for target in stdout.lines() {
println!("{}\n\t{:?}\n", target, Cfg::of(target));
}
}
}