use std::{error::Error, ffi::OsStr, process::Command};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct RustcTargetJson {
#[serde(rename = "linker-flavor")]
pub linker_flavor: String,
}
pub fn rustc_target_json(
rustc_path: &OsStr,
target_triple: &str,
) -> Result<RustcTargetJson, Box<dyn Error>> {
let output = Command::new(rustc_path)
.env("RUSTC_BOOTSTRAP", "1")
.args(["-Z", "unstable-options", "--print", "target-spec-json"])
.arg(format!("--target={target_triple}")) .output()
.map_err(|error| {
format!("Failed to invoke rustc to get target-spec-json for '{target_triple}': {error}")
})?;
if !output.status.success() {
return Err(format!(
"rustc returned an error when asked for target-spec-json for '{target_triple}' ({}): {}",
output.status,
String::from_utf8_lossy(&output.stderr)
)
.into());
}
serde_json::from_slice(&output.stdout).map_err(|error| {
format!("Failed to parse rustc target specification for '{target_triple}': {error}").into()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn query_linker_flavor() {
let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
for (target, expected_flavors) in [
(
"x86_64-unknown-linux-gnu",
&["gcc", "gnu-cc", "gnu-lld-cc"][..],
),
("thumbv7em-none-eabihf", &["ld.lld", "gnu-lld"][..]),
] {
let spec = rustc_target_json(&rustc, target).unwrap();
assert!(
expected_flavors.contains(&spec.linker_flavor.as_str()),
"unexpected linker flavor for {target}: {}",
spec.linker_flavor
);
}
}
}