1#![deny(missing_docs)]
28#![deny(warnings)]
29
30#[macro_use]
31extern crate thiserror;
32
33use std::env;
34use std::process::Command;
35
36#[cfg_attr(test, derive(Debug))]
38pub struct Cfg {
39 pub target_os: String,
41 pub target_family: Option<String>,
43 pub target_arch: String,
45 pub target_endian: String,
47 pub target_pointer_width: String,
49 pub target_env: String,
51 pub target_vendor: Option<String>,
53 pub target_has_atomic: Vec<String>,
55 pub target_feature: Vec<String>,
57 _extensible: (),
58}
59
60#[derive(Debug, Error)]
62pub enum Error {
63 #[error("rustc invocation failed: {0}")]
65 Rustc(String),
66 #[error("IO error: {0}")]
68 Io(#[from] std::io::Error),
69 #[error("`target_os` is missing")]
71 MissingTargetOs,
72 #[error("`target_arch` is missing")]
74 MissingTargetArch,
75 #[error("`target_endian` is missing")]
77 MissingTargetEndian,
78 #[error("`target_pointer_width` is missing")]
80 MissingTargetPointerWidth,
81 #[error("`target_env` is missing")]
83 MissingTargetEnv,
84}
85
86impl Cfg {
87 pub fn of(target: &str) -> Result<Cfg, Error> {
95 let output = Command::new(env::var("RUSTC").as_ref().map(|s| &**s).unwrap_or("rustc"))
97 .arg("--target")
98 .arg(target)
99 .args(&["--print", "cfg"])
100 .output()?;
101
102 if !output.status.success() {
103 return Err(Error::Rustc(
104 String::from_utf8(output.stderr)
105 .unwrap_or_else(|_| "(output was not valid UTF-8)".to_string()),
106 ));
107 }
108
109 let spec = String::from_utf8(output.stdout)
110 .map_err(|_| Error::Rustc("Target spec from rustc was not valid UTF-8".to_string()))?;
111 let mut target_os = None;
112 let mut target_family = None;
113 let mut target_arch = None;
114 let mut target_endian = None;
115 let mut target_pointer_width = None;
116 let mut target_env = None;
117 let mut target_vendor = None;
118 let mut target_has_atomic = vec![];
119 let mut target_feature = vec![];
120
121 for entry in spec.lines() {
122 let mut parts = entry.split('=');
123
124 if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
125 match key {
126 "target_os" => target_os = Some(value.trim_matches('"').to_string()),
127 "target_family" => target_family = Some(value.trim_matches('"').to_string()),
128 "target_arch" => target_arch = Some(value.trim_matches('"').to_string()),
129 "target_endian" => target_endian = Some(value.trim_matches('"').to_string()),
130 "target_pointer_width" => {
131 target_pointer_width = Some(value.trim_matches('"').to_string())
132 }
133 "target_env" => target_env = Some(value.trim_matches('"').to_string()),
134 "target_vendor" => target_vendor = Some(value.trim_matches('"').to_string()),
135 "target_has_atomic" => {
136 target_has_atomic.push(value.trim_matches('"').to_string())
137 }
138 "target_feature" => target_feature.push(value.trim_matches('"').to_string()),
139 _ => {}
140 }
141 }
142 }
143
144 Ok(Cfg {
145 target_os: target_os.ok_or(Error::MissingTargetOs)?,
146 target_family,
147 target_arch: target_arch.ok_or(Error::MissingTargetArch)?,
148 target_endian: target_endian.ok_or(Error::MissingTargetEndian)?,
149 target_pointer_width: target_pointer_width.ok_or(Error::MissingTargetPointerWidth)?,
150 target_env: target_env.ok_or(Error::MissingTargetEnv)?,
151 target_vendor,
152 target_has_atomic,
153 target_feature,
154 _extensible: (),
155 })
156 }
157}
158
159#[cfg(test)]
160mod test {
161 use std::process::Command;
162
163 use super::Cfg;
164
165 #[test]
166 fn all() {
167 let output = Command::new("rustc")
168 .args(&["--print", "target-list"])
169 .output()
170 .unwrap();
171
172 let stdout = String::from_utf8(output.stdout).unwrap();
173
174 assert!(output.status.success());
175
176 for target in stdout.lines() {
177 println!("{}\n\t{:?}\n", target, Cfg::of(target));
178 }
179 }
180}