Source code
Revision control
Copy as Markdown
Other Tools
use std::borrow::Cow;
use super::{generated, TargetInfo};
impl TargetInfo<'_> {
/// The LLVM/Clang target triple.
///
///
/// Rust and Clang don't really agree on target naming, so we first try to
/// find the matching trible based on `rustc`'s output, but if no such
/// triple exists, we attempt to construct the triple from scratch.
///
/// NOTE: You should never need to match on this explicitly, use the
/// fields on [`TargetInfo`] instead.
pub(crate) fn llvm_target(
&self,
rustc_target: &str,
version: Option<&str>,
) -> Cow<'static, str> {
if rustc_target == "armv7-apple-ios" {
// FIXME(madsmtm): Unnecessary once we bump MSRV to Rust 1.74
return Cow::Borrowed("armv7-apple-ios");
} else if self.os == "uefi" {
// Override the UEFI LLVM targets.
//
// The rustc mappings (as of 1.82) for the UEFI targets are:
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows
// * aarch64-unknown-uefi -> aarch64-unknown-windows
//
// However, in cc-rs all the UEFI targets use
// -windows-gnu. This has been the case since 2021 [1].
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows-gnu
// * aarch64-unknown-uefi -> aarch64-unknown-windows-gnu
//
// For now, override the UEFI mapping to keep the behavior
// of cc-rs unchanged.
//
// TODO: as discussed in [2], it may be possible to switch
// to new UEFI targets added to clang, and regardless it
// would be good to have consistency between rustc and
// cc-rs.
//
return Cow::Owned(format!("{}-unknown-windows-gnu", self.full_arch));
}
// If no version is requested, let's take the triple directly from
// `rustc` (the logic below is not yet good enough for most targets).
//
// FIXME(madsmtm): This should ideally be removed.
if version.is_none() {
if let Ok(index) = generated::LLVM_TARGETS
.binary_search_by_key(&rustc_target, |(rustc_target, _)| rustc_target)
{
let (_, llvm_target) = &generated::LLVM_TARGETS[index];
return Cow::Borrowed(llvm_target);
}
}
// Otherwise, attempt to construct the triple from the target info.
let arch = match self.full_arch {
riscv32 if riscv32.starts_with("riscv32") => "riscv32",
riscv64 if riscv64.starts_with("riscv64") => "riscv64",
"aarch64" if self.vendor == "apple" => "arm64",
"armv7" if self.vendor == "sony" => "thumbv7a", // FIXME
arch => arch,
};
let vendor = match self.vendor {
"kmc" | "nintendo" => "unknown",
"unknown" if self.os == "android" => "linux",
"uwp" => "pc",
"espressif" => "",
_ if self.arch == "msp430" => "",
vendor => vendor,
};
let os = match self.os {
"macos" => "macosx",
"visionos" => "xros",
"uefi" => "windows",
"solid_asp3" | "horizon" | "teeos" | "nuttx" | "espidf" => "none",
"nto" => "unknown", // FIXME
"trusty" => "unknown", // FIXME
os => os,
};
let version = version.unwrap_or("");
let env = match self.env {
"newlib" | "nto70" | "nto71" | "nto71_iosock" | "p1" | "p2" | "relibc" | "sgx"
| "uclibc" => "",
env => env,
};
let abi = match self.abi {
"sim" => "simulator",
"llvm" | "softfloat" | "uwp" | "vec-extabi" => "",
"ilp32" => "_ilp32",
"abi64" => "",
abi => abi,
};
Cow::Owned(match (vendor, env, abi) {
("", "", "") => format!("{arch}-{os}{version}"),
("", env, abi) => format!("{arch}-{os}{version}-{env}{abi}"),
(vendor, "", "") => format!("{arch}-{vendor}-{os}{version}"),
(vendor, env, abi) => format!("{arch}-{vendor}-{os}{version}-{env}{abi}"),
})
}
}
#[cfg(test)]
mod tests {
use std::process::Command;
use crate::TargetInfo;
#[test]
fn test_old_ios_target() {
assert_eq!(
TargetInfo {
full_arch: "armv7",
arch: "armv7",
vendor: "apple",
os: "ios",
env: "",
abi: "",
}
.llvm_target("armv7-apple-ios", None),
"armv7-apple-ios"
);
}
#[test]
fn basic_llvm_triple_guessing() {
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "unknown",
os: "linux",
env: "",
abi: "",
}
.llvm_target("invalid", None),
"aarch64-unknown-linux"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "gnu",
abi: "",
}
.llvm_target("invalid", None),
"x86_64-unknown-linux-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "gnu",
abi: "eabi",
}
.llvm_target("invalid", None),
"x86_64-unknown-linux-gnueabi"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "apple",
os: "macos",
env: "",
abi: "",
}
.llvm_target("invalid", None),
"x86_64-apple-macosx"
);
}
#[test]
fn llvm_version() {
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "ios",
env: "",
abi: "sim",
}
.llvm_target("aarch64-apple-ios-sim", Some("14.0")),
"arm64-apple-ios14.0-simulator"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "visionos",
env: "",
abi: "",
}
.llvm_target("aarch64-apple-visionos", Some("2.0")),
"arm64-apple-xros2.0"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "ios",
env: "",
abi: "macabi",
}
.llvm_target("aarch64-apple-ios-macabi", Some("13.1")),
"arm64-apple-ios13.1-macabi"
);
}
#[test]
fn uefi() {
assert_eq!(
TargetInfo {
full_arch: "i686",
arch: "x86",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("i686-unknown-uefi", None),
"i686-unknown-windows-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("x86_64-unknown-uefi", None),
"x86_64-unknown-windows-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("aarch64-unknown-uefi", None),
"aarch64-unknown-windows-gnu"
);
}
#[test]
#[ignore = "not yet done"]
fn llvm_for_all_rustc_targets() {
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
let target_list = Command::new(&rustc)
.arg("--print=target-list")
.output()
.unwrap()
.stdout;
let target_list = String::from_utf8(target_list).unwrap();
let mut has_failure = false;
for target in target_list.lines() {
let spec_json = Command::new(&rustc)
.arg("--target")
.arg(target)
.arg("-Zunstable-options")
.arg("--print=target-spec-json")
.env("RUSTC_BOOTSTRAP", "1") // Crimes
.output()
.unwrap()
.stdout;
let spec_json = String::from_utf8(spec_json).unwrap();
// JSON crimes
let expected = spec_json
.split_once("llvm-target\": \"")
.unwrap()
.1
.split_once("\"")
.unwrap()
.0;
let actual = TargetInfo::from_rustc_target(target)
.map(|target| target.llvm_target("invalid", None));
if Some(expected) != actual.as_deref().ok() {
eprintln!("failed comparing {target}:");
eprintln!(" expected: Ok({expected:?})");
eprintln!(" actual: {actual:?}");
eprintln!();
has_failure = true;
}
}
if has_failure {
panic!("failed comparing targets");
}
}
}