use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::env;
use std::fmt;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command as ProcessCommand;
use crate::triple::{Arch, Environment, Triple, Vendor, OS};
use crate::clang::clang_sysroot::{
GccInstallation, HeaderSearchEntry, IncludePathKind, Sysroot, VfsOverlay,
};
use crate::clang::clang_target::{
ClangABI, ClangOptLevel, ClangTargetInfo, DebugInfoKind, TargetCodeGenInfo, TargetFlags,
};
#[derive(Debug, Clone)]
pub struct X86SysrootManager {
pub target_triple: String,
pub triple: Triple,
pub explicit_sysroot: Option<PathBuf>,
pub detector: X86SysrootDetector,
pub cross_compilation: X86CrossCompilation,
pub multiarch: X86Multiarch,
pub triple_parser: X86TripleParser,
pub compiler_rt: X86CompilerRT,
pub resource_dir: X86ResourceDir,
pub cross_library: X86CrossLibrary,
pub gcc_install: Option<GccInstallation>,
pub use_compiler_rt: bool,
pub use_libcxx: bool,
pub is_cross: bool,
pub extra_includes: Vec<PathBuf>,
pub extra_libraries: Vec<PathBuf>,
pub env_vars: HashMap<String, String>,
}
impl X86SysrootManager {
pub fn new(triple_str: &str) -> Self {
let triple = Triple::parse(triple_str);
let detector = X86SysrootDetector::new(triple_str);
let cross_compilation = X86CrossCompilation::new(triple_str);
let multiarch = X86Multiarch::new(&triple);
let triple_parser = X86TripleParser::new(triple_str);
let compiler_rt = X86CompilerRT::new(&triple);
let resource_dir = X86ResourceDir::new(&triple);
let cross_library = X86CrossLibrary::new(&triple);
let is_cross = cross_compilation.is_cross;
let mut env_vars = HashMap::new();
for var in &[
"C_INCLUDE_PATH",
"CPLUS_INCLUDE_PATH",
"CPATH",
"LIBRARY_PATH",
"LD_LIBRARY_PATH",
"SYSROOT",
"SDKROOT",
"CROSS_COMPILE",
"PKG_CONFIG_PATH",
"PKG_CONFIG_SYSROOT_DIR",
"ANDROID_NDK_HOME",
"ANDROID_NDK_ROOT",
"ANDROID_SDK_HOME",
"NDK_HOME",
"XCODE_PATH",
"DEVELOPER_DIR",
"WINDOWS_SDK_PATH",
"MSVC_PATH",
] {
if let Ok(val) = env::var(var) {
env_vars.insert(var.to_string(), val);
}
}
Self {
target_triple: triple_str.to_string(),
triple,
explicit_sysroot: None,
detector,
cross_compilation,
multiarch,
triple_parser,
compiler_rt,
resource_dir,
cross_library,
gcc_install: None,
use_compiler_rt: false,
use_libcxx: false,
is_cross,
extra_includes: Vec::new(),
extra_libraries: Vec::new(),
env_vars,
}
}
pub fn with_sysroot(triple_str: &str, sysroot: PathBuf) -> Self {
let mut manager = Self::new(triple_str);
manager.explicit_sysroot = Some(sysroot.clone());
manager.detector.forced_sysroot = Some(sysroot);
manager
}
pub fn for_cross(triple_str: &str, sysroot: PathBuf) -> Self {
let mut manager = Self::with_sysroot(triple_str, sysroot);
manager.is_cross = true;
manager.use_compiler_rt = true;
manager
}
pub fn detect_sysroot(&mut self) -> Sysroot {
if let Some(ref path) = self.explicit_sysroot {
if path.exists() {
return Sysroot::explicit(path.clone());
}
}
self.detector.detect()
}
pub fn build_include_paths(&mut self, sysroot: &Sysroot) -> Vec<HeaderSearchEntry> {
let mut paths: Vec<HeaderSearchEntry> = Vec::new();
for inc in &self.extra_includes {
paths.push(HeaderSearchEntry::user(inc.clone()));
}
let resource_include = self.resource_dir.include_dir();
if resource_include.exists() {
paths.push(HeaderSearchEntry::resource(resource_include));
}
if let Some(ref gcc) = self.gcc_install {
for p in gcc.c_include_paths() {
paths.push(HeaderSearchEntry::system(p).with_from_gcc());
}
}
if self.use_libcxx {
let cxx = crate::clang::clang_sysroot::CxxStdLib::detect_libcxx(sysroot);
for p in &cxx.include_paths {
paths.push(HeaderSearchEntry::system(p.clone()));
}
}
for (var, kind) in &[
("CPATH", IncludePathKind::User),
("C_INCLUDE_PATH", IncludePathKind::System),
("CPLUS_INCLUDE_PATH", IncludePathKind::System),
] {
if let Some(val) = self.env_vars.get(*var) {
for part in val.split(':') {
let p = PathBuf::from(part);
if p.is_dir() {
let entry = HeaderSearchEntry::new(p, *kind).with_from_env();
paths.push(entry);
}
}
}
}
let usr_include = sysroot.usr_include();
if usr_include.exists() {
paths.push(HeaderSearchEntry::system(usr_include));
}
let usr_local_include = sysroot.usr_local_include();
if usr_local_include.exists() {
paths.push(HeaderSearchEntry::system(usr_local_include));
}
if self.triple.os == OS::Linux && self.triple.environment == Environment::Android {
let android_includes = self.detector.android_ndk_include_paths();
for p in android_includes {
paths.push(HeaderSearchEntry::system(p));
}
}
if self.triple.is_macho() {
let frameworks = sysroot.system_library_frameworks();
if frameworks.exists() {
paths.push(HeaderSearchEntry::framework(frameworks));
}
let lib_frameworks = sysroot.library_frameworks();
if lib_frameworks.exists() {
paths.push(HeaderSearchEntry::framework(lib_frameworks));
}
}
let cross_includes = self.cross_library.include_paths(sysroot);
for p in cross_includes {
paths.push(HeaderSearchEntry::system(p));
}
paths
}
pub fn build_library_paths(&mut self, sysroot: &Sysroot) -> Vec<PathBuf> {
let mut paths: Vec<PathBuf> = Vec::new();
paths.extend(self.extra_libraries.clone());
let multiarch_paths = self.multiarch.library_paths(sysroot);
paths.extend(multiarch_paths);
paths.push(self.resource_dir.lib_dir());
let usr_lib = sysroot.usr_lib();
if usr_lib.exists() {
paths.push(usr_lib);
}
let usr_local_lib = sysroot.path.join("usr").join("local").join("lib");
if usr_local_lib.exists() {
paths.push(usr_local_lib);
}
if let Some(ref gcc) = self.gcc_install {
let gcc_lib = gcc.install_dir.clone();
if gcc_lib.exists() {
paths.push(gcc_lib);
}
}
let cross_libs = self.cross_library.library_paths(sysroot);
paths.extend(cross_libs);
if let Some(val) = self.env_vars.get("LIBRARY_PATH") {
for part in val.split(':') {
let p = PathBuf::from(part);
if p.is_dir() {
paths.push(p);
}
}
}
paths
}
pub fn detect_gcc(&mut self) {
self.gcc_install = Some(GccInstallation::detect(&self.triple));
}
pub fn compiler_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if let Some(ref sysroot) = self.explicit_sysroot {
flags.push(format!("--sysroot={}", sysroot.display()));
}
flags.push(format!("-target {}", self.target_triple));
if self.use_compiler_rt {
flags.push("--rtlib=compiler-rt".to_string());
}
if self.use_libcxx {
flags.push("--stdlib=libc++".to_string());
}
flags.push(format!(
"-resource-dir {}",
self.resource_dir.path().display()
));
for inc in &self.extra_includes {
flags.push(format!("-I{}", inc.display()));
}
for lib in &self.extra_libraries {
flags.push(format!("-L{}", lib.display()));
}
flags
}
pub fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.use_compiler_rt {
flags.push("--rtlib=compiler-rt".to_string());
let builtins = self.compiler_rt.builtins_library_path();
if let Some(ref path) = builtins {
flags.push(path.to_string_lossy().to_string());
}
}
if self.use_libcxx {
flags.push("-lc++".to_string());
flags.push("-lc++abi".to_string());
}
if let Some(linker) = self.dynamic_linker() {
flags.push(format!("-dynamic-linker {}", linker));
}
flags
}
pub fn dynamic_linker(&self) -> Option<String> {
self.multiarch.dynamic_linker(&self.triple)
}
pub fn set_sysroot_arg(&mut self, arg: &str) {
let path_str = if let Some(stripped) = arg.strip_prefix("--sysroot=") {
stripped
} else if arg == "--sysroot" {
return; } else {
arg
};
let path = PathBuf::from(path_str);
if path.exists() {
self.explicit_sysroot = Some(path.clone());
self.detector.forced_sysroot = Some(path);
}
}
pub fn is_native(&self) -> bool {
!self.is_cross && self.cross_compilation.is_host_triple()
}
pub fn can_execute(&self) -> bool {
self.cross_compilation.can_execute_natively()
}
pub fn emulator(&self) -> Option<String> {
self.cross_compilation.emulator_command()
}
pub fn add_include(&mut self, path: PathBuf) {
if !self.extra_includes.contains(&path) {
self.extra_includes.push(path);
}
}
pub fn add_library(&mut self, path: PathBuf) {
if !self.extra_libraries.contains(&path) {
self.extra_libraries.push(path);
}
}
pub fn arch_directory_name(&self) -> String {
self.multiarch
.multiarch_dir
.clone()
.unwrap_or_else(|| self.target_triple.clone())
}
pub fn recommended_libc(&self) -> X86LibC {
X86CrossLibrary::detect_libc_type(&self.triple)
}
pub fn is_ready(&self) -> bool {
if self.is_native() {
return true;
}
self.explicit_sysroot.is_some()
|| self.detector.forced_sysroot.is_some()
|| self
.gcc_install
.as_ref()
.map(|g| g.is_valid)
.unwrap_or(false)
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str(&format!("Target: {}\n", self.target_triple));
s.push_str(&format!("Arch: {:?}\n", self.triple.arch));
s.push_str(&format!("OS: {:?}\n", self.triple.os));
s.push_str(&format!("Environment: {:?}\n", self.triple.environment));
s.push_str(&format!("Cross: {}\n", self.is_cross));
s.push_str(&format!("Native: {}\n", self.is_native()));
s.push_str(&format!(
"Sysroot: {}\n",
self.explicit_sysroot
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "auto-detect".to_string())
));
s.push_str(&format!("Compiler-RT: {}\n", self.use_compiler_rt));
s.push_str(&format!("libc++: {}\n", self.use_libcxx));
s.push_str(&format!(
"Multiarch: {}\n",
self.multiarch.multiarch_dir.as_deref().unwrap_or("none")
));
s.push_str(&format!(
"Dynamic linker: {}\n",
self.dynamic_linker().unwrap_or_else(|| "none".to_string())
));
s
}
pub fn detect_cxx_libraries(&self) -> Vec<(String, PathBuf)> {
let mut libs = Vec::new();
if let Some(ref gcc) = self.gcc_install {
if gcc.is_valid {
let libstd = gcc.install_dir.join("libstdc++.so");
if libstd.exists() {
libs.push(("libstdc++".to_string(), libstd));
}
let libstd_a = gcc.install_dir.join("libstdc++.a");
if libstd_a.exists() {
libs.push(("libstdc++ (static)".to_string(), libstd_a));
}
}
}
for lib_dir in &[
PathBuf::from("/usr/lib"),
PathBuf::from("/usr/local/lib"),
PathBuf::from("/usr/lib/x86_64-linux-gnu"),
] {
let libcxx = lib_dir.join("libc++.so");
if libcxx.exists() {
libs.push(("libc++".to_string(), libcxx));
break;
}
let libcxx_a = lib_dir.join("libc++.a");
if libcxx_a.exists() {
libs.push(("libc++ (static)".to_string(), libcxx_a));
break;
}
}
libs
}
pub fn configure_lto(&self) -> Vec<String> {
let mut flags = Vec::new();
flags.push("-flto=thin".to_string());
flags.push("-flto-jobs=auto".to_string());
if self.use_compiler_rt {
flags.push("-fuse-ld=lld".to_string());
}
flags
}
pub fn configure_sanitizers(&self, sanitizers: &[&str]) -> Vec<String> {
let mut flags = Vec::new();
if sanitizers.is_empty() {
return flags;
}
let sanitize_list = sanitizers.join(",");
flags.push(format!("-fsanitize={}", sanitize_list));
flags.push("-fno-omit-frame-pointer".to_string());
flags.push("-g".to_string());
flags
}
pub fn configure_coverage(&self) -> Vec<String> {
vec![
"-fprofile-instr-generate".to_string(),
"-fcoverage-mapping".to_string(),
]
}
pub fn configure_pgo(&self, profile_dir: &str) -> Vec<String> {
vec![
format!("-fprofile-instr-use={}", profile_dir),
"-fprofile-sample-use".to_string(),
]
}
pub fn configure_bolt(&self, profile_file: &str) -> Vec<String> {
vec![
format!("-fbolt-use={}", profile_file),
"-fbolt-target=x86-64".to_string(),
]
}
}
#[derive(Debug, Clone)]
pub struct X86SysrootCache {
cache: HashMap<String, Sysroot>,
gcc_cache: HashMap<String, GccInstallation>,
libc_cache: HashMap<String, X86CrossLibrary>,
max_entries: usize,
}
impl X86SysrootCache {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
gcc_cache: HashMap::new(),
libc_cache: HashMap::new(),
max_entries: 64,
}
}
pub fn get_sysroot(&mut self, triple: &str) -> Sysroot {
if let Some(cached) = self.cache.get(triple) {
return cached.clone();
}
let detector = X86SysrootDetector::new(triple);
let sysroot = detector.detect();
if self.cache.len() >= self.max_entries {
if let Some(key) = self.cache.keys().next().cloned() {
self.cache.remove(&key);
}
}
self.cache.insert(triple.to_string(), sysroot.clone());
sysroot
}
pub fn get_gcc(&mut self, triple: &Triple) -> GccInstallation {
let key = triple.to_string();
if let Some(cached) = self.gcc_cache.get(&key) {
return cached.clone();
}
let gcc = GccInstallation::detect(triple);
self.gcc_cache.insert(key, gcc.clone());
gcc
}
pub fn get_libc(&mut self, triple: &Triple) -> X86CrossLibrary {
let key = triple.to_string();
if let Some(cached) = self.libc_cache.get(&key) {
return cached.clone();
}
let libc = X86CrossLibrary::new(triple);
self.libc_cache.insert(key, libc.clone());
libc
}
pub fn clear(&mut self) {
self.cache.clear();
self.gcc_cache.clear();
self.libc_cache.clear();
}
pub fn len(&self) -> usize {
self.cache.len() + self.gcc_cache.len() + self.libc_cache.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for X86SysrootCache {
fn default() -> Self {
Self::new()
}
}
impl Default for X86SysrootManager {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct X86SysrootDetector {
pub target_triple: String,
pub triple: Triple,
pub forced_sysroot: Option<PathBuf>,
pub search_xcode: bool,
pub search_android_ndk: bool,
pub search_windows_sdk: bool,
pub search_mingw: bool,
pub custom_search_paths: Vec<PathBuf>,
pub strict: bool,
pub use_env: bool,
}
impl X86SysrootDetector {
pub fn new(triple_str: &str) -> Self {
let triple = Triple::parse(triple_str);
let search_xcode = triple.is_macho()
|| triple.os == OS::IOS
|| triple.os == OS::TvOS
|| triple.os == OS::WatchOS
|| cfg!(target_os = "macos");
let search_android_ndk = triple.environment == Environment::Android
|| triple.environment == Environment::Androideabi;
let search_windows_sdk = triple.os == OS::Win32 || triple.environment == Environment::MSVC;
let search_mingw = triple_str.contains("mingw") || triple_str.contains("windows-gnu");
Self {
target_triple: triple_str.to_string(),
triple,
forced_sysroot: None,
search_xcode,
search_android_ndk,
search_windows_sdk,
search_mingw,
custom_search_paths: Vec::new(),
strict: false,
use_env: true,
}
}
pub fn detect(&self) -> Sysroot {
if let Some(ref forced) = self.forced_sysroot {
if forced.exists() {
return Sysroot::explicit(forced.clone());
}
}
if self.use_env {
if let Ok(sysroot_env) = env::var("SYSROOT") {
let p = PathBuf::from(&sysroot_env);
if p.exists() {
return Sysroot::cross(p);
}
}
if let Ok(sdkroot) = env::var("SDKROOT") {
let p = PathBuf::from(&sdkroot);
if p.exists() {
return Sysroot::cross(p);
}
}
}
for custom in &self.custom_search_paths {
if custom.exists() {
return Sysroot::cross(custom.clone());
}
}
if self.search_android_ndk {
if let Some(ndk) = self.detect_android_ndk_sysroot() {
return Sysroot::cross(ndk);
}
}
if self.search_xcode {
if let Some(sdk) = self.detect_macos_sdk() {
return Sysroot::cross(sdk);
}
if let Some(sdk) = self.detect_ios_sdk() {
return Sysroot::cross(sdk);
}
if let Some(sdk) = self.detect_tvos_sdk() {
return Sysroot::cross(sdk);
}
if let Some(sdk) = self.detect_watchos_sdk() {
return Sysroot::cross(sdk);
}
}
if self.search_windows_sdk {
if let Some(sdk) = self.detect_windows_sdk() {
return Sysroot::cross(sdk);
}
}
if self.search_mingw {
if let Some(mingw) = self.detect_mingw_sysroot() {
return Sysroot::cross(mingw);
}
}
if let Some(cross) = self.detect_gcc_cross_sysroot() {
return Sysroot::cross(cross);
}
if self.is_host_triple() {
return Sysroot::host();
}
if self.triple.os == OS::Linux {
let usr = PathBuf::from("/usr");
if usr.exists() {
return Sysroot::cross(PathBuf::from("/"));
}
}
Sysroot {
path: PathBuf::new(),
is_explicit: false,
is_cross: true,
}
}
pub fn detect_android_ndk_sysroot(&self) -> Option<PathBuf> {
let ndk_homes = self.find_android_ndk_homes();
for ndk_home in &ndk_homes {
let sysroot_r23 = ndk_home
.join("toolchains")
.join("llvm")
.join("prebuilt")
.join(self.host_platform_tag())
.join("sysroot");
if sysroot_r23.exists() {
return Some(sysroot_r23);
}
let sysroot_legacy = ndk_home.join("sysroot");
if sysroot_legacy.exists() {
let usr_include = sysroot_legacy.join("usr").join("include");
if usr_include.exists() {
return Some(sysroot_legacy);
}
}
let android_arch = self.android_arch_name();
let platforms_dir = ndk_home.join("platforms");
if platforms_dir.exists() {
if let Some(platform) = self.find_highest_android_platform(&platforms_dir) {
let arch_sysroot = platform.join(format!("arch-{}", android_arch));
if arch_sysroot.exists() {
return Some(arch_sysroot);
}
}
}
}
None
}
fn find_android_ndk_homes(&self) -> Vec<PathBuf> {
let mut homes = Vec::new();
for var in &["ANDROID_NDK_HOME", "ANDROID_NDK_ROOT", "NDK_HOME"] {
if let Ok(val) = env::var(var) {
let p = PathBuf::from(val);
if p.exists() {
homes.push(p);
}
}
}
let common_paths = [
"/usr/local/android-ndk",
"/opt/android-ndk",
"/opt/android-sdk/ndk",
"/opt/android-sdk/ndk-bundle",
"/usr/lib/android-sdk/ndk",
"/usr/lib/android-sdk/ndk-bundle",
&format!("{}/Android/Sdk/ndk", env::var("HOME").unwrap_or_default()),
&format!(
"{}/Android/Sdk/ndk-bundle",
env::var("HOME").unwrap_or_default()
),
];
for path_str in &common_paths {
let p = PathBuf::from(path_str);
if p.exists() && !homes.contains(&p) {
homes.push(p);
}
}
for path_str in &common_paths {
let base = PathBuf::from(path_str);
let parent = base.parent().map(|p| p.to_path_buf());
if let Some(parent) = parent {
if let Ok(entries) = fs::read_dir(&parent) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with("ndk") || name.starts_with("android-ndk") {
let p = entry.path();
if p.is_dir() && !homes.contains(&p) {
homes.push(p);
}
}
}
}
}
}
homes
}
fn android_arch_name(&self) -> &'static str {
match self.triple.arch {
Arch::X86 => "x86",
Arch::X86_64 => "x86_64",
Arch::ARM => "arm",
Arch::AArch64 => "arm64",
Arch::Mips => "mips",
Arch::Mips64 => "mips64",
_ => "x86",
}
}
fn find_highest_android_platform(&self, platforms_dir: &Path) -> Option<PathBuf> {
let mut platforms: Vec<(u32, PathBuf)> = Vec::new();
if let Ok(entries) = fs::read_dir(platforms_dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if let Some(level_str) = name.strip_prefix("android-") {
if let Ok(level) = level_str.parse::<u32>() {
platforms.push((level, entry.path()));
}
}
}
}
platforms.sort_by_key(|(level, _)| *level);
platforms.last().map(|(_, path)| path.clone())
}
fn host_platform_tag(&self) -> &'static str {
if cfg!(target_os = "linux") {
"linux-x86_64"
} else if cfg!(target_os = "macos") {
"darwin-x86_64"
} else if cfg!(target_os = "windows") {
"windows-x86_64"
} else {
"linux-x86_64"
}
}
pub fn android_ndk_include_paths(&self) -> Vec<PathBuf> {
let mut paths = Vec::new();
if let Some(sysroot) = self.detect_android_ndk_sysroot() {
let usr_include = sysroot.join("usr").join("include");
if usr_include.exists() {
paths.push(usr_include.clone());
let arch_include = usr_include.join(self.android_arch_name());
if arch_include.exists() {
paths.push(arch_include);
}
let api_level = self.infer_android_api_level();
let arch_include_api = usr_include
.join(self.android_arch_name())
.join(format!("android-{}", api_level));
if arch_include_api.exists() {
paths.push(arch_include_api);
}
}
let cxx_include = sysroot.join("usr").join("include").join("c++");
if cxx_include.exists() {
if let Ok(entries) = fs::read_dir(&cxx_include) {
for entry in entries.flatten() {
if entry.path().is_dir() {
paths.push(entry.path());
}
}
}
}
}
paths
}
fn infer_android_api_level(&self) -> u32 {
if let Ok(platform) = env::var("ANDROID_PLATFORM") {
if let Some(level_str) = platform.strip_prefix("android-") {
if let Ok(level) = level_str.parse::<u32>() {
return level;
}
}
}
match self.triple.arch {
Arch::X86_64 => 21,
Arch::AArch64 => 21,
_ => 16, }
}
pub fn detect_macos_sdk(&self) -> Option<PathBuf> {
if !(self.triple.is_macos() || self.triple.os == OS::Darwin || self.triple.os == OS::MacOSX)
{
return None;
}
if let Ok(sdkroot) = env::var("SDKROOT") {
let p = PathBuf::from(&sdkroot);
if p.exists() {
return Some(p);
}
}
let xcode_path = self.find_xcode_path()?;
let sdk_dir = xcode_path
.join("Contents")
.join("Developer")
.join("Platforms")
.join("MacOSX.platform")
.join("Developer")
.join("SDKs");
self.find_latest_sdk(&sdk_dir, "MacOSX")
}
pub fn detect_ios_sdk(&self) -> Option<PathBuf> {
if self.triple.os != OS::IOS && !self.target_triple.contains("ios") {
return None;
}
let xcode_path = self.find_xcode_path()?;
let sdk_dir = xcode_path
.join("Contents")
.join("Developer")
.join("Platforms")
.join("iPhoneOS.platform")
.join("Developer")
.join("SDKs");
self.find_latest_sdk(&sdk_dir, "iPhoneOS")
}
pub fn detect_tvos_sdk(&self) -> Option<PathBuf> {
if self.triple.os != OS::TvOS && !self.target_triple.contains("tvos") {
return None;
}
let xcode_path = self.find_xcode_path()?;
let sdk_dir = xcode_path
.join("Contents")
.join("Developer")
.join("Platforms")
.join("AppleTVOS.platform")
.join("Developer")
.join("SDKs");
self.find_latest_sdk(&sdk_dir, "AppleTVOS")
}
pub fn detect_watchos_sdk(&self) -> Option<PathBuf> {
if self.triple.os != OS::WatchOS && !self.target_triple.contains("watchos") {
return None;
}
let xcode_path = self.find_xcode_path()?;
let sdk_dir = xcode_path
.join("Contents")
.join("Developer")
.join("Platforms")
.join("WatchOS.platform")
.join("Developer")
.join("SDKs");
self.find_latest_sdk(&sdk_dir, "WatchOS")
}
fn find_xcode_path(&self) -> Option<PathBuf> {
if let Ok(dev_dir) = env::var("DEVELOPER_DIR") {
let p = PathBuf::from(&dev_dir);
if p.exists() {
if p.ends_with("Developer") {
return p.parent().map(|p| p.to_path_buf());
}
return Some(p);
}
}
if let Ok(xcode_path) = env::var("XCODE_PATH") {
let p = PathBuf::from(&xcode_path);
if p.exists() {
return Some(p);
}
}
let default_xcode = PathBuf::from("/Applications/Xcode.app");
if default_xcode.exists() {
return Some(default_xcode);
}
if let Ok(output) = ProcessCommand::new("xcrun")
.args(["--show-sdk-path"])
.output()
{
if output.status.success() {
let sdk_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let sdk = PathBuf::from(&sdk_path);
if let Some(xcode) = sdk
.parent() .and_then(|p| p.parent()) .and_then(|p| p.parent()) .and_then(|p| p.parent()) .and_then(|p| p.parent()) .and_then(|p| p.parent()) .and_then(|p| p.parent())
{
if xcode.exists() {
return Some(xcode.to_path_buf());
}
}
}
}
None
}
fn find_latest_sdk(&self, sdk_dir: &Path, prefix: &str) -> Option<PathBuf> {
if !sdk_dir.exists() {
return None;
}
let mut sdks: Vec<(String, PathBuf)> = Vec::new();
if let Ok(entries) = fs::read_dir(sdk_dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with(prefix) && name.ends_with(".sdk") {
sdks.push((name, entry.path()));
}
}
}
sdks.sort_by(|(a, _), (b, _)| {
let va = Self::parse_sdk_version(a);
let vb = Self::parse_sdk_version(b);
va.partial_cmp(&vb).unwrap_or(std::cmp::Ordering::Equal)
});
sdks.last().map(|(_, path)| path.clone())
}
fn parse_sdk_version(name: &str) -> Vec<u32> {
let mut version = Vec::new();
let mut current = String::new();
for ch in name.chars() {
if ch.is_ascii_digit() {
current.push(ch);
} else if !current.is_empty() {
if let Ok(num) = current.parse::<u32>() {
version.push(num);
}
current.clear();
}
}
if !current.is_empty() {
if let Ok(num) = current.parse::<u32>() {
version.push(num);
}
}
version
}
pub fn detect_windows_sdk(&self) -> Option<PathBuf> {
let program_files_x86 =
env::var("ProgramFiles(x86)").unwrap_or_else(|_| "C:\\Program Files (x86)".to_string());
let sdk_candidates = [
PathBuf::from(&program_files_x86)
.join("Windows Kits")
.join("10"),
PathBuf::from("C:\\Program Files\\Windows Kits\\10"),
PathBuf::from("C:\\Program Files (x86)\\Windows Kits\\10"),
];
if let Ok(sdk_path) = env::var("WINDOWS_SDK_PATH") {
let p = PathBuf::from(&sdk_path);
if p.exists() {
return Some(p);
}
}
for candidate in &sdk_candidates {
if candidate.exists() {
let include_dir = candidate.join("Include");
if include_dir.exists() {
if let Some(latest) = self.find_latest_windows_sdk_version(candidate) {
return Some(latest);
}
}
let um_dir = candidate.join("Include").join("um");
if um_dir.exists() {
return Some(candidate.clone());
}
}
}
None
}
fn find_latest_windows_sdk_version(&self, sdk_base: &Path) -> Option<PathBuf> {
let include_dir = sdk_base.join("Include");
if !include_dir.exists() {
return None;
}
let mut versions: Vec<(Vec<u32>, PathBuf)> = Vec::new();
if let Ok(entries) = fs::read_dir(&include_dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
let parts: Vec<&str> = name.split('.').collect();
if parts.len() >= 2 {
let nums: Vec<u32> =
parts.iter().filter_map(|s| s.parse::<u32>().ok()).collect();
if nums.len() >= 2 {
versions.push((nums, sdk_base.join("Include").join(&name)));
}
}
}
}
versions.sort_by(|(a, _), (b, _)| a.cmp(b));
versions.last().map(|(_, path)| path.clone())
}
pub fn detect_mingw_sysroot(&self) -> Option<PathBuf> {
let candidates = [
PathBuf::from("/usr/x86_64-w64-mingw32"),
PathBuf::from("/usr/i686-w64-mingw32"),
PathBuf::from("/usr/x86_64-w64-mingw32/sys-root/mingw"),
PathBuf::from("/usr/i686-w64-mingw32/sys-root/mingw"),
PathBuf::from("/mingw64"),
PathBuf::from("/mingw32"),
PathBuf::from("C:\\msys64\\mingw64"),
PathBuf::from("C:\\msys64\\mingw32"),
PathBuf::from("C:\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64"),
PathBuf::from("C:\\mingw-w64\\i686-8.1.0-posix-dwarf-rt_v6-rev0\\mingw32"),
];
for candidate in &candidates {
if candidate.exists() {
let include = candidate.join("include");
let lib = candidate.join("lib");
if include.exists() && lib.exists() {
return Some(candidate.clone());
}
}
}
if let Ok(output) = ProcessCommand::new("x86_64-w64-mingw32-gcc")
.args(["-print-sysroot"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() {
return Some(p);
}
}
}
None
}
pub fn detect_gcc_cross_sysroot(&self) -> Option<PathBuf> {
let gcc_names = if self.triple.is_64bit() && self.triple.arch == Arch::X86_64 {
vec![
format!("{}-gcc", self.target_triple),
"x86_64-linux-gnu-gcc".to_string(),
"x86_64-linux-musl-gcc".to_string(),
"x86_64-elf-gcc".to_string(),
]
} else {
vec![
format!("{}-gcc", self.target_triple),
"i686-linux-gnu-gcc".to_string(),
"i386-linux-gnu-gcc".to_string(),
"i686-elf-gcc".to_string(),
]
};
for gcc_name in &gcc_names {
if let Ok(output) = ProcessCommand::new(gcc_name)
.args(["-print-sysroot"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !path.is_empty() {
let p = PathBuf::from(&path);
if p.exists() {
return Some(p);
}
}
}
}
}
let cross_prefix = format!("/usr/{}", self.target_triple);
let candidates = [
PathBuf::from(&cross_prefix),
PathBuf::from("/usr/cross"),
PathBuf::from("/opt/cross"),
];
for candidate in &candidates {
if candidate.exists() {
let include = candidate.join("include");
let lib = candidate.join("lib");
if include.exists() || lib.exists() {
return Some(candidate.clone());
}
}
}
None
}
pub fn is_host_triple(&self) -> bool {
let host_triple = Self::detect_host_triple();
let host_parsed = Triple::parse(&host_triple);
if host_parsed.arch != self.triple.arch {
return false;
}
match (host_parsed.os, self.triple.os) {
(OS::Linux, OS::Linux) => true,
(OS::Darwin, OS::Darwin) | (OS::Darwin, OS::MacOSX) | (OS::MacOSX, OS::Darwin) => true,
(OS::Win32, OS::Win32) => true,
(OS::FreeBSD, OS::FreeBSD) => true,
_ => false,
}
}
pub fn detect_host_triple() -> String {
let arch = if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "x86") {
"i386"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else if cfg!(target_arch = "arm") {
"arm"
} else if cfg!(target_arch = "riscv64") {
"riscv64"
} else {
"unknown"
};
let vendor = "unknown";
let (os, env) = if cfg!(target_os = "linux") {
if cfg!(target_env = "musl") {
("linux", "musl")
} else if cfg!(target_env = "gnu") {
("linux", "gnu")
} else {
("linux", "gnu")
}
} else if cfg!(target_os = "macos") {
("darwin", "unknown")
} else if cfg!(target_os = "windows") {
if cfg!(target_env = "msvc") {
("windows", "msvc")
} else {
("windows", "gnu")
}
} else if cfg!(target_os = "freebsd") {
("freebsd", "unknown")
} else {
("unknown", "unknown")
};
format!("{}-{}-{}-{}", arch, vendor, os, env)
}
pub fn list_sysroots(&self) -> Vec<(String, PathBuf)> {
let mut results = Vec::new();
results.push(("Host (native)".to_string(), PathBuf::from("/")));
let usr = PathBuf::from("/usr");
if usr.exists() {
results.push(("Linux /usr".to_string(), usr));
}
if let Some(ndk) = self.detect_android_ndk_sysroot() {
results.push(("Android NDK".to_string(), ndk));
}
if let Some(sdk) = self.detect_macos_sdk() {
results.push(("macOS SDK".to_string(), sdk));
}
if let Some(sdk) = self.detect_ios_sdk() {
results.push(("iOS SDK".to_string(), sdk));
}
if let Some(sdk) = self.detect_windows_sdk() {
results.push(("Windows SDK".to_string(), sdk));
}
if let Some(mingw) = self.detect_mingw_sysroot() {
results.push(("MinGW".to_string(), mingw));
}
if let Some(cross) = self.detect_gcc_cross_sysroot() {
results.push(("GCC Cross".to_string(), cross));
}
results
}
}
impl Default for X86SysrootDetector {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct X86CrossCompilation {
pub target_triple: String,
pub triple: Triple,
pub host_triple: String,
pub build_triple: String,
pub is_cross: bool,
pub is_canadian_cross: bool,
pub toolchain_prefix: Option<String>,
pub toolchain_bin_dir: Option<PathBuf>,
pub emulator: Option<String>,
pub emulator_args: Vec<String>,
pub pkg_config_wrapper: Option<PathBuf>,
pub sysroot: Option<PathBuf>,
pub cross_env: HashMap<String, String>,
}
impl X86CrossCompilation {
pub fn new(triple_str: &str) -> Self {
let triple = Triple::parse(triple_str);
let host_triple = X86SysrootDetector::detect_host_triple();
let build_triple = host_triple.clone();
let is_cross = !Self::is_same_architecture(&triple, &Triple::parse(&host_triple));
let toolchain_prefix = if is_cross {
Self::detect_toolchain_prefix(triple_str)
} else {
None
};
let (emulator, emulator_args) = Self::detect_emulator(&triple);
let pkg_config_wrapper = if is_cross {
Self::detect_pkg_config_wrapper(triple_str)
} else {
None
};
let mut cross_env = HashMap::new();
if is_cross {
if let Ok(val) = env::var("CROSS_COMPILE") {
cross_env.insert("CROSS_COMPILE".to_string(), val);
}
if let Ok(val) = env::var("PKG_CONFIG_SYSROOT_DIR") {
cross_env.insert("PKG_CONFIG_SYSROOT_DIR".to_string(), val);
}
if let Ok(val) = env::var("PKG_CONFIG_PATH") {
cross_env.insert("PKG_CONFIG_PATH".to_string(), val);
}
}
Self {
target_triple: triple_str.to_string(),
triple,
host_triple,
build_triple,
is_cross,
is_canadian_cross: false,
toolchain_prefix,
toolchain_bin_dir: None,
emulator,
emulator_args,
pkg_config_wrapper,
sysroot: None,
cross_env,
}
}
fn is_same_architecture(target: &Triple, host: &Triple) -> bool {
if target.arch == host.arch && target.os == host.os {
return true;
}
if target.arch == Arch::X86 && host.arch == Arch::X86_64 {
return target.os == host.os;
}
false
}
pub fn detect_toolchain_prefix(triple_str: &str) -> Option<String> {
let candidates = vec![
format!("{}-", triple_str),
triple_str.replace("unknown-", "").to_string() + "-",
];
for prefix in &candidates {
let gcc_name = format!("{}gcc", prefix);
if which::which(&gcc_name).is_ok() {
return Some(prefix.clone());
}
let clang_name = format!("{}clang", prefix);
if which::which(&clang_name).is_ok() {
return Some(prefix.clone());
}
}
None
}
pub fn detect_emulator(triple: &Triple) -> (Option<String>, Vec<String>) {
match triple.arch {
Arch::X86 => {
if cfg!(target_arch = "x86_64") {
if cfg!(target_os = "linux") && triple.os == OS::Linux {
return (None, Vec::new()); }
}
if which::which("qemu-i386").is_ok() {
return (Some("qemu-i386".to_string()), vec!["-L".to_string()]);
}
(None, Vec::new())
}
Arch::X86_64 => {
if triple.os == OS::Win32 {
if which::which("wine64").is_ok() {
return (Some("wine64".to_string()), Vec::new());
}
if which::which("wine").is_ok() {
return (Some("wine".to_string()), Vec::new());
}
}
if !cfg!(target_arch = "x86_64") && triple.os == OS::Linux {
if which::which("qemu-x86_64").is_ok() {
return (
Some("qemu-x86_64".to_string()),
vec!["-L".to_string(), "/usr/x86_64-linux-gnu".to_string()],
);
}
}
(None, Vec::new())
}
_ => {
let arch_name = triple.arch.as_str();
let qemu_name = format!("qemu-{}", arch_name);
if which::which(&qemu_name).is_ok() {
return (Some(qemu_name), vec!["-L".to_string()]);
}
(None, Vec::new())
}
}
}
pub fn detect_pkg_config_wrapper(triple_str: &str) -> Option<PathBuf> {
let wrapper_names = vec![
format!("{}-pkg-config", triple_str),
format!("{}-pkgconfig", triple_str),
];
for name in &wrapper_names {
if let Ok(path) = which::which(name) {
return Some(path);
}
}
None
}
pub fn host_to_target_mapping(&self) -> HashMap<String, Vec<String>> {
let mut map = HashMap::new();
map.insert(
"x86_64-unknown-linux-gnu".to_string(),
vec![
"x86_64-unknown-linux-gnu".to_string(),
"x86_64-unknown-linux-musl".to_string(),
"i386-unknown-linux-gnu".to_string(),
"x86_64-unknown-linux-gnux32".to_string(),
"x86_64-pc-windows-gnu".to_string(),
"x86_64-pc-windows-msvc".to_string(),
"i686-pc-windows-gnu".to_string(),
"x86_64-apple-darwin".to_string(),
"x86_64-unknown-freebsd".to_string(),
],
);
map.insert(
"x86_64-apple-darwin".to_string(),
vec![
"x86_64-apple-darwin".to_string(),
"x86_64-apple-ios".to_string(),
"aarch64-apple-darwin".to_string(),
"aarch64-apple-ios".to_string(),
],
);
map.insert(
"x86_64-pc-windows-msvc".to_string(),
vec![
"x86_64-pc-windows-msvc".to_string(),
"i686-pc-windows-msvc".to_string(),
"aarch64-pc-windows-msvc".to_string(),
"x86_64-pc-windows-gnu".to_string(),
],
);
map
}
pub fn setup_canadian_cross(
&mut self,
build: &str,
host: &str,
target: &str,
) -> Result<(), String> {
self.build_triple = build.to_string();
self.host_triple = host.to_string();
self.target_triple = target.to_string();
self.triple = Triple::parse(target);
self.is_cross = true;
self.is_canadian_cross = true;
let host_target_prefix = format!("{}-{}-", host, target);
self.toolchain_prefix = Self::detect_toolchain_prefix(&format!("{}-{}", host, target));
Ok(())
}
pub fn emulator_command(&self) -> Option<String> {
if let Some(ref emu) = self.emulator {
let mut cmd = emu.clone();
for arg in &self.emulator_args {
cmd.push(' ');
cmd.push_str(arg);
}
Some(cmd)
} else {
None
}
}
pub fn can_execute_natively(&self) -> bool {
if self.is_cross {
return false;
}
if self.triple.arch == Arch::X86 && cfg!(target_arch = "x86_64") {
return cfg!(target_os = "linux");
}
if self.triple.os == OS::Win32 {
return cfg!(target_os = "windows") || self.emulator.is_some();
}
!self.is_cross
}
pub fn is_host_triple(&self) -> bool {
let current_host = X86SysrootDetector::detect_host_triple();
self.host_triple == current_host
}
pub fn pkg_config_command(&self) -> String {
if let Some(ref wrapper) = self.pkg_config_wrapper {
return wrapper.to_string_lossy().to_string();
}
if let Some(ref prefix) = self.toolchain_prefix {
return format!("{}pkg-config", prefix);
}
"pkg-config".to_string()
}
pub fn pkg_config_env(&self) -> Vec<(String, String)> {
let mut env_vars = Vec::new();
if let Some(ref sysroot) = self.sysroot {
env_vars.push((
"PKG_CONFIG_SYSROOT_DIR".to_string(),
sysroot.to_string_lossy().to_string(),
));
}
if self.is_cross {
let lib_paths = format!(
"/usr/lib/{}/pkgconfig:/usr/share/pkgconfig",
self.target_triple
);
env_vars.push(("PKG_CONFIG_LIBDIR".to_string(), lib_paths));
}
env_vars
}
pub fn list_toolchains() -> Vec<String> {
let mut toolchains = Vec::new();
if let Ok(path) = env::var("PATH") {
for dir in path.split(':') {
let dir_path = PathBuf::from(dir);
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if (name.ends_with("-gcc") || name.ends_with("-clang"))
&& name.contains("linux")
{
let prefix = if name.ends_with("-gcc") {
name.strip_suffix("gcc").unwrap().to_string()
} else {
name.strip_suffix("clang").unwrap().to_string()
};
if !toolchains.contains(&prefix) {
toolchains.push(prefix);
}
}
}
}
}
}
toolchains.sort();
toolchains.dedup();
toolchains
}
}
impl Default for X86CrossCompilation {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct X86RemoteBuildConfig {
pub remote_host: Option<String>,
pub remote_user: Option<String>,
pub remote_sysroot: Option<PathBuf>,
pub use_distcc: bool,
pub use_icecc: bool,
pub remote_jobs: u32,
pub ssh_options: Vec<String>,
}
impl X86RemoteBuildConfig {
pub fn new() -> Self {
Self {
remote_host: None,
remote_user: None,
remote_sysroot: None,
use_distcc: false,
use_icecc: false,
remote_jobs: 4,
ssh_options: Vec::new(),
}
}
pub fn with_distcc(mut self, hosts: &[&str]) -> Self {
self.use_distcc = true;
self.remote_host = Some(hosts.join(" ")).filter(|s| !s.is_empty());
self
}
pub fn with_icecc(mut self) -> Self {
self.use_icecc = true;
self
}
pub fn compiler_wrapper(&self) -> Option<String> {
if self.use_distcc {
Some("distcc".to_string())
} else if self.use_icecc {
Some("icecc".to_string())
} else {
None
}
}
}
impl Default for X86RemoteBuildConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CrossPackageManager {
pub use_apt_cross: bool,
pub use_dnf_cross: bool,
pub use_pacman_cross: bool,
pub cross_arch_suffix: String,
}
impl X86CrossPackageManager {
pub fn new(target_triple: &str) -> Self {
let triple = Triple::parse(target_triple);
let cross_arch_suffix = match triple.arch {
Arch::X86_64 => ":amd64",
Arch::X86 => ":i386",
Arch::AArch64 => ":arm64",
Arch::ARM => {
if triple.environment == Environment::GNUEABIHF {
":armhf"
} else {
":armel"
}
}
Arch::RISCV64 => ":riscv64",
_ => "",
}
.to_string();
let use_apt_cross = which::which("apt-get").is_ok();
let use_dnf_cross = which::which("dnf").is_ok();
let use_pacman_cross = which::which("pacman").is_ok();
Self {
use_apt_cross,
use_dnf_cross,
use_pacman_cross,
cross_arch_suffix,
}
}
pub fn install_cross_deps_command(&self) -> Option<String> {
if self.use_apt_cross && !self.cross_arch_suffix.is_empty() {
Some(format!(
"dpkg --add-architecture {} && apt-get update && apt-get install -y crossbuild-essential{}",
self.cross_arch_suffix.trim_start_matches(':'),
self.cross_arch_suffix
))
} else if self.use_dnf_cross {
Some("dnf install -y gcc-c++-cross binutils-cross".to_string())
} else if self.use_pacman_cross {
Some("pacman -S --noconfirm cross-compiler".to_string())
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct X86QemuEmulation {
pub available: bool,
pub qemu_binary: Option<PathBuf>,
pub binfmt_configured: bool,
pub qemu_flags: Vec<String>,
pub qemu_arch_name: String,
}
impl X86QemuEmulation {
pub fn detect(target_arch: Arch) -> Self {
let qemu_arch_name = match target_arch {
Arch::X86_64 => "x86_64",
Arch::X86 => "i386",
Arch::AArch64 => "aarch64",
Arch::ARM => "arm",
Arch::RISCV64 => "riscv64",
Arch::RISCV32 => "riscv32",
Arch::Mips => "mips",
Arch::Mips64 => "mips64",
Arch::Mipsel => "mipsel",
Arch::Mips64el => "mips64el",
Arch::PowerPC => "ppc",
Arch::PowerPC64 => "ppc64",
Arch::PowerPC64le => "ppc64le",
Arch::Sparc => "sparc",
Arch::Sparcv9 => "sparc64",
Arch::SystemZ => "s390x",
_ => target_arch.as_str(),
}
.to_string();
let qemu_name = format!("qemu-{}", qemu_arch_name);
let qemu_static_name = format!("qemu-{}-static", qemu_arch_name);
let (available, qemu_binary) = if let Ok(path) = which::which(&qemu_static_name) {
(true, Some(path))
} else if let Ok(path) = which::which(&qemu_name) {
(true, Some(path))
} else {
(false, None)
};
let binfmt_configured = Path::new("/proc/sys/fs/binfmt_misc/qemu-x86_64").exists()
|| Path::new("/proc/sys/fs/binfmt_misc/qemu-i386").exists();
let qemu_flags = vec![
"-L".to_string(),
format!("/usr/{}", qemu_arch_name),
"-E".to_string(),
format!("LD_LIBRARY_PATH=/usr/{}/lib", qemu_arch_name),
];
Self {
available,
qemu_binary,
binfmt_configured,
qemu_flags,
qemu_arch_name,
}
}
pub fn emulation_command(&self, binary: &str) -> Option<String> {
if let Some(ref qemu) = self.qemu_binary {
let mut cmd = qemu.to_string_lossy().to_string();
for flag in &self.qemu_flags {
cmd.push(' ');
cmd.push_str(flag);
}
cmd.push(' ');
cmd.push_str(binary);
Some(cmd)
} else {
None
}
}
pub fn can_transparently_emulate(&self) -> bool {
self.binfmt_configured
}
}
#[derive(Debug, Clone)]
pub struct X86Multiarch {
pub triple: Triple,
pub multiarch_dir: Option<String>,
pub rpm_lib_dir: Option<String>,
pub prefer_multiarch: bool,
pub search_order: Vec<PathBuf>,
pub dynamic_linker_path: Option<String>,
pub is_debian: bool,
pub is_rpm: bool,
}
impl X86Multiarch {
pub fn new(triple: &Triple) -> Self {
let multiarch_dir = Self::determine_multiarch_dir(triple);
let rpm_lib_dir = Self::determine_rpm_lib_dir(triple);
let is_debian = Self::detect_debian();
let is_rpm = Self::detect_rpm();
let dynamic_linker_path = Self::determine_dynamic_linker(triple);
let mut search_order = Vec::new();
if is_debian {
if let Some(ref dir) = multiarch_dir {
search_order.push(PathBuf::from("/usr/lib").join(dir));
}
}
if is_rpm {
if let Some(ref dir) = rpm_lib_dir {
search_order.push(PathBuf::from(dir));
}
}
search_order.push(PathBuf::from("/usr/lib"));
search_order.push(PathBuf::from("/usr/local/lib"));
search_order.push(PathBuf::from("/lib"));
Self {
triple: triple.clone(),
multiarch_dir,
rpm_lib_dir,
prefer_multiarch: is_debian,
search_order,
dynamic_linker_path,
is_debian,
is_rpm,
}
}
pub fn determine_multiarch_dir(triple: &Triple) -> Option<String> {
match triple.arch {
Arch::X86_64 => match triple.environment {
Environment::GNUX32 => Some("x86_64-linux-gnux32".to_string()),
Environment::GNU | Environment::GNUABI64 => Some("x86_64-linux-gnu".to_string()),
Environment::Musl => Some("x86_64-linux-musl".to_string()),
Environment::Android => Some("x86_64-linux-android".to_string()),
_ => Some("x86_64-linux-gnu".to_string()),
},
Arch::X86 => match triple.environment {
Environment::GNU | Environment::GNUEABI => Some("i386-linux-gnu".to_string()),
Environment::Musl => Some("i386-linux-musl".to_string()),
Environment::Android => Some("i686-linux-android".to_string()),
_ => Some("i386-linux-gnu".to_string()),
},
Arch::AArch64 => match triple.environment {
Environment::GNU => Some("aarch64-linux-gnu".to_string()),
Environment::Musl => Some("aarch64-linux-musl".to_string()),
Environment::Android => Some("aarch64-linux-android".to_string()),
_ => Some("aarch64-linux-gnu".to_string()),
},
Arch::ARM => match triple.environment {
Environment::GNUEABIHF => Some("arm-linux-gnueabihf".to_string()),
Environment::GNUEABI => Some("arm-linux-gnueabi".to_string()),
Environment::Musl => Some("arm-linux-musleabihf".to_string()),
Environment::Android => Some("arm-linux-androideabi".to_string()),
_ => Some("arm-linux-gnueabihf".to_string()),
},
Arch::RISCV64 => Some("riscv64-linux-gnu".to_string()),
Arch::RISCV32 => Some("riscv32-linux-gnu".to_string()),
Arch::PowerPC64le => Some("powerpc64le-linux-gnu".to_string()),
Arch::Sparcv9 => Some("sparc64-linux-gnu".to_string()),
Arch::SystemZ => Some("s390x-linux-gnu".to_string()),
_ => None,
}
}
fn determine_rpm_lib_dir(triple: &Triple) -> Option<String> {
if triple.arch.is_64bit() {
Some("/usr/lib64".to_string())
} else {
Some("/usr/lib".to_string())
}
}
fn detect_debian() -> bool {
Path::new("/etc/debian_version").exists()
|| Path::new("/etc/dpkg/origins/debian").exists()
|| Path::new("/usr/share/debconf").exists()
}
fn detect_rpm() -> bool {
Path::new("/etc/redhat-release").exists()
|| Path::new("/etc/fedora-release").exists()
|| Path::new("/etc/centos-release").exists()
|| Path::new("/etc/SuSE-release").exists()
|| Path::new("/etc/os-release").exists() && {
if let Ok(content) = fs::read_to_string("/etc/os-release") {
content.contains("rhel")
|| content.contains("fedora")
|| content.contains("centos")
|| content.contains("suse")
} else {
false
}
}
}
pub fn determine_dynamic_linker(triple: &Triple) -> Option<String> {
match triple.arch {
Arch::X86_64 => match triple.environment {
Environment::Musl => Some("/lib/ld-musl-x86_64.so.1".to_string()),
Environment::Android => Some("/system/bin/linker64".to_string()),
_ => Some("/lib64/ld-linux-x86-64.so.2".to_string()),
},
Arch::X86 => match triple.environment {
Environment::Musl => Some("/lib/ld-musl-i386.so.1".to_string()),
Environment::Android => Some("/system/bin/linker".to_string()),
_ => Some("/lib/ld-linux.so.2".to_string()),
},
Arch::AArch64 => match triple.environment {
Environment::Musl => Some("/lib/ld-musl-aarch64.so.1".to_string()),
Environment::Android => Some("/system/bin/linker64".to_string()),
_ => Some("/lib/ld-linux-aarch64.so.1".to_string()),
},
Arch::ARM => {
if triple.environment == Environment::GNUEABIHF {
Some("/lib/ld-linux-armhf.so.3".to_string())
} else {
Some("/lib/ld-linux.so.3".to_string())
}
}
Arch::RISCV64 => Some("/lib/ld-linux-riscv64-lp64d.so.1".to_string()),
Arch::PowerPC64le => Some("/lib64/ld64.so.2".to_string()),
Arch::SystemZ => Some("/lib/ld64.so.1".to_string()),
_ => None,
}
}
pub fn dynamic_linker(&self, triple: &Triple) -> Option<String> {
Self::determine_dynamic_linker(triple)
}
pub fn library_paths(&self, sysroot: &Sysroot) -> Vec<PathBuf> {
let mut paths = Vec::new();
if self.prefer_multiarch {
if let Some(ref dir) = self.multiarch_dir {
let multiarch_path = sysroot.path.join("usr").join("lib").join(dir);
if multiarch_path.exists() {
paths.push(multiarch_path);
}
let multiarch_lib = sysroot.path.join("lib").join(dir);
if multiarch_lib.exists() {
paths.push(multiarch_lib);
}
}
}
if self.is_rpm {
if let Some(ref dir) = self.rpm_lib_dir {
let rpm_path = sysroot.path.join(dir.trim_start_matches('/'));
if rpm_path.exists() {
paths.push(rpm_path);
}
}
}
for lib_dir in &["usr/lib", "usr/local/lib", "lib"] {
let p = sysroot.path.join(lib_dir);
if p.exists() && !paths.contains(&p) {
paths.push(p);
}
}
if let Some(ref dir) = self.multiarch_dir {
let gcc_lib = sysroot.path.join("usr").join("lib").join("gcc").join(dir);
if gcc_lib.exists() {
if let Ok(entries) = fs::read_dir(&gcc_lib) {
for entry in entries.flatten() {
let p = entry.path();
if p.is_dir() {
paths.push(p);
}
}
}
}
}
paths
}
pub fn include_paths(&self, sysroot: &Sysroot) -> Vec<PathBuf> {
let mut paths = Vec::new();
if let Some(ref dir) = self.multiarch_dir {
let inc_dir = sysroot.path.join("usr").join("include").join(dir);
if inc_dir.exists() {
paths.push(inc_dir);
}
let gcc_inc = sysroot
.path
.join("usr")
.join("lib")
.join("gcc")
.join(dir)
.join("include");
if gcc_inc.exists() {
paths.push(gcc_inc);
}
}
paths
}
pub fn normalize_library_path(&self, path: &Path) -> PathBuf {
if self.is_debian && self.prefer_multiarch {
if let Some(ref dir) = self.multiarch_dir {
let standard_lib = PathBuf::from("/usr/lib");
if path.starts_with(&standard_lib) {
let multiarch_lib = standard_lib.join(dir);
if multiarch_lib.exists() {
return multiarch_lib;
}
}
}
}
path.to_path_buf()
}
pub fn is_multiarch_path(&self, path: &Path) -> bool {
if let Some(ref dir) = self.multiarch_dir {
path.to_string_lossy().contains(dir.as_str())
} else {
false
}
}
pub fn known_multiarch_tuples() -> Vec<String> {
let mut tuples = Vec::new();
if let Ok(entries) = fs::read_dir("/usr/lib") {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.contains("-linux-") || name.contains("-gnu") {
if entry.path().is_dir() {
tuples.push(name);
}
}
}
}
tuples.sort();
tuples
}
pub fn search_order_priority(&self) -> Vec<(&'static str, u32)> {
let mut order = Vec::new();
if self.is_debian {
order.push(("multiarch", 1));
order.push(("rpm", 2));
} else if self.is_rpm {
order.push(("rpm", 1));
order.push(("multiarch", 2));
}
order.push(("standard", 3));
order.push(("local", 4));
order.push(("gcc", 5));
order
}
pub fn detect_distribution() -> X86LinuxDistro {
if Path::new("/etc/arch-release").exists() {
return X86LinuxDistro::Arch;
}
if Path::new("/etc/gentoo-release").exists() {
return X86LinuxDistro::Gentoo;
}
if Path::new("/etc/alpine-release").exists() {
return X86LinuxDistro::Alpine;
}
if Path::new("/etc/nixos").exists() || Path::new("/nix/store").exists() {
return X86LinuxDistro::NixOS;
}
if Path::new("/etc/openwrt_release").exists() {
return X86LinuxDistro::OpenWrt;
}
if Path::new("/etc/SuSE-release").exists()
|| (Path::new("/etc/os-release").exists() && {
if let Ok(content) = fs::read_to_string("/etc/os-release") {
content.contains("opensuse") || content.contains("suse")
} else {
false
}
})
{
return X86LinuxDistro::OpenSUSE;
}
if Self::detect_debian() {
return X86LinuxDistro::Debian;
}
if Self::detect_rpm() {
if Path::new("/etc/fedora-release").exists() {
return X86LinuxDistro::Fedora;
}
return X86LinuxDistro::RHEL;
}
X86LinuxDistro::Unknown
}
pub fn all_debian_multiarch_tuples() -> Vec<&'static str> {
vec![
"x86_64-linux-gnu",
"x86_64-linux-gnux32",
"x86_64-linux-musl",
"x86_64-linux-android",
"i386-linux-gnu",
"i386-linux-musl",
"i686-linux-android",
"aarch64-linux-gnu",
"aarch64-linux-musl",
"aarch64-linux-android",
"arm-linux-gnueabi",
"arm-linux-gnueabihf",
"arm-linux-musleabi",
"arm-linux-musleabihf",
"arm-linux-androideabi",
"riscv64-linux-gnu",
"riscv32-linux-gnu",
"powerpc64le-linux-gnu",
"powerpc-linux-gnu",
"sparc64-linux-gnu",
"s390x-linux-gnu",
"mips-linux-gnu",
"mipsel-linux-gnu",
"mips64-linux-gnuabi64",
"mips64el-linux-gnuabi64",
]
}
pub fn libc_dir_suffix(triple: &Triple) -> &'static str {
match triple.environment {
Environment::Musl => "-musl",
Environment::Android | Environment::Androideabi => "-android",
_ => "-gnu",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86LinuxDistro {
Unknown,
Debian,
RHEL,
Fedora,
OpenSUSE,
Arch,
Gentoo,
Alpine,
NixOS,
OpenWrt,
}
impl X86LinuxDistro {
pub fn as_str(&self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::Debian => "debian",
Self::RHEL => "rhel",
Self::Fedora => "fedora",
Self::OpenSUSE => "opensuse",
Self::Arch => "arch",
Self::Gentoo => "gentoo",
Self::Alpine => "alpine",
Self::NixOS => "nixos",
Self::OpenWrt => "openwrt",
}
}
pub fn uses_multiarch(&self) -> bool {
matches!(self, Self::Debian)
}
pub fn uses_lib64(&self) -> bool {
matches!(
self,
Self::RHEL | Self::Fedora | Self::OpenSUSE | Self::Arch | Self::Gentoo
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UbuntuVariant {
Vanilla,
Kubuntu,
Xubuntu,
Lubuntu,
UbuntuServer,
UbuntuCore,
LinuxMint,
PopOS,
Elementary,
}
impl X86LinuxDistro {
pub fn detect_ubuntu_variant() -> Option<UbuntuVariant> {
if let Ok(content) = fs::read_to_string("/etc/os-release") {
let lower = content.to_lowercase();
if lower.contains("kubuntu") {
return Some(UbuntuVariant::Kubuntu);
}
if lower.contains("xubuntu") {
return Some(UbuntuVariant::Xubuntu);
}
if lower.contains("lubuntu") {
return Some(UbuntuVariant::Lubuntu);
}
if lower.contains("ubuntu-core") || lower.contains("ubuntu core") {
return Some(UbuntuVariant::UbuntuCore);
}
if lower.contains("linux mint") || lower.contains("linuxmint") {
return Some(UbuntuVariant::LinuxMint);
}
if lower.contains("pop") && lower.contains("os") {
return Some(UbuntuVariant::PopOS);
}
if lower.contains("elementary") {
return Some(UbuntuVariant::Elementary);
}
if lower.contains("ubuntu") {
if lower.contains("server") {
return Some(UbuntuVariant::UbuntuServer);
}
return Some(UbuntuVariant::Vanilla);
}
}
None
}
}
impl Default for X86Multiarch {
fn default() -> Self {
Self::new(&Triple::x86_64_linux())
}
}
#[derive(Debug, Clone)]
pub struct X86TripleParser {
pub original: String,
pub triple: Triple,
pub arch_info: X86ArchInfo,
pub os_info: X86OsInfo,
pub object_format: X86ObjectFormat,
pub is_valid: bool,
pub normalized: String,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86ArchInfo {
pub name: String,
pub family: X86ArchFamily,
pub bit_width: u32,
pub is_extended: bool,
pub cpu_sub_arch: Option<String>,
pub has_sse2: bool,
pub default_cpu: &'static str,
pub default_fpu: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86ArchFamily {
X86_16,
X86_32,
X86_64,
X32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86OsInfo {
pub name: String,
pub default_object_format: X86ObjectFormat,
pub default_libc: X86LibC,
pub supports_multiarch: bool,
pub include_pattern: String,
pub lib_pattern: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86ObjectFormat {
ELF,
MachO,
COFF,
Wasm,
XCOFF,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86LibC {
Glibc,
Musl,
Bionic,
LibSystem,
Msvcrt,
UCRT,
Newlib,
Picolibc,
None_,
}
impl X86TripleParser {
pub fn new(triple_str: &str) -> Self {
let triple = Triple::parse(triple_str);
let arch_info = Self::parse_arch_details(&triple);
let os_info = Self::parse_os_details(&triple);
let object_format = Self::infer_object_format(&triple);
let normalized = Self::normalize_triple(&triple);
let is_valid = triple.arch != Arch::Unknown && triple.os != OS::Unknown;
let warnings = Self::check_warnings(&triple);
Self {
original: triple_str.to_string(),
triple,
arch_info,
os_info,
object_format,
is_valid,
normalized,
warnings,
}
}
pub fn parse_arch_details(triple: &Triple) -> X86ArchInfo {
match triple.arch {
Arch::X86_64 => X86ArchInfo {
name: "x86_64".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: false,
cpu_sub_arch: None,
has_sse2: true, default_cpu: "x86-64",
default_fpu: "sse2",
},
Arch::X86 => X86ArchInfo {
name: "i386".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: false,
cpu_sub_arch: None,
has_sse2: false,
default_cpu: "i686",
default_fpu: "x87",
},
_ => X86ArchInfo {
name: triple.arch.as_str().to_string(),
family: if triple.arch.is_64bit() {
X86ArchFamily::X86_64
} else {
X86ArchFamily::X86_32
},
bit_width: if triple.arch.is_64bit() { 64 } else { 32 },
is_extended: false,
cpu_sub_arch: None,
has_sse2: triple.arch.is_64bit(),
default_cpu: "generic",
default_fpu: "x87",
},
}
}
pub fn parse_os_details(triple: &Triple) -> X86OsInfo {
match triple.os {
OS::Linux => {
let libc = match triple.environment {
Environment::Musl => X86LibC::Musl,
Environment::Android | Environment::Androideabi => X86LibC::Bionic,
_ => X86LibC::Glibc,
};
X86OsInfo {
name: "linux".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: libc,
supports_multiarch: true,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
}
}
OS::Darwin | OS::MacOSX => X86OsInfo {
name: "darwin".to_string(),
default_object_format: X86ObjectFormat::MachO,
default_libc: X86LibC::LibSystem,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::IOS => X86OsInfo {
name: "ios".to_string(),
default_object_format: X86ObjectFormat::MachO,
default_libc: X86LibC::LibSystem,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::TvOS => X86OsInfo {
name: "tvos".to_string(),
default_object_format: X86ObjectFormat::MachO,
default_libc: X86LibC::LibSystem,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::WatchOS => X86OsInfo {
name: "watchos".to_string(),
default_object_format: X86ObjectFormat::MachO,
default_libc: X86LibC::LibSystem,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::Win32 => {
let libc = match triple.environment {
Environment::MSVC => X86LibC::Msvcrt,
Environment::GNU | Environment::Cygnus => X86LibC::Msvcrt,
_ => X86LibC::UCRT,
};
X86OsInfo {
name: "windows".to_string(),
default_object_format: X86ObjectFormat::COFF,
default_libc: libc,
supports_multiarch: false,
include_pattern: "/include".to_string(),
lib_pattern: "/lib".to_string(),
}
}
OS::FreeBSD => X86OsInfo {
name: "freebsd".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: X86LibC::Glibc,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::NetBSD => X86OsInfo {
name: "netbsd".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: X86LibC::Glibc,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::OpenBSD => X86OsInfo {
name: "openbsd".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: X86LibC::Glibc,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
OS::Haiku => X86OsInfo {
name: "haiku".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: X86LibC::Glibc,
supports_multiarch: false,
include_pattern: "/boot/system/develop/headers".to_string(),
lib_pattern: "/boot/system/lib".to_string(),
},
OS::WASI => X86OsInfo {
name: "wasi".to_string(),
default_object_format: X86ObjectFormat::Wasm,
default_libc: X86LibC::None_,
supports_multiarch: false,
include_pattern: "/include".to_string(),
lib_pattern: "/lib".to_string(),
},
_ => X86OsInfo {
name: "unknown".to_string(),
default_object_format: X86ObjectFormat::ELF,
default_libc: X86LibC::Glibc,
supports_multiarch: false,
include_pattern: "/usr/include".to_string(),
lib_pattern: "/usr/lib".to_string(),
},
}
}
pub fn infer_object_format(triple: &Triple) -> X86ObjectFormat {
if triple.is_elf() {
X86ObjectFormat::ELF
} else if triple.is_macho() {
X86ObjectFormat::MachO
} else if triple.is_coff() {
X86ObjectFormat::COFF
} else if triple.os == OS::WASI {
X86ObjectFormat::Wasm
} else if triple.os == OS::AIX {
X86ObjectFormat::XCOFF
} else {
X86ObjectFormat::ELF
}
}
pub fn normalize_triple(triple: &Triple) -> String {
let arch_str = match triple.arch {
Arch::X86 => "i386",
_ => triple.arch.as_str(),
};
let vendor_str = triple.vendor.as_str();
let os_str = triple.os.as_str();
let env_str = match (triple.os, triple.environment) {
(OS::Linux, Environment::GNU) => "gnu",
(OS::Linux, Environment::Musl) => "musl",
(OS::Linux, Environment::Android | Environment::Androideabi) => "android",
(OS::Darwin | OS::MacOSX, _) => "",
(OS::Win32, Environment::MSVC) => "msvc",
(OS::Win32, Environment::GNU) => "mingw",
_ => triple.environment.as_str(),
};
if env_str.is_empty() || env_str == "unknown" {
format!("{}-{}-{}", arch_str, vendor_str, os_str)
} else {
format!("{}-{}-{}-{}", arch_str, vendor_str, os_str, env_str)
}
}
fn check_warnings(triple: &Triple) -> Vec<String> {
let mut warnings = Vec::new();
if triple.arch == Arch::Unknown {
warnings.push(format!("Unknown architecture: {}", triple.arch.as_str()));
}
if triple.os == OS::Unknown {
warnings.push(format!("Unknown OS: {}", triple.os.as_str()));
}
if triple.arch == Arch::X86_64 && triple.os == OS::IOS {
warnings.push("x86_64-ios is deprecated; use arm64 for iOS targets".to_string());
}
warnings
}
pub fn parse_x86_arch(name: &str) -> Option<X86ArchInfo> {
let info = match name {
"i386" => X86ArchInfo {
name: name.to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: false,
cpu_sub_arch: Some(name.to_string()),
has_sse2: false,
default_cpu: "i386",
default_fpu: "x87",
},
"i486" => X86ArchInfo {
name: name.to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: false,
cpu_sub_arch: Some(name.to_string()),
has_sse2: false,
default_cpu: "i486",
default_fpu: "x87",
},
"i586" => X86ArchInfo {
name: name.to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: false,
cpu_sub_arch: Some(name.to_string()),
has_sse2: false,
default_cpu: "i586",
default_fpu: "x87",
},
"i686" => X86ArchInfo {
name: "i686".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: false,
cpu_sub_arch: Some("i686".to_string()),
has_sse2: false,
default_cpu: "i686",
default_fpu: "x87",
},
"pentium" => X86ArchInfo {
name: "pentium".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentium".to_string()),
has_sse2: false,
default_cpu: "pentium",
default_fpu: "x87",
},
"pentium2" | "pentiumii" => X86ArchInfo {
name: "pentium2".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentium2".to_string()),
has_sse2: false,
default_cpu: "pentium2",
default_fpu: "x87",
},
"pentium3" | "pentiumiii" => X86ArchInfo {
name: "pentium3".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentium3".to_string()),
has_sse2: false,
default_cpu: "pentium3",
default_fpu: "sse",
},
"pentium4" | "pentiumiv" => X86ArchInfo {
name: "pentium4".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentium4".to_string()),
has_sse2: true,
default_cpu: "pentium4",
default_fpu: "sse2",
},
"pentium-m" | "pentiumm" => X86ArchInfo {
name: "pentium-m".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentium-m".to_string()),
has_sse2: true,
default_cpu: "pentium-m",
default_fpu: "sse2",
},
"athlon" | "athlon-xp" => X86ArchInfo {
name: "athlon".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("athlon".to_string()),
has_sse2: false,
default_cpu: "athlon",
default_fpu: "x87",
},
"athlon64" | "athlon-fx" => X86ArchInfo {
name: "athlon64".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("athlon64".to_string()),
has_sse2: true,
default_cpu: "athlon64",
default_fpu: "sse2",
},
"k8" | "opteron" => X86ArchInfo {
name: "k8".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("k8".to_string()),
has_sse2: true,
default_cpu: "k8",
default_fpu: "sse2",
},
"k8-sse3" | "opteron-sse3" => X86ArchInfo {
name: "k8-sse3".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("k8-sse3".to_string()),
has_sse2: true,
default_cpu: "k8-sse3",
default_fpu: "sse2",
},
"amdfam10" | "barcelona" => X86ArchInfo {
name: "amdfam10".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("amdfam10".to_string()),
has_sse2: true,
default_cpu: "amdfam10",
default_fpu: "sse2",
},
"btver1" => X86ArchInfo {
name: "btver1".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("btver1".to_string()),
has_sse2: true,
default_cpu: "btver1",
default_fpu: "sse2",
},
"btver2" => X86ArchInfo {
name: "btver2".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("btver2".to_string()),
has_sse2: true,
default_cpu: "btver2",
default_fpu: "sse2",
},
"bdver1" | "bulldozer" => X86ArchInfo {
name: "bdver1".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("bdver1".to_string()),
has_sse2: true,
default_cpu: "bdver1",
default_fpu: "sse2",
},
"bdver2" | "piledriver" => X86ArchInfo {
name: "bdver2".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("bdver2".to_string()),
has_sse2: true,
default_cpu: "bdver2",
default_fpu: "sse2",
},
"bdver3" | "steamroller" => X86ArchInfo {
name: "bdver3".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("bdver3".to_string()),
has_sse2: true,
default_cpu: "bdver3",
default_fpu: "sse2",
},
"bdver4" | "excavator" => X86ArchInfo {
name: "bdver4".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("bdver4".to_string()),
has_sse2: true,
default_cpu: "bdver4",
default_fpu: "sse2",
},
"znver1" | "zen" => X86ArchInfo {
name: "znver1".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("znver1".to_string()),
has_sse2: true,
default_cpu: "znver1",
default_fpu: "sse2",
},
"znver2" | "zen2" => X86ArchInfo {
name: "znver2".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("znver2".to_string()),
has_sse2: true,
default_cpu: "znver2",
default_fpu: "sse2",
},
"znver3" | "zen3" => X86ArchInfo {
name: "znver3".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("znver3".to_string()),
has_sse2: true,
default_cpu: "znver3",
default_fpu: "sse2",
},
"znver4" | "zen4" => X86ArchInfo {
name: "znver4".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("znver4".to_string()),
has_sse2: true,
default_cpu: "znver4",
default_fpu: "sse2",
},
"znver5" | "zen5" => X86ArchInfo {
name: "znver5".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("znver5".to_string()),
has_sse2: true,
default_cpu: "znver5",
default_fpu: "sse2",
},
"core2" | "core2duo" | "penryn" => X86ArchInfo {
name: "core2".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("core2".to_string()),
has_sse2: true,
default_cpu: "core2",
default_fpu: "sse2",
},
"nehalem" | "corei7" | "core-i7" => X86ArchInfo {
name: "nehalem".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("nehalem".to_string()),
has_sse2: true,
default_cpu: "nehalem",
default_fpu: "sse2",
},
"westmere" => X86ArchInfo {
name: "westmere".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("westmere".to_string()),
has_sse2: true,
default_cpu: "westmere",
default_fpu: "sse2",
},
"sandybridge" | "sandy-bridge" | "corei7-2" => X86ArchInfo {
name: "sandybridge".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("sandybridge".to_string()),
has_sse2: true,
default_cpu: "sandybridge",
default_fpu: "sse2",
},
"ivybridge" | "ivy-bridge" | "corei7-3" => X86ArchInfo {
name: "ivybridge".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("ivybridge".to_string()),
has_sse2: true,
default_cpu: "ivybridge",
default_fpu: "sse2",
},
"haswell" | "corei7-4" => X86ArchInfo {
name: "haswell".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("haswell".to_string()),
has_sse2: true,
default_cpu: "haswell",
default_fpu: "sse2",
},
"broadwell" | "corei7-5" => X86ArchInfo {
name: "broadwell".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("broadwell".to_string()),
has_sse2: true,
default_cpu: "broadwell",
default_fpu: "sse2",
},
"skylake" | "skylake-client" | "corei7-6" => X86ArchInfo {
name: "skylake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("skylake".to_string()),
has_sse2: true,
default_cpu: "skylake",
default_fpu: "sse2",
},
"skylake-avx512" => X86ArchInfo {
name: "skylake-avx512".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("skylake-avx512".to_string()),
has_sse2: true,
default_cpu: "skylake-avx512",
default_fpu: "sse2",
},
"cannonlake" => X86ArchInfo {
name: "cannonlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("cannonlake".to_string()),
has_sse2: true,
default_cpu: "cannonlake",
default_fpu: "sse2",
},
"icelake-client" | "icelake" => X86ArchInfo {
name: "icelake-client".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("icelake-client".to_string()),
has_sse2: true,
default_cpu: "icelake-client",
default_fpu: "sse2",
},
"icelake-server" => X86ArchInfo {
name: "icelake-server".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("icelake-server".to_string()),
has_sse2: true,
default_cpu: "icelake-server",
default_fpu: "sse2",
},
"tigerlake" => X86ArchInfo {
name: "tigerlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("tigerlake".to_string()),
has_sse2: true,
default_cpu: "tigerlake",
default_fpu: "sse2",
},
"sapphirerapids" => X86ArchInfo {
name: "sapphirerapids".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("sapphirerapids".to_string()),
has_sse2: true,
default_cpu: "sapphirerapids",
default_fpu: "sse2",
},
"alderlake" => X86ArchInfo {
name: "alderlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("alderlake".to_string()),
has_sse2: true,
default_cpu: "alderlake",
default_fpu: "sse2",
},
"raptorlake" => X86ArchInfo {
name: "raptorlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("raptorlake".to_string()),
has_sse2: true,
default_cpu: "raptorlake",
default_fpu: "sse2",
},
"meteorlake" => X86ArchInfo {
name: "meteorlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("meteorlake".to_string()),
has_sse2: true,
default_cpu: "meteorlake",
default_fpu: "sse2",
},
"graniterapids" => X86ArchInfo {
name: "graniterapids".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("graniterapids".to_string()),
has_sse2: true,
default_cpu: "graniterapids",
default_fpu: "sse2",
},
"arrowlake" | "arrowlake-s" => X86ArchInfo {
name: "arrowlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("arrowlake".to_string()),
has_sse2: true,
default_cpu: "arrowlake",
default_fpu: "sse2",
},
"lunarlake" => X86ArchInfo {
name: "lunarlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("lunarlake".to_string()),
has_sse2: true,
default_cpu: "lunarlake",
default_fpu: "sse2",
},
"pantherlake" => X86ArchInfo {
name: "pantherlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("pantherlake".to_string()),
has_sse2: true,
default_cpu: "pantherlake",
default_fpu: "sse2",
},
"bonnell" | "atom" => X86ArchInfo {
name: "bonnell".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("bonnell".to_string()),
has_sse2: true,
default_cpu: "bonnell",
default_fpu: "sse2",
},
"silvermont" => X86ArchInfo {
name: "silvermont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("silvermont".to_string()),
has_sse2: true,
default_cpu: "silvermont",
default_fpu: "sse2",
},
"goldmont" => X86ArchInfo {
name: "goldmont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("goldmont".to_string()),
has_sse2: true,
default_cpu: "goldmont",
default_fpu: "sse2",
},
"goldmont-plus" => X86ArchInfo {
name: "goldmont-plus".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("goldmont-plus".to_string()),
has_sse2: true,
default_cpu: "goldmont-plus",
default_fpu: "sse2",
},
"tremont" => X86ArchInfo {
name: "tremont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("tremont".to_string()),
has_sse2: true,
default_cpu: "tremont",
default_fpu: "sse2",
},
"gracemont" => X86ArchInfo {
name: "gracemont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("gracemont".to_string()),
has_sse2: true,
default_cpu: "gracemont",
default_fpu: "sse2",
},
"crestmont" => X86ArchInfo {
name: "crestmont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("crestmont".to_string()),
has_sse2: true,
default_cpu: "crestmont",
default_fpu: "sse2",
},
"skymont" => X86ArchInfo {
name: "skymont".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("skymont".to_string()),
has_sse2: true,
default_cpu: "skymont",
default_fpu: "sse2",
},
"x86-64" | "x86_64" | "amd64" | "x64" => X86ArchInfo {
name: "x86-64".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: false,
cpu_sub_arch: None,
has_sse2: true,
default_cpu: "x86-64",
default_fpu: "sse2",
},
"x32" | "x86_64-x32" => X86ArchInfo {
name: "x32".to_string(),
family: X86ArchFamily::X32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("x32".to_string()),
has_sse2: true,
default_cpu: "x86-64",
default_fpu: "sse2",
},
"knl" | "knightslanding" => X86ArchInfo {
name: "knl".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("knl".to_string()),
has_sse2: true,
default_cpu: "knl",
default_fpu: "sse2",
},
"knm" | "knightsmill" => X86ArchInfo {
name: "knm".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("knm".to_string()),
has_sse2: true,
default_cpu: "knm",
default_fpu: "sse2",
},
"c3" => X86ArchInfo {
name: "c3".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("c3".to_string()),
has_sse2: false,
default_cpu: "c3",
default_fpu: "x87",
},
"c3-2" => X86ArchInfo {
name: "c3-2".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("c3-2".to_string()),
has_sse2: true,
default_cpu: "c3-2",
default_fpu: "sse2",
},
"c7" => X86ArchInfo {
name: "c7".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("c7".to_string()),
has_sse2: true,
default_cpu: "c7",
default_fpu: "sse2",
},
"geode" => X86ArchInfo {
name: "geode".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("geode".to_string()),
has_sse2: false,
default_cpu: "geode",
default_fpu: "x87",
},
"winchip-c6" => X86ArchInfo {
name: "winchip-c6".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("winchip-c6".to_string()),
has_sse2: false,
default_cpu: "winchip-c6",
default_fpu: "x87",
},
"winchip2" => X86ArchInfo {
name: "winchip2".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("winchip2".to_string()),
has_sse2: false,
default_cpu: "winchip2",
default_fpu: "x87",
},
"pentiumpro" => X86ArchInfo {
name: "pentiumpro".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("pentiumpro".to_string()),
has_sse2: false,
default_cpu: "pentiumpro",
default_fpu: "x87",
},
"prescott" => X86ArchInfo {
name: "prescott".to_string(),
family: X86ArchFamily::X86_32,
bit_width: 32,
is_extended: true,
cpu_sub_arch: Some("prescott".to_string()),
has_sse2: true,
default_cpu: "prescott",
default_fpu: "sse2",
},
"nocona" => X86ArchInfo {
name: "nocona".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("nocona".to_string()),
has_sse2: true,
default_cpu: "nocona",
default_fpu: "sse2",
},
"rocketlake" => X86ArchInfo {
name: "rocketlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("rocketlake".to_string()),
has_sse2: true,
default_cpu: "rocketlake",
default_fpu: "sse2",
},
"cooperlake" => X86ArchInfo {
name: "cooperlake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("cooperlake".to_string()),
has_sse2: true,
default_cpu: "cooperlake",
default_fpu: "sse2",
},
"cascadelake" => X86ArchInfo {
name: "cascadelake".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("cascadelake".to_string()),
has_sse2: true,
default_cpu: "cascadelake",
default_fpu: "sse2",
},
"emeraldrapids" => X86ArchInfo {
name: "emeraldrapids".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("emeraldrapids".to_string()),
has_sse2: true,
default_cpu: "emeraldrapids",
default_fpu: "sse2",
},
"sierraforest" => X86ArchInfo {
name: "sierraforest".to_string(),
family: X86ArchFamily::X86_64,
bit_width: 64,
is_extended: true,
cpu_sub_arch: Some("sierraforest".to_string()),
has_sse2: true,
default_cpu: "sierraforest",
default_fpu: "sse2",
},
_ => return None,
};
Some(info)
}
pub fn parse_x86_vendor(name: &str) -> Option<Vendor> {
match name.to_lowercase().as_str() {
"pc" => Some(Vendor::PC),
"unknown" => Some(Vendor::Unknown),
"apple" => Some(Vendor::Apple),
"ibm" => Some(Vendor::IBM),
"intel" => Some(Vendor::Unknown), "amd" => Some(Vendor::AMD),
"nvidia" => Some(Vendor::Nvidia),
"img" | "imagination" | "mips" => Some(Vendor::Imagination),
"scei" | "sony" | "ps" => Some(Vendor::SCEI),
"fsl" | "freescale" | "nxp" => Some(Vendor::Freescale),
"mti" => Some(Vendor::Mips),
"ti" | "texas" | "texasinstruments" => Some(Vendor::Unknown),
"qcom" | "qualcomm" => Some(Vendor::Unknown),
"samsung" => Some(Vendor::Unknown),
"marvell" | "cavium" => Some(Vendor::Unknown),
"broadcom" => Some(Vendor::Unknown),
"allwinner" => Some(Vendor::Unknown),
"rockchip" => Some(Vendor::Unknown),
"mediatek" => Some(Vendor::Unknown),
"hisilicon" | "huawei" => Some(Vendor::Unknown),
"ampere" => Some(Vendor::Unknown),
"phytium" => Some(Vendor::Unknown),
_ => None,
}
}
pub fn parse_x86_os(name: &str) -> Option<OS> {
match name.to_lowercase().as_str() {
"linux" => Some(OS::Linux),
"darwin" | "macosx" | "macos" => Some(OS::Darwin),
"windows" | "win32" => Some(OS::Win32),
"freebsd" => Some(OS::FreeBSD),
"netbsd" => Some(OS::NetBSD),
"openbsd" => Some(OS::OpenBSD),
"haiku" => Some(OS::Haiku),
"solaris" | "sunos" => Some(OS::Unknown),
"android" => Some(OS::Linux),
"ios" => Some(OS::IOS),
"tvos" => Some(OS::TvOS),
"watchos" => Some(OS::WatchOS),
"wasi" => Some(OS::WASI),
"fuchsia" => Some(OS::Fuchsia),
"aix" => Some(OS::AIX),
"hurd" => Some(OS::Hurd),
_ => None,
}
}
pub fn parse_x86_env(name: &str) -> Option<Environment> {
match name.to_lowercase().as_str() {
"gnu" => Some(Environment::GNU),
"gnueabi" => Some(Environment::GNUEABI),
"gnueabihf" => Some(Environment::GNUEABIHF),
"gnuabi64" => Some(Environment::GNUABI64),
"gnux32" => Some(Environment::GNUX32),
"musl" | "musleabi" | "musleabihf" => Some(Environment::Musl),
"android" => Some(Environment::Android),
"androideabi" => Some(Environment::Androideabi),
"msvc" => Some(Environment::MSVC),
"mingw" | "mingw32" => Some(Environment::GNU),
"cygwin" => Some(Environment::Cygnus),
_ => None,
}
}
pub fn known_x86_cpus() -> Vec<&'static str> {
vec![
"i386",
"i486",
"i586",
"i686",
"pentium",
"pentium-mmx",
"pentiumpro",
"pentium2",
"pentium3",
"pentium3m",
"pentium-m",
"pentium4",
"pentium4m",
"prescott",
"nocona",
"core2",
"penryn",
"nehalem",
"westmere",
"sandybridge",
"ivybridge",
"haswell",
"broadwell",
"skylake",
"skylake-avx512",
"cannonlake",
"icelake-client",
"icelake-server",
"cascadelake",
"cooperlake",
"tigerlake",
"rocketlake",
"alderlake",
"raptorlake",
"meteorlake",
"arrowlake",
"lunarlake",
"pantherlake",
"sapphirerapids",
"emeraldrapids",
"graniterapids",
"sierraforest",
"bonnell",
"silvermont",
"goldmont",
"goldmont-plus",
"tremont",
"gracemont",
"crestmont",
"skymont",
"knl",
"knm",
"k6",
"k6-2",
"k6-3",
"athlon",
"athlon-tbird",
"athlon-4",
"athlon-xp",
"athlon-mp",
"k8",
"opteron",
"athlon64",
"athlon-fx",
"k8-sse3",
"opteron-sse3",
"amdfam10",
"barcelona",
"btver1",
"btver2",
"bdver1",
"bdver2",
"bdver3",
"bdver4",
"znver1",
"znver2",
"znver3",
"znver4",
"znver5",
"c3",
"c3-2",
"c7",
"x86-64",
"x86-64-v2",
"x86-64-v3",
"x86-64-v4",
"geode",
"winchip-c6",
"winchip2",
]
}
pub fn default_features_for_cpu(cpu: &str) -> Vec<&'static str> {
match cpu {
"i386" | "i486" => vec!["+cmov"],
"i586" => vec!["+cmov", "+mmx"],
"i686" => vec!["+cmov", "+mmx", "+sse"],
"pentium4" | "prescott" | "nocona" => {
vec!["+cmov", "+mmx", "+sse", "+sse2", "+sse3"]
}
"core2" | "penryn" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1",
]
}
"nehalem" | "westmere" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1", "+sse4.2",
"+popcnt",
]
}
"sandybridge" | "ivybridge" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1", "+sse4.2",
"+popcnt", "+avx",
]
}
"haswell" | "broadwell" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1", "+sse4.2",
"+popcnt", "+avx", "+avx2", "+fma", "+bmi", "+bmi2", "+lzcnt", "+movbe",
]
}
"skylake" | "skylake-avx512" | "cannonlake" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
]
}
"icelake-client" | "icelake-server" | "tigerlake" | "rocketlake" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
"+avx512ifma",
"+avx512vbmi",
"+avx512vbmi2",
"+avx512vnni",
"+avx512bitalg",
"+avx512vpopcntdq",
"+gfni",
"+vaes",
"+vpclmulqdq",
]
}
"alderlake" | "raptorlake" | "meteorlake" | "arrowlake" | "lunarlake" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
"+avx512ifma",
"+avx512vbmi",
"+avx512vbmi2",
"+avx512vnni",
"+avx512bitalg",
"+avx512vpopcntdq",
"+gfni",
"+vaes",
"+vpclmulqdq",
"+avxvnni",
"+avx512fp16",
]
}
"znver1" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+clzero",
"+adx",
"+rdseed",
"+sha",
"+xsavec",
"+xsaves",
"+clflushopt",
]
}
"znver2" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+clzero",
"+adx",
"+rdseed",
"+sha",
"+xsavec",
"+xsaves",
"+clflushopt",
"+clwb",
"+mwaitx",
"+rdpru",
]
}
"znver3" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+clzero",
"+adx",
"+rdseed",
"+sha",
"+xsavec",
"+xsaves",
"+clflushopt",
"+clwb",
"+mwaitx",
"+rdpru",
"+vaes",
"+vpclmulqdq",
"+shstk",
"+invpcid",
]
}
"znver4" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+clzero",
"+adx",
"+rdseed",
"+sha",
"+xsavec",
"+xsaves",
"+clflushopt",
"+clwb",
"+mwaitx",
"+rdpru",
"+vaes",
"+vpclmulqdq",
"+shstk",
"+invpcid",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
"+avx512ifma",
"+avx512vbmi",
"+avx512vbmi2",
"+avx512vnni",
"+avx512bitalg",
"+avx512vpopcntdq",
"+gfni",
]
}
"znver5" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+clzero",
"+adx",
"+rdseed",
"+sha",
"+xsavec",
"+xsaves",
"+clflushopt",
"+clwb",
"+mwaitx",
"+rdpru",
"+vaes",
"+vpclmulqdq",
"+shstk",
"+invpcid",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
"+avx512ifma",
"+avx512vbmi",
"+avx512vbmi2",
"+avx512vnni",
"+avx512bitalg",
"+avx512vpopcntdq",
"+gfni",
"+avx512fp16",
"+avxvnni",
]
}
"x86-64" | "x86-64-v2" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1", "+sse4.2",
"+popcnt",
]
}
"x86-64-v3" => {
vec![
"+cmov", "+mmx", "+sse", "+sse2", "+sse3", "+ssse3", "+sse4.1", "+sse4.2",
"+popcnt", "+avx", "+avx2", "+fma", "+bmi", "+bmi2", "+lzcnt", "+movbe",
]
}
"x86-64-v4" => {
vec![
"+cmov",
"+mmx",
"+sse",
"+sse2",
"+sse3",
"+ssse3",
"+sse4.1",
"+sse4.2",
"+popcnt",
"+avx",
"+avx2",
"+fma",
"+bmi",
"+bmi2",
"+lzcnt",
"+movbe",
"+avx512f",
"+avx512cd",
"+avx512bw",
"+avx512dq",
"+avx512vl",
]
}
_ => vec!["+cmov", "+mmx", "+sse", "+sse2"],
}
}
}
impl Default for X86TripleParser {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct X86CompilerRT {
pub triple: Triple,
pub resource_dir: Option<PathBuf>,
pub has_compiler_rt: bool,
pub has_libgcc: bool,
pub preferred_runtime: X86RuntimeLib,
pub builtins_path: Option<PathBuf>,
pub sanitizer_paths: HashMap<String, PathBuf>,
pub profile_path: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RuntimeLib {
CompilerRT,
LibGcc,
Auto,
}
impl X86CompilerRT {
pub fn new(triple: &Triple) -> Self {
let resource_dir = Self::detect_resource_dir();
let has_compiler_rt = Self::check_compiler_rt_available(triple, resource_dir.as_deref());
let has_libgcc = Self::check_libgcc_available(triple);
let builtins_path = if has_compiler_rt {
Self::find_builtins_library(triple, resource_dir.as_deref())
} else if has_libgcc {
Self::find_libgcc_path(triple)
} else {
None
};
Self {
triple: triple.clone(),
resource_dir,
has_compiler_rt,
has_libgcc,
preferred_runtime: X86RuntimeLib::Auto,
builtins_path,
sanitizer_paths: Self::find_sanitizer_runtimes(triple),
profile_path: Self::find_profile_runtime(triple),
}
}
pub fn detect_resource_dir() -> Option<PathBuf> {
if let Ok(output) = ProcessCommand::new("clang")
.args(["-print-resource-dir"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() {
return Some(p);
}
}
}
let candidates = [
PathBuf::from("/usr/lib/clang"),
PathBuf::from("/usr/local/lib/clang"),
PathBuf::from("/opt/clang/lib/clang"),
];
for candidate in &candidates {
if let Ok(entries) = fs::read_dir(candidate) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
let name = entry.file_name().to_string_lossy().to_string();
if name.chars().next().map_or(false, |c| c.is_ascii_digit()) {
return Some(path);
}
}
}
}
}
None
}
pub fn check_compiler_rt_available(triple: &Triple, resource_dir: Option<&Path>) -> bool {
if let Some(res_dir) = resource_dir {
let lib_dir = res_dir.join("lib");
if lib_dir.exists() {
let triple_dir = lib_dir.join(triple.to_string());
if triple_dir.exists() {
let builtins_name = Self::builtins_library_name(triple);
let builtins_path = triple_dir.join(&builtins_name);
return builtins_path.exists();
}
let linux_dir = lib_dir.join("linux");
if linux_dir.exists() {
let builtins_name = Self::builtins_library_name(triple);
let builtins_path = linux_dir.join(&builtins_name);
return builtins_path.exists();
}
}
}
if let Ok(output) = ProcessCommand::new("clang")
.args(["--rtlib=compiler-rt", "-print-libgcc-file-name"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !path.is_empty() {
return true;
}
}
}
false
}
pub fn check_libgcc_available(triple: &Triple) -> bool {
if let Ok(output) = ProcessCommand::new("gcc")
.args(["-print-libgcc-file-name"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !path.is_empty() && Path::new(&path).exists() {
return true;
}
}
}
let libgcc_candidates = [
format!("/usr/lib/gcc/{}/libgcc.a", triple.to_string()),
format!("/usr/lib/gcc/{}/libgcc_s.so", triple.to_string()),
];
for candidate in &libgcc_candidates {
if Path::new(candidate).exists() {
return true;
}
}
false
}
pub fn builtins_library_name(triple: &Triple) -> String {
format!("libclang_rt.builtins-{}.a", triple.arch.as_str())
}
pub fn find_builtins_library(triple: &Triple, resource_dir: Option<&Path>) -> Option<PathBuf> {
let lib_name = Self::builtins_library_name(triple);
if let Some(res_dir) = resource_dir {
let search_paths = vec![
res_dir.join("lib").join(triple.to_string()).join(&lib_name),
res_dir.join("lib").join("linux").join(&lib_name),
res_dir.join("lib").join(&lib_name),
];
for path in &search_paths {
if path.exists() {
return Some(path.clone());
}
}
}
let common_paths = vec![
format!("/usr/lib/clang/18/lib/linux/{}", lib_name),
format!("/usr/lib/llvm-18/lib/clang/18/lib/linux/{}", lib_name),
format!("/usr/lib/clang/17/lib/linux/{}", lib_name),
format!("/usr/lib/llvm-17/lib/clang/17/lib/linux/{}", lib_name),
];
for path_str in &common_paths {
let p = PathBuf::from(path_str);
if p.exists() {
return Some(p);
}
}
None
}
pub fn find_libgcc_path(triple: &Triple) -> Option<PathBuf> {
if let Ok(output) = ProcessCommand::new("gcc")
.args([
&format!("-target{}", triple.to_string()),
"-print-libgcc-file-name",
])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() {
return Some(p);
}
}
}
None
}
pub fn builtins_library_path(&self) -> Option<PathBuf> {
self.builtins_path.clone()
}
pub fn compiler_rt_library(&self, component: &str) -> Option<PathBuf> {
if let Some(ref res_dir) = self.resource_dir {
let lib_name = format!("libclang_rt.{}-{}.a", component, self.triple.arch.as_str());
let search_paths = vec![
res_dir
.join("lib")
.join(self.triple.to_string())
.join(&lib_name),
res_dir.join("lib").join("linux").join(&lib_name),
];
for path in &search_paths {
if path.exists() {
return Some(path.clone());
}
}
}
None
}
pub fn find_sanitizer_runtimes(triple: &Triple) -> HashMap<String, PathBuf> {
let mut paths = HashMap::new();
let sanitizers = [
"asan", "ubsan_standalone", "lsan", "tsan", "msan", "dfsan", "hwasan", "memprof", "xray", "fuzzer", "scudo_standalone", "gwp_asan", ];
if let Some(res_dir) = Self::detect_resource_dir() {
let lib_dir = res_dir.join("lib").join("linux");
for sanitizer in &sanitizers {
let lib_name = format!("libclang_rt.{}-{}.a", sanitizer, triple.arch.as_str());
let path = lib_dir.join(&lib_name);
if path.exists() {
paths.insert(sanitizer.to_string(), path);
}
}
}
paths
}
pub fn find_profile_runtime(triple: &Triple) -> Option<PathBuf> {
if let Some(res_dir) = Self::detect_resource_dir() {
let lib_name = format!("libclang_rt.profile-{}.a", triple.arch.as_str());
let path = res_dir.join("lib").join("linux").join(&lib_name);
if path.exists() {
return Some(path);
}
}
None
}
pub fn select_runtime(&self) -> X86RuntimeLib {
match self.preferred_runtime {
X86RuntimeLib::CompilerRT => X86RuntimeLib::CompilerRT,
X86RuntimeLib::LibGcc => X86RuntimeLib::LibGcc,
X86RuntimeLib::Auto => {
if self.has_compiler_rt {
X86RuntimeLib::CompilerRT
} else if self.has_libgcc {
X86RuntimeLib::LibGcc
} else {
X86RuntimeLib::LibGcc
}
}
}
}
pub fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
match self.select_runtime() {
X86RuntimeLib::CompilerRT => {
flags.push("--rtlib=compiler-rt".to_string());
if let Some(ref path) = self.builtins_path {
flags.push(path.to_string_lossy().to_string());
}
}
X86RuntimeLib::LibGcc => {
flags.push("--rtlib=libgcc".to_string());
}
X86RuntimeLib::Auto => {
}
}
flags
}
pub fn needs_libgcc_eh(triple: &Triple) -> bool {
if triple.os == OS::Linux && triple.environment == Environment::GNU {
return true;
}
if triple.os == OS::Win32 && triple.environment == Environment::GNU {
return true;
}
false
}
pub fn runtime_libraries(&self) -> Vec<String> {
let mut libs = Vec::new();
match self.select_runtime() {
X86RuntimeLib::CompilerRT => {
libs.push("clang_rt.builtins".to_string());
if Self::needs_libgcc_eh(&self.triple) {
libs.push("unwind".to_string());
}
}
X86RuntimeLib::LibGcc => {
libs.push("gcc".to_string());
if Self::needs_libgcc_eh(&self.triple) {
libs.push("gcc_eh".to_string());
}
}
X86RuntimeLib::Auto => {}
}
libs
}
pub fn has_cfi(&self) -> bool {
if let Some(ref res_dir) = self.resource_dir {
let cfi_name = format!("libclang_rt.cfi-{}.a", self.triple.arch.as_str());
let cfi_path = res_dir.join("lib").join("linux").join(&cfi_name);
return cfi_path.exists();
}
false
}
pub fn has_shadow_call_stack(&self) -> bool {
if let Some(ref res_dir) = self.resource_dir {
let scs_name = format!("libclang_rt.scs-{}.a", self.triple.arch.as_str());
let scs_path = res_dir.join("lib").join("linux").join(&scs_name);
return scs_path.exists();
}
false
}
pub fn has_safestack(&self) -> bool {
if let Some(ref res_dir) = self.resource_dir {
let ss_name = format!("libclang_rt.safestack-{}.a", self.triple.arch.as_str());
let ss_path = res_dir.join("lib").join("linux").join(&ss_name);
return ss_path.exists();
}
false
}
pub fn available_components(&self) -> HashMap<String, bool> {
let mut components = HashMap::new();
let component_names = vec![
("builtins", true), ("asan", true), ("asan_cxx", false), ("ubsan_standalone", true),
("ubsan_minimal", false),
("lsan", true),
("tsan", true),
("msan", true),
("dfsan", true),
("hwasan", true),
("memprof", true),
("xray", true),
("xray-basic", false),
("xray-fdr", false),
("fuzzer", true),
("fuzzer_no_main", false),
("scudo_standalone", true),
("scudo_minimal", false),
("gwp_asan", true),
("profile", true),
("cfi", true),
("cfi_diag", false),
("dd", true), ("safestack", true),
("stats", false),
("stats_client", false),
("orc", false), ("shadowcallstack", true),
];
if let Some(ref res_dir) = self.resource_dir {
let lib_dir = res_dir.join("lib").join("linux");
for (name, _default_available) in &component_names {
let lib_name = format!("libclang_rt.{}-{}.a", name, self.triple.arch.as_str());
let path = lib_dir.join(&lib_name);
components.insert(name.to_string(), path.exists());
}
} else {
for (name, default_available) in &component_names {
components.insert(name.to_string(), *default_available);
}
}
components
}
pub fn supports_hwasan(triple: &Triple) -> bool {
matches!(triple.arch, Arch::AArch64)
}
pub fn supports_mte(triple: &Triple) -> bool {
matches!(triple.arch, Arch::AArch64)
}
pub fn recommended_sanitizer_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.sanitizer_paths.contains_key("asan") {
flags.push("-fsanitize=address".to_string());
flags.push("-fno-omit-frame-pointer".to_string());
flags.push("-fno-optimize-sibling-calls".to_string());
}
if self.sanitizer_paths.contains_key("ubsan_standalone") {
flags.push("-fsanitize=undefined".to_string());
flags.push("-fno-sanitize-recover=all".to_string());
}
flags
}
pub fn detect_compiler_rt_version() -> Option<String> {
if let Some(res_dir) = Self::detect_resource_dir() {
if let Some(version_dir) = res_dir.parent() {
return version_dir
.file_name()
.map(|n| n.to_string_lossy().to_string());
}
return res_dir.file_name().map(|n| n.to_string_lossy().to_string());
}
None
}
}
impl Default for X86CompilerRT {
fn default() -> Self {
Self::new(&Triple::x86_64_linux())
}
}
#[derive(Debug, Clone)]
pub struct X86ResourceDir {
pub triple: Triple,
pub path: PathBuf,
pub clang_version: String,
pub is_valid: bool,
pub include_subdir: PathBuf,
pub lib_subdir: PathBuf,
pub share_subdir: PathBuf,
}
impl X86ResourceDir {
pub fn new(triple: &Triple) -> Self {
let (path, version) = Self::detect_resource_dir();
let include_subdir = path.join("include");
let lib_subdir = path.join("lib");
let share_subdir = path.join("share");
let is_valid = path.exists();
Self {
triple: triple.clone(),
path,
clang_version: version,
is_valid,
include_subdir,
lib_subdir,
share_subdir,
}
}
pub fn detect_resource_dir() -> (PathBuf, String) {
if let Ok(output) = ProcessCommand::new("clang")
.args(["-print-resource-dir"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() {
let version = p
.parent()
.and_then(|p| p.file_name())
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "unknown".to_string());
return (p, version);
}
}
}
let search_bases = vec![
PathBuf::from("/usr/lib/clang"),
PathBuf::from("/usr/local/lib/clang"),
PathBuf::from("/opt/clang/lib/clang"),
PathBuf::from("/usr/lib/llvm"),
PathBuf::from("/usr/lib/llvm-18/lib/clang"),
PathBuf::from("/usr/lib/llvm-17/lib/clang"),
PathBuf::from("/usr/lib/llvm-16/lib/clang"),
PathBuf::from("/usr/lib/llvm-15/lib/clang"),
PathBuf::from("/usr/lib/llvm-14/lib/clang"),
];
for base in &search_bases {
for version_dir in Self::find_version_dirs(base) {
let p = version_dir.clone();
if p.join("include").exists() && p.join("lib").exists() {
let version = p
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_default();
return (p, version);
}
}
}
let default_path = PathBuf::from("/usr/lib/clang/18");
(default_path, "18".to_string())
}
fn find_version_dirs(base: &Path) -> Vec<PathBuf> {
let mut dirs = Vec::new();
if let Ok(entries) = fs::read_dir(base) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.chars().all(|c| c.is_ascii_digit() || c == '.') {
if entry.path().is_dir() {
dirs.push(entry.path());
}
}
}
}
dirs.sort_by(|a, b| {
let va = a
.file_name()
.and_then(|n| n.to_str())
.and_then(|s| s.parse::<f64>().ok());
let vb = b
.file_name()
.and_then(|n| n.to_str())
.and_then(|s| s.parse::<f64>().ok());
vb.partial_cmp(&va).unwrap_or(std::cmp::Ordering::Equal)
});
dirs
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn include_dir(&self) -> PathBuf {
self.include_subdir.clone()
}
pub fn lib_dir(&self) -> PathBuf {
self.lib_subdir.clone()
}
pub fn share_dir(&self) -> PathBuf {
self.share_subdir.clone()
}
pub fn builtin_headers_dir(&self) -> PathBuf {
self.include_dir()
}
pub fn target_lib_dir(&self) -> PathBuf {
self.lib_dir().join(self.triple.to_string())
}
pub fn config_dir(&self) -> PathBuf {
self.share_dir()
}
pub fn include_path(&self, header: &str) -> PathBuf {
self.include_dir().join(header)
}
pub fn has_builtin_header(&self, header: &str) -> bool {
self.include_path(header).exists()
}
pub fn list_builtin_headers(&self) -> Vec<String> {
let mut headers = Vec::new();
let inc_dir = self.include_dir();
if let Ok(entries) = fs::read_dir(&inc_dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.ends_with(".h") {
headers.push(name);
}
}
}
headers.sort();
headers
}
pub fn expected_builtin_headers() -> Vec<&'static str> {
vec![
"__clang_cuda_builtin_vars.h",
"__clang_cuda_cmath.h",
"__clang_cuda_complex_builtins.h",
"__clang_cuda_device_functions.h",
"__clang_cuda_intrinsics.h",
"__clang_cuda_libdevice_declares.h",
"__clang_cuda_math.h",
"__clang_cuda_math_forward_declares.h",
"__clang_cuda_runtime_wrapper.h",
"__clang_hip_cmath.h",
"__clang_hip_libdevice_declares.h",
"__clang_hip_math.h",
"__clang_hip_runtime_wrapper.h",
"__stdarg_va_arg.h",
"__stddef_max_align_t.h",
"__stddef_null.h",
"__stddef_offsetof.h",
"__stddef_ptrdiff_t.h",
"__stddef_rsize_t.h",
"__stddef_size_t.h",
"__stddef_unreachable.h",
"__stddef_wchar_t.h",
"__stddef_wint_t.h",
"__wmmintrin_aes.h",
"__wmmintrin_pclmul.h",
"adxintrin.h",
"altivec.h",
"ammintrin.h",
"arm_acle.h",
"arm_cmse.h",
"arm_fp16.h",
"arm_mve.h",
"arm_neon.h",
"arm_sve.h",
"armintr.h",
"avx2intrin.h",
"avx512bf16intrin.h",
"avx512bitalgintrin.h",
"avx512bwintrin.h",
"avx512cdintrin.h",
"avx512dqintrin.h",
"avx512erintrin.h",
"avx512fintrin.h",
"avx512fp16intrin.h",
"avx512ifmaintrin.h",
"avx512pfintrin.h",
"avx512vbmi2intrin.h",
"avx512vbmiintrin.h",
"avx512vlbwintrin.h",
"avx512vldqintrin.h",
"avx512vlintrin.h",
"avx512vnniintrin.h",
"avx512vp2intersectintrin.h",
"avx512vpopcntdqintrin.h",
"avxintrin.h",
"bmi2intrin.h",
"bmiintrin.h",
"cetintrin.h",
"cldemoteintrin.h",
"clflushoptintrin.h",
"clwbintrin.h",
"clzerointrin.h",
"cpuid.h",
"crc32intrin.h",
"emmintrin.h",
"enqcmdintrin.h",
"f16cintrin.h",
"float.h",
"fma4intrin.h",
"fmaintrin.h",
"fxsrintrin.h",
"gfniintrin.h",
"hresetintrin.h",
"ia32intrin.h",
"immintrin.h",
"intrin.h",
"inttypes.h",
"invpcidintrin.h",
"iso646.h",
"keylockerintrin.h",
"limits.h",
"lwpintrin.h",
"lzcntintrin.h",
"mm3dnow.h",
"mm_malloc.h",
"mmintrin.h",
"movdirintrin.h",
"mwaitxintrin.h",
"nmmintrin.h",
"opencl-c-base.h",
"opencl-c.h",
"pconfigintrin.h",
"pkuintrin.h",
"pmmintrin.h",
"popcntintrin.h",
"prfchwintrin.h",
"ptwriteintrin.h",
"rdseedintrin.h",
"rtmintrin.h",
"s390intrin.h",
"serializeintrin.h",
"sgxintrin.h",
"shaintrin.h",
"smmintrin.h",
"stdalign.h",
"stdarg.h",
"stdatomic.h",
"stdbool.h",
"stddef.h",
"stdint.h",
"stdnoreturn.h",
"tbmintrin.h",
"tmmintrin.h",
"tsxldtrkintrin.h",
"uintrintrin.h",
"vaesintrin.h",
"varargs.h",
"vpclmulqdqintrin.h",
"waitpkgintrin.h",
"wbnoinvdintrin.h",
"wmmintrin.h",
"x86intrin.h",
"xmmintrin.h",
"xopintrin.h",
"xsavecintrin.h",
"xsavesintrin.h",
"xtestintrin.h",
]
}
pub fn sanitizer_library_path(&self, sanitizer: &str) -> Option<PathBuf> {
let arch = self.triple.arch.as_str();
let lib_name = format!("libclang_rt.{}-{}.a", sanitizer, arch);
let path = self.target_lib_dir().join(&lib_name);
if path.exists() {
Some(path)
} else {
let linux_path = self.lib_dir().join("linux").join(&lib_name);
if linux_path.exists() {
Some(linux_path)
} else {
None
}
}
}
pub fn builtins_path(&self) -> Option<PathBuf> {
self.sanitizer_library_path("builtins")
}
pub fn validate(&self) -> Vec<String> {
let mut issues = Vec::new();
if !self.path.exists() {
issues.push(format!(
"Resource directory does not exist: {}",
self.path.display()
));
return issues;
}
if !self.include_dir().exists() {
issues.push("Include directory missing in resource dir".to_string());
}
if !self.lib_dir().exists() {
issues.push("Library directory missing in resource dir".to_string());
}
let essential_headers = ["stddef.h", "stdarg.h", "stdbool.h", "stdint.h", "float.h"];
for header in &essential_headers {
if !self.has_builtin_header(header) {
issues.push(format!("Missing essential header: {}", header));
}
}
issues
}
}
impl Default for X86ResourceDir {
fn default() -> Self {
Self::new(&Triple::x86_64_linux())
}
}
#[derive(Debug, Clone)]
pub struct X86CrossLibrary {
pub triple: Triple,
pub libc_type: X86LibC,
pub found: bool,
pub root_path: Option<PathBuf>,
pub include_dirs: Vec<PathBuf>,
pub library_dirs: Vec<PathBuf>,
pub crt_dir: Option<PathBuf>,
pub dynamic_linker: Option<String>,
pub version: Option<String>,
}
impl X86CrossLibrary {
pub fn new(triple: &Triple) -> Self {
let libc_type = Self::detect_libc_type(triple);
let (root_path, found) = Self::find_cross_library(triple, libc_type);
let include_dirs = Self::find_include_dirs(triple, libc_type, root_path.as_deref());
let library_dirs = Self::find_library_dirs(triple, libc_type, root_path.as_deref());
let crt_dir = Self::find_crt_dir(triple, libc_type, root_path.as_deref());
let dynamic_linker = Self::detect_dynamic_linker(triple, libc_type);
let version = Self::detect_libc_version(triple, libc_type, root_path.as_deref());
Self {
triple: triple.clone(),
libc_type,
found,
root_path,
include_dirs,
library_dirs,
crt_dir,
dynamic_linker,
version,
}
}
pub fn detect_libc_type(triple: &Triple) -> X86LibC {
match triple.environment {
Environment::Musl => X86LibC::Musl,
Environment::Android | Environment::Androideabi => X86LibC::Bionic,
_ => match triple.os {
OS::Linux => X86LibC::Glibc,
OS::Darwin | OS::MacOSX | OS::IOS | OS::TvOS | OS::WatchOS => X86LibC::LibSystem,
OS::Win32 => match triple.environment {
Environment::MSVC => X86LibC::Msvcrt,
_ => X86LibC::UCRT,
},
_ => X86LibC::Glibc,
},
}
}
pub fn find_cross_library(triple: &Triple, libc_type: X86LibC) -> (Option<PathBuf>, bool) {
match libc_type {
X86LibC::Glibc => Self::find_glibc_cross(triple),
X86LibC::Musl => Self::find_musl_cross(triple),
X86LibC::Bionic => Self::find_bionic_cross(triple),
X86LibC::Newlib => Self::find_newlib_cross(triple),
X86LibC::Picolibc => Self::find_picolibc_cross(triple),
X86LibC::LibSystem => (None, true), X86LibC::Msvcrt | X86LibC::UCRT => (None, true), X86LibC::None_ => (None, false),
}
}
pub fn find_glibc_cross(triple: &Triple) -> (Option<PathBuf>, bool) {
let candidates = vec![
format!("/usr/{}", triple.to_string()),
format!("/usr/cross/{}", triple.to_string()),
format!("/opt/cross/{}", triple.to_string()),
format!("/usr/{}/sys-root", triple.to_string()),
];
for candidate in &candidates {
let p = PathBuf::from(candidate);
if p.exists() {
let libc_so = p.join("lib").join("libc.so.6");
let libc_a = p.join("lib").join("libc.a");
let include = p.join("include").join("stdio.h");
if (libc_so.exists() || libc_a.exists()) && include.exists() {
return (Some(p), true);
}
}
}
if let Ok(output) = ProcessCommand::new("gcc")
.args([&format!("-target={}", triple.to_string()), "-print-sysroot"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() && p.join("usr").join("lib").exists() {
return (Some(p), true);
}
}
}
if !Self::is_cross_target(triple) {
return (Some(PathBuf::from("/")), true);
}
(None, false)
}
pub fn detect_glibc_version(root: Option<&Path>) -> Option<String> {
let lib_dir = if let Some(r) = root {
r.join("lib")
} else {
PathBuf::from("/lib")
};
let libc_so = lib_dir.join("libc.so.6");
if let Ok(content) = fs::read_to_string(&libc_so) {
for line in content.lines() {
if line.contains("GLIBC_") {
if let Some(version) = line.split("GLIBC_").nth(1) {
return Some(version.trim().to_string());
}
}
}
}
if let Ok(output) = ProcessCommand::new(&libc_so).output() {
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if line.contains("GNU C Library") {
let parts: Vec<&str> = line.split_whitespace().collect();
for (i, part) in parts.iter().enumerate() {
if *part == "version" && i + 1 < parts.len() {
let v = parts[i + 1].trim_end_matches('.');
return Some(v.to_string());
}
}
}
if line.contains("release version") {
if let Some(idx) = line.find("release version") {
let rest = &line[idx + "release version".len()..];
let v = rest.trim().trim_end_matches('.');
return Some(v.to_string());
}
}
}
}
None
}
fn is_cross_target(triple: &Triple) -> bool {
let host_triple = X86SysrootDetector::detect_host_triple();
let host = Triple::parse(&host_triple);
triple.arch != host.arch || triple.os != host.os
}
pub fn find_musl_cross(triple: &Triple) -> (Option<PathBuf>, bool) {
let arch_dir = match triple.arch {
Arch::X86_64 => "x86_64",
Arch::X86 => "i386",
_ => triple.arch.as_str(),
};
let candidates = vec![
format!("/usr/{}-linux-musl", arch_dir),
format!("/usr/{}-linux-musl-cross", arch_dir),
format!("/opt/musl/{}-linux-musl", arch_dir),
format!("/opt/{}-linux-musl-cross", arch_dir),
format!("/usr/local/musl/{}-linux-musl", arch_dir),
format!("/usr/local/{}-linux-musl", arch_dir),
format!("/opt/cross/{}-linux-musl", arch_dir),
format!("/usr/local/musl-cross/{}-linux-musl", arch_dir),
];
for candidate in &candidates {
let p = PathBuf::from(candidate);
if p.exists() {
let include = p.join("include").join("stdio.h");
let lib = p.join("lib").join("libc.a");
if include.exists() && lib.exists() {
return (Some(p), true);
}
}
}
let wrapper_names = vec![format!("{}-linux-musl-gcc", arch_dir), format!("musl-gcc")];
for wrapper in &wrapper_names {
if let Ok(output) = ProcessCommand::new(wrapper)
.args(["-print-sysroot"])
.output()
{
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
let p = PathBuf::from(&path);
if p.exists() {
return (Some(p), true);
}
}
}
}
(None, false)
}
pub fn detect_musl_version(root: Option<&Path>) -> Option<String> {
let include_dir = if let Some(r) = root {
r.join("include")
} else {
PathBuf::from("/usr/include")
};
for header in &["version.h", "features.h"] {
let path = include_dir.join(header);
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
if line.contains("VERSION") && line.contains("\"") {
if let Some(start) = line.find('"') {
if let Some(end) = line[start + 1..].find('"') {
let version = &line[start + 1..start + 1 + end];
return Some(version.to_string());
}
}
}
}
}
}
let libc_so = if let Some(r) = root {
r.join("lib").join("libc.so")
} else {
PathBuf::from("/lib/libc.so")
};
if let Ok(output) = ProcessCommand::new(&libc_so).output() {
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if line.contains("musl libc") {
if let Some(idx) = line.find("Version") {
let rest = &line[idx + "Version".len()..];
return Some(rest.trim().to_string());
}
}
}
}
None
}
pub fn find_bionic_cross(triple: &Triple) -> (Option<PathBuf>, bool) {
let detector = X86SysrootDetector::new(&triple.to_string());
if let Some(ndk_sysroot) = detector.detect_android_ndk_sysroot() {
return (Some(ndk_sysroot), true);
}
let android_arch = match triple.arch {
Arch::X86_64 => "x86_64",
Arch::X86 => "x86",
Arch::AArch64 => "arm64",
Arch::ARM => "arm",
_ => "x86",
};
let candidates = vec![
format!(
"/usr/android-ndk/platforms/android-21/arch-{}",
android_arch
),
format!(
"/opt/android-ndk/platforms/android-21/arch-{}",
android_arch
),
];
for candidate in &candidates {
let p = PathBuf::from(candidate);
if p.exists() {
return (Some(p), true);
}
}
(None, false)
}
pub fn detect_bionic_version(root: Option<&Path>) -> Option<String> {
let usr_include = if let Some(r) = root {
r.join("usr").join("include")
} else {
PathBuf::from("/usr/include")
};
let api_header = usr_include.join("android").join("api-level.h");
if let Ok(content) = fs::read_to_string(&api_header) {
for line in content.lines() {
if line.contains("__ANDROID_API__") {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(val) = parts.last() {
return Some(val.to_string());
}
}
}
}
None
}
pub fn find_newlib_cross(triple: &Triple) -> (Option<PathBuf>, bool) {
let arch_name = triple.arch.as_str();
let candidates = vec![
format!("/usr/{}-elf", arch_name),
format!("/opt/cross/{}-elf", arch_name),
format!("/usr/local/{}-elf", arch_name),
];
for candidate in &candidates {
let p = PathBuf::from(candidate);
if p.exists() {
let include = p.join("include").join("stdio.h");
let lib = p.join("lib").join("libc.a");
if include.exists() && lib.exists() {
return (Some(p), true);
}
}
}
(None, false)
}
pub fn detect_newlib_version(root: Option<&Path>) -> Option<String> {
let include_dir = if let Some(r) = root {
r.join("include")
} else {
PathBuf::from("/usr/include")
};
let newlib_h = include_dir.join("_newlib_version.h");
if let Ok(content) = fs::read_to_string(&newlib_h) {
for line in content.lines() {
if line.contains("_NEWLIB_VERSION") && line.contains("\"") {
if let Some(start) = line.find('"') {
if let Some(end) = line[start + 1..].find('"') {
return Some(line[start + 1..start + 1 + end].to_string());
}
}
}
}
}
let newlib_header = include_dir.join("newlib.h");
if let Ok(content) = fs::read_to_string(&newlib_header) {
for line in content.lines() {
if line.contains("_NEWLIB_VERSION") || line.contains("__NEWLIB__") {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(val) = parts.last() {
let v = val.trim_matches('"');
return Some(v.to_string());
}
}
}
}
None
}
pub fn find_picolibc_cross(triple: &Triple) -> (Option<PathBuf>, bool) {
let arch_name = triple.arch.as_str();
let candidates = vec![
format!("/usr/lib/picolibc/{}-elf", arch_name),
format!("/opt/picolibc/{}-elf", arch_name),
format!("/usr/local/picolibc/{}-elf", arch_name),
format!("/usr/{}-elf/lib/picolibc", arch_name),
format!("/usr/lib/gcc/{}-elf/picolibc", arch_name),
];
for candidate in &candidates {
let p = PathBuf::from(candidate);
if p.exists() {
let include = p.join("include").join("stdio.h");
if include.exists() {
return (Some(p), true);
}
}
}
let specs_candidates = vec![
PathBuf::from("/usr/lib/picolibc.specs"),
PathBuf::from("/usr/local/lib/picolibc.specs"),
];
for spec in &specs_candidates {
if spec.exists() {
if let Some(parent) = spec.parent() {
let include = parent.join("include").join("stdio.h");
if include.exists() {
return (Some(parent.to_path_buf()), true);
}
}
}
}
(None, false)
}
pub fn detect_picolibc_version(root: Option<&Path>) -> Option<String> {
let include_dir = if let Some(r) = root {
r.join("include")
} else {
PathBuf::from("/usr/include")
};
let picolibc_h = include_dir.join("picolibc.h");
if let Ok(content) = fs::read_to_string(&picolibc_h) {
for line in content.lines() {
if line.contains("PICOLIBC_VERSION") || line.contains("__PICOLIBC_VERSION__") {
if let Some(start) = line.find('"') {
if let Some(end) = line[start + 1..].find('"') {
return Some(line[start + 1..start + 1 + end].to_string());
}
}
}
}
}
let version_h = include_dir.join("version.h");
if let Ok(content) = fs::read_to_string(&version_h) {
for line in content.lines() {
if line.contains("PICOLIBC") && line.contains("VERSION") {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(val) = parts.last() {
return Some(val.trim_matches('"').to_string());
}
}
}
}
None
}
pub fn find_include_dirs(
triple: &Triple,
libc_type: X86LibC,
root: Option<&Path>,
) -> Vec<PathBuf> {
let base = match root {
Some(r) => r.to_path_buf(),
None => PathBuf::from("/"),
};
let mut dirs = Vec::new();
match libc_type {
X86LibC::Glibc | X86LibC::Musl | X86LibC::None_ | X86LibC::LibSystem => {
let candidates = vec![
base.join("usr").join("include"),
base.join("include"),
base.join("usr").join("local").join("include"),
];
for p in candidates {
if p.exists() && !dirs.contains(&p) {
dirs.push(p);
}
}
if libc_type == X86LibC::Glibc {
let multiarch = X86Multiarch::new(triple);
let sysroot = Sysroot::cross(base.clone());
let ma_paths = multiarch.include_paths(&sysroot);
for p in ma_paths {
if !dirs.contains(&p) {
dirs.push(p);
}
}
}
}
X86LibC::Bionic => {
dirs.push(base.join("usr").join("include"));
if let Some(arch) = Self::android_arch_for_triple(triple) {
dirs.push(base.join("usr").join("include").join(arch));
}
}
X86LibC::Msvcrt | X86LibC::UCRT => {
dirs.push(base.join("include"));
dirs.push(base.join("Include"));
if libc_type == X86LibC::UCRT {
dirs.push(base.join("Include").join("ucrt"));
}
}
X86LibC::Newlib | X86LibC::Picolibc => {
dirs.push(base.join("include"));
let machine_dir = base.join("include").join("machine");
if machine_dir.exists() {
dirs.push(machine_dir);
}
let sys_dir = base.join("include").join("sys");
if sys_dir.exists() {
dirs.push(sys_dir);
}
}
}
dirs
}
pub fn find_library_dirs(
triple: &Triple,
libc_type: X86LibC,
root: Option<&Path>,
) -> Vec<PathBuf> {
let base = match root {
Some(r) => r.to_path_buf(),
None => PathBuf::from("/"),
};
let mut dirs = Vec::new();
match libc_type {
X86LibC::Glibc | X86LibC::Musl | X86LibC::None_ | X86LibC::LibSystem => {
let candidates = vec![
base.join("usr").join("lib"),
base.join("lib"),
base.join("usr").join("local").join("lib"),
base.join("usr").join("lib64"),
base.join("lib64"),
];
for p in candidates {
if p.exists() && !dirs.contains(&p) {
dirs.push(p);
}
}
}
X86LibC::Bionic => {
dirs.push(base.join("usr").join("lib"));
if let Some(arch) = Self::android_arch_for_triple(triple) {
dirs.push(base.join("usr").join("lib").join(arch));
}
}
X86LibC::Msvcrt | X86LibC::UCRT => {
dirs.push(base.join("lib"));
dirs.push(base.join("Lib"));
let arch_dir = if triple.arch.is_64bit() { "x64" } else { "x86" };
dirs.push(base.join("Lib").join(arch_dir));
}
X86LibC::Newlib | X86LibC::Picolibc => {
dirs.push(base.join("lib"));
for variant in &["", "nano", "pic"] {
let vdir = base.join("lib").join(variant);
if vdir.exists() && !dirs.contains(&vdir) {
dirs.push(vdir);
}
}
}
}
dirs
}
pub fn find_crt_dir(
triple: &Triple,
libc_type: X86LibC,
root: Option<&Path>,
) -> Option<PathBuf> {
let base = match root {
Some(r) => r.to_path_buf(),
None => PathBuf::from("/"),
};
match libc_type {
X86LibC::Glibc => {
let candidates = vec![
base.join("usr").join("lib"),
base.join("usr").join("lib64"),
base.join("lib"),
base.join("lib64"),
];
for p in candidates {
let crt1 = p.join("crt1.o");
if crt1.exists() {
return Some(p);
}
}
None
}
X86LibC::Musl => {
let candidates = vec![base.join("lib"), base.join("usr").join("lib")];
for p in candidates {
let crt1 = p.join("crt1.o");
if crt1.exists() {
return Some(p);
}
let scrt1 = p.join("Scrt1.o");
if scrt1.exists() {
return Some(p);
}
}
None
}
X86LibC::Bionic => {
let candidates = vec![base.join("usr").join("lib")];
for p in candidates {
let crtbegin = p.join("crtbegin_dynamic.o");
if crtbegin.exists() {
return Some(p);
}
}
None
}
X86LibC::Newlib | X86LibC::Picolibc => {
let candidates = vec![base.join("lib")];
for p in candidates {
let crt0 = p.join("crt0.o");
if crt0.exists() {
return Some(p);
}
}
None
}
_ => None,
}
}
pub fn detect_dynamic_linker(triple: &Triple, libc_type: X86LibC) -> Option<String> {
match libc_type {
X86LibC::Glibc => X86Multiarch::determine_dynamic_linker(triple),
X86LibC::Musl => {
let arch_dir = match triple.arch {
Arch::X86_64 => "x86_64",
Arch::X86 => "i386",
_ => triple.arch.as_str(),
};
Some(format!("/lib/ld-musl-{}.so.1", arch_dir))
}
X86LibC::Bionic => {
if triple.arch.is_64bit() {
Some("/system/bin/linker64".to_string())
} else {
Some("/system/bin/linker".to_string())
}
}
_ => None,
}
}
pub fn detect_libc_version(
triple: &Triple,
libc_type: X86LibC,
root: Option<&Path>,
) -> Option<String> {
match libc_type {
X86LibC::Glibc => Self::detect_glibc_version(root),
X86LibC::Musl => Self::detect_musl_version(root),
X86LibC::Bionic => Self::detect_bionic_version(root),
X86LibC::Newlib => Self::detect_newlib_version(root),
X86LibC::Picolibc => Self::detect_picolibc_version(root),
_ => None,
}
}
fn android_arch_for_triple(triple: &Triple) -> Option<String> {
match triple.arch {
Arch::X86 => Some("i686".to_string()),
Arch::X86_64 => Some("x86_64".to_string()),
Arch::ARM => Some("arm".to_string()),
Arch::AArch64 => Some("aarch64".to_string()),
_ => None,
}
}
pub fn include_paths(&self, _sysroot: &Sysroot) -> Vec<PathBuf> {
self.include_dirs.clone()
}
pub fn library_paths(&self, _sysroot: &Sysroot) -> Vec<PathBuf> {
self.library_dirs.clone()
}
pub fn is_cross(&self) -> bool {
Self::is_cross_target(&self.triple)
}
pub fn compiler_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
match self.libc_type {
X86LibC::Glibc => {
}
X86LibC::Musl => {
flags.push("-D_LARGEFILE64_SOURCE".to_string());
flags.push("-D_XOPEN_SOURCE=700".to_string());
}
X86LibC::Bionic => {
flags.push("-D__ANDROID__".to_string());
flags.push("-DANDROID".to_string());
}
X86LibC::Newlib => {
flags.push("-ffreestanding".to_string());
flags.push("-nostdlib".to_string());
}
X86LibC::Picolibc => {
flags.push("-ffreestanding".to_string());
flags.push("-specs=picolibc.specs".to_string());
}
X86LibC::None_ => {
flags.push("-ffreestanding".to_string());
flags.push("-nostdlib".to_string());
}
_ => {}
}
if let Some(ref root) = self.root_path {
flags.push(format!("--sysroot={}", root.display()));
}
flags
}
pub fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
match self.libc_type {
X86LibC::Glibc => {
flags.push("-lc".to_string());
flags.push("-lgcc_s".to_string());
}
X86LibC::Musl => {
flags.push("-lc".to_string());
}
X86LibC::Bionic => {
flags.push("-lc".to_string());
flags.push("-ldl".to_string());
flags.push("-lm".to_string());
}
X86LibC::Newlib => {
flags.push("-lc".to_string());
flags.push("-lnosys".to_string());
}
X86LibC::Picolibc => {
flags.push("-lc".to_string());
flags.push("-lgcc".to_string());
}
X86LibC::None_ => {
flags.push("-nostdlib".to_string());
}
_ => {}
}
if let Some(ref linker) = self.dynamic_linker {
flags.push(format!("-Wl,--dynamic-linker={}", linker));
}
flags
}
pub fn crt_objects(&self, is_shared: bool, is_pie: bool) -> Vec<String> {
let mut objects = Vec::new();
let crt_prefix = self
.crt_dir
.as_ref()
.map(|p| p.to_string_lossy().to_string());
match self.libc_type {
X86LibC::Glibc => {
if let Some(ref prefix) = crt_prefix {
if is_shared {
objects.push(format!("{}/crti.o", prefix));
objects.push(format!("{}/crtbeginS.o", prefix));
} else if is_pie {
objects.push(format!("{}/Scrt1.o", prefix));
objects.push(format!("{}/crti.o", prefix));
objects.push(format!("{}/crtbeginS.o", prefix));
} else {
objects.push(format!("{}/crt1.o", prefix));
objects.push(format!("{}/crti.o", prefix));
objects.push(format!("{}/crtbegin.o", prefix));
}
}
}
X86LibC::Musl => {
if let Some(ref prefix) = crt_prefix {
if is_shared {
objects.push(format!("{}/rcrt1.o", prefix));
objects.push(format!("{}/crtbeginS.o", prefix));
} else if is_pie {
objects.push(format!("{}/Scrt1.o", prefix));
objects.push(format!("{}/crti.o", prefix));
objects.push(format!("{}/crtbeginS.o", prefix));
} else {
objects.push(format!("{}/crt1.o", prefix));
objects.push(format!("{}/crti.o", prefix));
objects.push(format!("{}/crtbegin.o", prefix));
}
}
}
X86LibC::Bionic => {
if let Some(ref prefix) = crt_prefix {
objects.push(format!("{}/crtbegin_dynamic.o", prefix));
objects.push(format!("{}/crtend_android.o", prefix));
}
}
X86LibC::Newlib | X86LibC::Picolibc => {
if let Some(ref prefix) = crt_prefix {
objects.push(format!("{}/crt0.o", prefix));
}
}
_ => {}
}
objects
}
pub fn crt_end_objects(&self) -> Vec<String> {
let mut objects = Vec::new();
if let Some(ref crt_dir) = self.crt_dir {
let prefix = crt_dir.to_string_lossy();
match self.libc_type {
X86LibC::Glibc | X86LibC::Musl => {
objects.push(format!("{}/crtend.o", prefix));
objects.push(format!("{}/crtn.o", prefix));
}
X86LibC::Bionic => {
objects.push(format!("{}/crtend_android.o", prefix));
}
_ => {}
}
}
objects
}
pub fn standard_headers_available(&self) -> Vec<String> {
let mut headers = Vec::new();
let std_headers = vec![
"stdio.h",
"stdlib.h",
"string.h",
"math.h",
"time.h",
"signal.h",
"setjmp.h",
"errno.h",
"assert.h",
"ctype.h",
"locale.h",
"stddef.h",
"stdint.h",
"stdbool.h",
"stdarg.h",
"limits.h",
"float.h",
"unistd.h",
"fcntl.h",
"pthread.h",
"dlfcn.h",
];
for inc_dir in &self.include_dirs {
for header in &std_headers {
let path = inc_dir.join(header);
if path.exists() {
headers.push(header.to_string());
}
}
}
headers.sort();
headers.dedup();
headers
}
pub fn libc_path(&self) -> Option<PathBuf> {
for lib_dir in &self.library_dirs {
let libc_so = lib_dir.join("libc.so.6");
if libc_so.exists() {
return Some(libc_so);
}
let libc_so_sym = lib_dir.join("libc.so");
if libc_so_sym.exists() {
return Some(libc_so_sym);
}
let libc_a = lib_dir.join("libc.a");
if libc_a.exists() {
return Some(libc_a);
}
}
None
}
pub fn libm_path(&self) -> Option<PathBuf> {
for lib_dir in &self.library_dirs {
let libm_so = lib_dir.join("libm.so.6");
if libm_so.exists() {
return Some(libm_so);
}
let libm_so_sym = lib_dir.join("libm.so");
if libm_so_sym.exists() {
return Some(libm_so_sym);
}
let libm_a = lib_dir.join("libm.a");
if libm_a.exists() {
return Some(libm_a);
}
}
None
}
pub fn libpthread_path(&self) -> Option<PathBuf> {
for lib_dir in &self.library_dirs {
let libpthread_so = lib_dir.join("libpthread.so");
if libpthread_so.exists() {
return Some(libpthread_so);
}
let libpthread_a = lib_dir.join("libpthread.a");
if libpthread_a.exists() {
return Some(libpthread_a);
}
let libc_so = lib_dir.join("libc.so");
if libc_so.exists() {
return Some(libc_so); }
}
None
}
pub fn validate(&self) -> Vec<String> {
let mut issues = Vec::new();
if !self.found {
issues.push(format!(
"Cross-library not found for {}",
self.triple.to_string()
));
return issues;
}
if self.include_dirs.is_empty() {
issues.push("No include directories found".to_string());
} else {
let mut has_stdio = false;
for inc_dir in &self.include_dirs {
if inc_dir.join("stdio.h").exists() {
has_stdio = true;
break;
}
}
if !has_stdio {
issues.push("stdio.h not found in include directories".to_string());
}
}
if self.library_dirs.is_empty() {
issues.push("No library directories found".to_string());
}
if self.libc_type != X86LibC::None_ && self.crt_dir.is_none() {
issues.push("CRT directory not found (startup objects missing)".to_string());
}
issues
}
pub fn supports_shared_libraries(&self) -> bool {
match self.libc_type {
X86LibC::Glibc | X86LibC::Musl | X86LibC::Bionic | X86LibC::LibSystem => true,
X86LibC::Newlib | X86LibC::Picolibc | X86LibC::None_ => false,
X86LibC::Msvcrt | X86LibC::UCRT => true,
}
}
pub fn supports_static_linking(&self) -> bool {
self.libc_path()
.map(|p| p.extension().map(|e| e == "a").unwrap_or(false))
.unwrap_or(false)
}
pub fn supports_pie(&self) -> bool {
match self.libc_type {
X86LibC::Glibc | X86LibC::Musl => true,
X86LibC::Bionic => {
if let Some(ref root) = self.root_path {
if let Some(ref crt) = self.crt_dir {
crt.join("Scrt1.o").exists()
} else {
false
}
} else {
true }
}
_ => false,
}
}
}
impl Default for X86CrossLibrary {
fn default() -> Self {
Self::new(&Triple::x86_64_linux())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sysroot_manager_new() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
assert_eq!(manager.target_triple, "x86_64-unknown-linux-gnu");
assert_eq!(manager.triple.arch, Arch::X86_64);
assert_eq!(manager.triple.os, OS::Linux);
}
#[test]
fn test_sysroot_manager_with_sysroot() {
let manager =
X86SysrootManager::with_sysroot("i386-unknown-linux-gnu", PathBuf::from("/custom"));
assert_eq!(manager.explicit_sysroot, Some(PathBuf::from("/custom")));
assert_eq!(manager.triple.arch, Arch::X86);
}
#[test]
fn test_sysroot_manager_for_cross() {
let manager = X86SysrootManager::for_cross(
"x86_64-unknown-linux-musl",
PathBuf::from("/musl-sysroot"),
);
assert!(manager.is_cross);
assert!(manager.use_compiler_rt);
assert_eq!(
manager.explicit_sysroot,
Some(PathBuf::from("/musl-sysroot"))
);
}
#[test]
fn test_sysroot_manager_default_triple() {
let manager = X86SysrootManager::default();
assert_eq!(manager.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_sysroot_manager_set_sysroot_arg() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.set_sysroot_arg("--sysroot=/test/path");
assert_eq!(manager.explicit_sysroot, Some(PathBuf::from("/test/path")));
}
#[test]
fn test_sysroot_manager_compiler_flags() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.explicit_sysroot = Some(PathBuf::from("/test"));
manager.use_compiler_rt = true;
let flags = manager.compiler_flags();
assert!(flags.iter().any(|f| f.contains("--sysroot=")));
assert!(flags.iter().any(|f| f.contains("-target")));
assert!(flags.iter().any(|f| f.contains("compiler-rt")));
}
#[test]
fn test_sysroot_manager_is_native() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(manager.is_native());
}
}
#[test]
fn test_sysroot_manager_dynamic_linker() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let linker = manager.dynamic_linker();
assert!(linker.is_some());
let linker_str = linker.unwrap();
assert!(linker_str.contains("ld-linux"));
}
#[test]
fn test_sysroot_detector_new() {
let detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
assert_eq!(detector.target_triple, "x86_64-unknown-linux-gnu");
assert!(!detector.search_android_ndk);
assert!(!detector.search_windows_sdk);
}
#[test]
fn test_sysroot_detector_android() {
let detector = X86SysrootDetector::new("i686-linux-android");
assert!(detector.search_android_ndk);
}
#[test]
fn test_sysroot_detector_windows() {
let detector = X86SysrootDetector::new("x86_64-pc-windows-msvc");
assert!(detector.search_windows_sdk);
}
#[test]
fn test_sysroot_detector_macos() {
let detector = X86SysrootDetector::new("x86_64-apple-darwin");
assert!(detector.search_xcode);
}
#[test]
fn test_sysroot_detector_ios() {
let detector = X86SysrootDetector::new("x86_64-apple-ios");
assert!(detector.search_xcode);
}
#[test]
fn test_sysroot_detector_host_triple() {
let detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
let result = detector.is_host_triple();
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(result);
}
}
#[test]
fn test_sysroot_detector_detect_host_triple() {
let host = X86SysrootDetector::detect_host_triple();
assert!(!host.is_empty());
assert!(host.contains("-"));
}
#[test]
fn test_sysroot_detector_android_arch_name() {
let detector = X86SysrootDetector::new("x86_64-linux-android");
assert_eq!(detector.android_arch_name(), "x86_64");
let detector = X86SysrootDetector::new("i686-linux-android");
assert_eq!(detector.android_arch_name(), "x86");
}
#[test]
fn test_sysroot_detector_host_platform_tag() {
let detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
let tag = detector.host_platform_tag();
if cfg!(target_os = "linux") {
assert!(tag.contains("linux"));
} else if cfg!(target_os = "macos") {
assert!(tag.contains("darwin"));
} else if cfg!(target_os = "windows") {
assert!(tag.contains("windows"));
}
}
#[test]
fn test_parse_sdk_version() {
let v = X86SysrootDetector::parse_sdk_version("MacOSX14.5.sdk");
assert_eq!(v, vec![14, 5]);
let v = X86SysrootDetector::parse_sdk_version("iPhoneOS17.0.sdk");
assert_eq!(v, vec![17, 0]);
let v = X86SysrootDetector::parse_sdk_version("MacOSX.sdk");
assert!(v.is_empty());
}
#[test]
fn test_sysroot_detector_default() {
let detector = X86SysrootDetector::default();
assert_eq!(detector.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_sysroot_detector_detect_host() {
let detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
let sysroot = detector.detect();
if cfg!(target_os = "linux") {
assert_eq!(sysroot.path, PathBuf::from("/"));
assert!(!sysroot.is_explicit);
}
}
#[test]
fn test_sysroot_detector_forced() {
let mut detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
detector.forced_sysroot = Some(PathBuf::from("/custom/sysroot"));
assert_eq!(
detector.forced_sysroot,
Some(PathBuf::from("/custom/sysroot"))
);
}
#[test]
fn test_cross_compilation_new() {
let cc = X86CrossCompilation::new("x86_64-unknown-linux-gnu");
assert_eq!(cc.target_triple, "x86_64-unknown-linux-gnu");
assert!(!cc.is_canadian_cross);
}
#[test]
fn test_cross_compilation_is_cross() {
let cc = X86CrossCompilation::new("aarch64-unknown-linux-gnu");
if cfg!(target_arch = "x86_64") {
assert!(cc.is_cross);
}
}
#[test]
fn test_cross_compilation_native() {
let cc = X86CrossCompilation::new("x86_64-unknown-linux-gnu");
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(!cc.is_cross);
}
}
#[test]
fn test_cross_compilation_32bit_on_64bit() {
let cc = X86CrossCompilation::new("i386-unknown-linux-gnu");
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(!cc.is_cross);
}
}
#[test]
fn test_cross_compilation_emulator_x86_64_windows() {
let cc = X86CrossCompilation::new("x86_64-pc-windows-msvc");
if cfg!(target_os = "linux") {
let emu = cc.emulator;
if let Some(ref emu) = emu {
assert!(emu.contains("wine"));
}
}
}
#[test]
fn test_cross_compilation_emulator_x86_windows() {
let cc = X86CrossCompilation::new("i686-pc-windows-msvc");
let _ = cc.emulator_command();
}
#[test]
fn test_cross_compilation_canadian_setup() {
let mut cc = X86CrossCompilation::new("x86_64-unknown-linux-gnu");
let result = cc.setup_canadian_cross(
"x86_64-apple-darwin",
"aarch64-unknown-linux-gnu",
"arm-unknown-linux-gnueabihf",
);
assert!(result.is_ok());
assert!(cc.is_canadian_cross);
assert_eq!(cc.build_triple, "x86_64-apple-darwin");
assert_eq!(cc.host_triple, "aarch64-unknown-linux-gnu");
}
#[test]
fn test_cross_compilation_host_to_target_mapping() {
let cc = X86CrossCompilation::new("x86_64-unknown-linux-gnu");
let map = cc.host_to_target_mapping();
assert!(map.contains_key("x86_64-unknown-linux-gnu"));
let targets = &map["x86_64-unknown-linux-gnu"];
assert!(targets.contains(&"x86_64-unknown-linux-gnu".to_string()));
assert!(targets.contains(&"x86_64-unknown-linux-musl".to_string()));
}
#[test]
fn test_cross_compilation_pkg_config_command() {
let cc = X86CrossCompilation::new("x86_64-unknown-linux-gnu");
let cmd = cc.pkg_config_command();
assert!(!cmd.is_empty());
}
#[test]
fn test_cross_compilation_pkg_config_env() {
let mut cc = X86CrossCompilation::new("arm-unknown-linux-gnueabihf");
cc.sysroot = Some(PathBuf::from("/sysroot"));
let env = cc.pkg_config_env();
assert!(env.iter().any(|(k, _)| k == "PKG_CONFIG_SYSROOT_DIR"));
}
#[test]
fn test_cross_compilation_default() {
let cc = X86CrossCompilation::default();
assert_eq!(cc.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_multiarch_new_x86_64() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("x86_64-linux-gnu".to_string()));
}
#[test]
fn test_multiarch_new_i386() {
let triple = Triple::parse("i386-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("i386-linux-gnu".to_string()));
}
#[test]
fn test_multiarch_new_x32() {
let triple = Triple::parse("x86_64-unknown-linux-gnux32");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("x86_64-linux-gnux32".to_string()));
}
#[test]
fn test_multiarch_musl() {
let triple = Triple::parse("x86_64-unknown-linux-musl");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("x86_64-linux-musl".to_string()));
}
#[test]
fn test_multiarch_android() {
let triple = Triple::parse("x86_64-linux-android");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("x86_64-linux-android".to_string()));
}
#[test]
fn test_multiarch_aarch64() {
let triple = Triple::parse("aarch64-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("aarch64-linux-gnu".to_string()));
}
#[test]
fn test_multiarch_armhf() {
let triple = Triple::parse("arm-unknown-linux-gnueabihf");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("arm-linux-gnueabihf".to_string()));
}
#[test]
fn test_multiarch_arm() {
let triple = Triple::parse("arm-unknown-linux-gnueabi");
let ma = X86Multiarch::new(&triple);
assert_eq!(ma.multiarch_dir, Some("arm-linux-gnueabi".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_x86_64() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/lib64/ld-linux-x86-64.so.2".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_i386() {
let triple = Triple::parse("i386-unknown-linux-gnu");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/lib/ld-linux.so.2".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_musl() {
let triple = Triple::parse("x86_64-unknown-linux-musl");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/lib/ld-musl-x86_64.so.1".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_android() {
let triple = Triple::parse("x86_64-linux-android");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/system/bin/linker64".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_android32() {
let triple = Triple::parse("i686-linux-android");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/system/bin/linker".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_aarch64() {
let triple = Triple::parse("aarch64-unknown-linux-gnu");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/lib/ld-linux-aarch64.so.1".to_string()));
}
#[test]
fn test_multiarch_dynamic_linker_armhf() {
let triple = Triple::parse("arm-unknown-linux-gnueabihf");
let linker = X86Multiarch::determine_dynamic_linker(&triple);
assert_eq!(linker, Some("/lib/ld-linux-armhf.so.3".to_string()));
}
#[test]
fn test_multiarch_default() {
let ma = X86Multiarch::default();
assert_eq!(ma.multiarch_dir, Some("x86_64-linux-gnu".to_string()));
}
#[test]
fn test_multiarch_is_multiarch_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
assert!(ma.is_multiarch_path(Path::new("/usr/lib/x86_64-linux-gnu")));
assert!(!ma.is_multiarch_path(Path::new("/usr/lib")));
}
#[test]
fn test_multiarch_normalize_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
let normalized = ma.normalize_library_path(Path::new("/usr/lib"));
if ma.is_debian && ma.prefer_multiarch {
assert!(normalized.to_string_lossy().contains("x86_64-linux-gnu"));
}
}
#[test]
fn test_triple_parser_new() {
let parser = X86TripleParser::new("x86_64-unknown-linux-gnu");
assert!(parser.is_valid);
assert_eq!(parser.arch_info.family, X86ArchFamily::X86_64);
assert_eq!(parser.arch_info.bit_width, 64);
assert!(parser.arch_info.has_sse2);
}
#[test]
fn test_triple_parser_i386() {
let parser = X86TripleParser::new("i386-unknown-linux-gnu");
assert!(parser.is_valid);
assert_eq!(parser.arch_info.family, X86ArchFamily::X86_32);
assert_eq!(parser.arch_info.bit_width, 32);
assert!(!parser.arch_info.has_sse2);
}
#[test]
fn test_triple_parser_normalize() {
let parser = X86TripleParser::new("amd64-unknown-linux");
assert!(parser.normalized.contains("x86_64"));
}
#[test]
fn test_triple_parser_object_format_elf() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let fmt = X86TripleParser::infer_object_format(&triple);
assert_eq!(fmt, X86ObjectFormat::ELF);
}
#[test]
fn test_triple_parser_object_format_macho() {
let triple = Triple::parse("x86_64-apple-darwin");
let fmt = X86TripleParser::infer_object_format(&triple);
assert_eq!(fmt, X86ObjectFormat::MachO);
}
#[test]
fn test_triple_parser_object_format_coff() {
let triple = Triple::parse("x86_64-pc-windows-msvc");
let fmt = X86TripleParser::infer_object_format(&triple);
assert_eq!(fmt, X86ObjectFormat::COFF);
}
#[test]
fn test_triple_parser_parse_x86_arch_basic() {
let info = X86TripleParser::parse_x86_arch("i686").unwrap();
assert_eq!(info.family, X86ArchFamily::X86_32);
assert_eq!(info.bit_width, 32);
assert!(!info.has_sse2);
}
#[test]
fn test_triple_parser_parse_x86_arch_pentium4() {
let info = X86TripleParser::parse_x86_arch("pentium4").unwrap();
assert!(info.has_sse2);
assert_eq!(info.cpu_sub_arch, Some("pentium4".to_string()));
}
#[test]
fn test_triple_parser_parse_x86_arch_athlon() {
let info = X86TripleParser::parse_x86_arch("athlon64").unwrap();
assert_eq!(info.family, X86ArchFamily::X86_64);
assert!(info.has_sse2);
}
#[test]
fn test_triple_parser_parse_x86_arch_zen() {
for cpu in &["znver1", "znver2", "znver3", "znver4", "znver5"] {
let info = X86TripleParser::parse_x86_arch(cpu).unwrap();
assert_eq!(info.family, X86ArchFamily::X86_64);
assert!(info.has_sse2);
}
}
#[test]
fn test_triple_parser_parse_x86_arch_core() {
for cpu in &[
"core2",
"nehalem",
"sandybridge",
"haswell",
"skylake",
"icelake-client",
"tigerlake",
"alderlake",
"raptorlake",
"meteorlake",
] {
let info = X86TripleParser::parse_x86_arch(cpu).unwrap();
assert_eq!(info.family, X86ArchFamily::X86_64);
assert!(info.has_sse2);
}
}
#[test]
fn test_triple_parser_parse_x86_arch_atom() {
for cpu in &[
"silvermont",
"goldmont",
"goldmont-plus",
"tremont",
"gracemont",
] {
let info = X86TripleParser::parse_x86_arch(cpu).unwrap();
assert_eq!(info.family, X86ArchFamily::X86_64);
assert!(info.has_sse2);
}
}
#[test]
fn test_triple_parser_parse_x86_arch_x32() {
let info = X86TripleParser::parse_x86_arch("x32").unwrap();
assert_eq!(info.family, X86ArchFamily::X32);
assert_eq!(info.bit_width, 32);
}
#[test]
fn test_triple_parser_parse_x86_arch_invalid() {
assert!(X86TripleParser::parse_x86_arch("nonexistentcpu").is_none());
}
#[test]
fn test_triple_parser_parse_x86_vendor() {
assert_eq!(X86TripleParser::parse_x86_vendor("pc"), Some(Vendor::PC));
assert_eq!(
X86TripleParser::parse_x86_vendor("apple"),
Some(Vendor::Apple)
);
assert_eq!(
X86TripleParser::parse_x86_vendor("unknown"),
Some(Vendor::Unknown)
);
}
#[test]
fn test_triple_parser_parse_x86_os() {
assert_eq!(X86TripleParser::parse_x86_os("linux"), Some(OS::Linux));
assert_eq!(X86TripleParser::parse_x86_os("darwin"), Some(OS::Darwin));
assert_eq!(X86TripleParser::parse_x86_os("windows"), Some(OS::Win32));
assert_eq!(X86TripleParser::parse_x86_os("freebsd"), Some(OS::FreeBSD));
assert_eq!(X86TripleParser::parse_x86_os("haiku"), Some(OS::Haiku));
}
#[test]
fn test_triple_parser_parse_x86_env() {
assert_eq!(
X86TripleParser::parse_x86_env("gnu"),
Some(Environment::GNU)
);
assert_eq!(
X86TripleParser::parse_x86_env("musl"),
Some(Environment::Musl)
);
assert_eq!(
X86TripleParser::parse_x86_env("msvc"),
Some(Environment::MSVC)
);
assert_eq!(
X86TripleParser::parse_x86_env("android"),
Some(Environment::Android)
);
}
#[test]
fn test_triple_parser_known_cpus() {
let cpus = X86TripleParser::known_x86_cpus();
assert!(cpus.contains(&"x86-64"));
assert!(cpus.contains(&"skylake"));
assert!(cpus.contains(&"znver4"));
assert!(cpus.len() > 30);
}
#[test]
fn test_triple_parser_default_features() {
let features = X86TripleParser::default_features_for_cpu("haswell");
assert!(features.contains(&"+avx"));
assert!(features.contains(&"+avx2"));
assert!(features.contains(&"+sse2"));
let features = X86TripleParser::default_features_for_cpu("i686");
assert!(features.contains(&"+sse"));
assert!(features.contains(&"+mmx"));
let features = X86TripleParser::default_features_for_cpu("znver3");
assert!(features.contains(&"+avx2"));
}
#[test]
fn test_triple_parser_default() {
let parser = X86TripleParser::default();
assert!(parser.is_valid);
assert_eq!(parser.triple.arch, Arch::X86_64);
}
#[test]
fn test_compiler_rt_new() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let crt = X86CompilerRT::new(&triple);
assert_eq!(crt.triple.arch, Arch::X86_64);
}
#[test]
fn test_compiler_rt_builtins_library_name() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let name = X86CompilerRT::builtins_library_name(&triple);
assert!(name.contains("builtins"));
assert!(name.contains("x86_64"));
let triple = Triple::parse("i386-unknown-linux-gnu");
let name = X86CompilerRT::builtins_library_name(&triple);
assert!(name.contains("i386"));
}
#[test]
fn test_compiler_rt_select_runtime_auto() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let mut crt = X86CompilerRT::new(&triple);
crt.preferred_runtime = X86RuntimeLib::Auto;
let selected = crt.select_runtime();
assert!(selected == X86RuntimeLib::CompilerRT || selected == X86RuntimeLib::LibGcc);
}
#[test]
fn test_compiler_rt_select_runtime_forced() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let mut crt = X86CompilerRT::new(&triple);
crt.preferred_runtime = X86RuntimeLib::CompilerRT;
assert_eq!(crt.select_runtime(), X86RuntimeLib::CompilerRT);
crt.preferred_runtime = X86RuntimeLib::LibGcc;
assert_eq!(crt.select_runtime(), X86RuntimeLib::LibGcc);
}
#[test]
fn test_compiler_rt_needs_libgcc_eh() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
assert!(X86CompilerRT::needs_libgcc_eh(&triple));
let triple = Triple::parse("x86_64-unknown-linux-musl");
assert!(!X86CompilerRT::needs_libgcc_eh(&triple));
}
#[test]
fn test_compiler_rt_runtime_libraries() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let mut crt = X86CompilerRT::new(&triple);
crt.preferred_runtime = X86RuntimeLib::CompilerRT;
let libs = crt.runtime_libraries();
assert!(libs.iter().any(|l| l.contains("builtins")));
crt.preferred_runtime = X86RuntimeLib::LibGcc;
let libs = crt.runtime_libraries();
assert!(libs.contains(&"gcc".to_string()));
}
#[test]
fn test_compiler_rt_linker_flags() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let mut crt = X86CompilerRT::new(&triple);
crt.preferred_runtime = X86RuntimeLib::CompilerRT;
let flags = crt.linker_flags();
assert!(flags.iter().any(|f| f.contains("compiler-rt")));
}
#[test]
fn test_compiler_rt_default() {
let crt = X86CompilerRT::default();
assert_eq!(crt.triple.arch, Arch::X86_64);
}
#[test]
fn test_resource_dir_new() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
assert!(!rd.path.as_os_str().is_empty());
}
#[test]
fn test_resource_dir_expected_headers() {
let headers = X86ResourceDir::expected_builtin_headers();
assert!(headers.contains(&"stddef.h"));
assert!(headers.contains(&"stdarg.h"));
assert!(headers.contains(&"stdint.h"));
assert!(headers.contains(&"float.h"));
assert!(headers.contains(&"stdbool.h"));
assert!(headers.contains(&"x86intrin.h"));
}
#[test]
fn test_resource_dir_path_methods() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let path = rd.path();
let inc = rd.include_dir();
let lib = rd.lib_dir();
assert!(inc.ends_with("include"));
assert!(lib.ends_with("lib"));
assert_eq!(inc, rd.builtin_headers_dir());
}
#[test]
fn test_resource_dir_validate() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let issues = rd.validate();
assert!(issues.len() >= 0);
}
#[test]
fn test_resource_dir_default() {
let rd = X86ResourceDir::default();
assert!(!rd.path.as_os_str().is_empty());
}
#[test]
fn test_resource_dir_target_lib_dir() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let target_lib = rd.target_lib_dir();
assert!(target_lib.to_string_lossy().contains("x86_64"));
}
#[test]
fn test_cross_library_new() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
assert_eq!(cl.libc_type, X86LibC::Glibc);
}
#[test]
fn test_cross_library_detect_libc_glibc() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
assert_eq!(X86CrossLibrary::detect_libc_type(&triple), X86LibC::Glibc);
}
#[test]
fn test_cross_library_detect_libc_musl() {
let triple = Triple::parse("x86_64-unknown-linux-musl");
assert_eq!(X86CrossLibrary::detect_libc_type(&triple), X86LibC::Musl);
}
#[test]
fn test_cross_library_detect_libc_bionic() {
let triple = Triple::parse("x86_64-linux-android");
assert_eq!(X86CrossLibrary::detect_libc_type(&triple), X86LibC::Bionic);
}
#[test]
fn test_cross_library_detect_libc_darwin() {
let triple = Triple::parse("x86_64-apple-darwin");
assert_eq!(
X86CrossLibrary::detect_libc_type(&triple),
X86LibC::LibSystem
);
}
#[test]
fn test_cross_library_detect_libc_msvc() {
let triple = Triple::parse("x86_64-pc-windows-msvc");
assert_eq!(X86CrossLibrary::detect_libc_type(&triple), X86LibC::Msvcrt);
}
#[test]
fn test_cross_library_dynamic_linker_glibc() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let linker = X86CrossLibrary::detect_dynamic_linker(&triple, X86LibC::Glibc);
assert_eq!(linker, Some("/lib64/ld-linux-x86-64.so.2".to_string()));
}
#[test]
fn test_cross_library_dynamic_linker_musl() {
let triple = Triple::parse("x86_64-unknown-linux-musl");
let linker = X86CrossLibrary::detect_dynamic_linker(&triple, X86LibC::Musl);
assert_eq!(linker, Some("/lib/ld-musl-x86_64.so.1".to_string()));
}
#[test]
fn test_cross_library_dynamic_linker_bionic() {
let triple = Triple::parse("x86_64-linux-android");
let linker = X86CrossLibrary::detect_dynamic_linker(&triple, X86LibC::Bionic);
assert_eq!(linker, Some("/system/bin/linker64".to_string()));
let triple = Triple::parse("i686-linux-android");
let linker = X86CrossLibrary::detect_dynamic_linker(&triple, X86LibC::Bionic);
assert_eq!(linker, Some("/system/bin/linker".to_string()));
}
#[test]
fn test_cross_library_is_cross() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(!cl.is_cross());
}
}
#[test]
fn test_cross_library_compiler_flags_musl() {
let triple = Triple::parse("x86_64-unknown-linux-musl");
let cl = X86CrossLibrary::new(&triple);
let flags = cl.compiler_flags();
assert!(flags.iter().any(|f| f.contains("_LARGEFILE64_SOURCE")));
}
#[test]
fn test_cross_library_compiler_flags_newlib() {
let triple = Triple::parse("i686-elf");
let cl = X86CrossLibrary::new(&triple);
let flags = cl.compiler_flags();
assert!(flags.iter().any(|f| f == "-ffreestanding"));
assert!(flags.iter().any(|f| f == "-nostdlib"));
}
#[test]
fn test_cross_library_default() {
let cl = X86CrossLibrary::default();
assert_eq!(cl.libc_type, X86LibC::Glibc);
}
#[test]
fn test_cross_library_include_paths() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let sysroot = Sysroot::host();
let paths = cl.include_paths(&sysroot);
assert!(!paths.is_empty());
}
#[test]
fn test_cross_library_library_paths() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let sysroot = Sysroot::host();
let paths = cl.library_paths(&sysroot);
assert!(!paths.is_empty());
}
#[test]
fn test_full_linux_glibc_pipeline() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.detect_gcc();
let sysroot = manager.detect_sysroot();
let includes = manager.build_include_paths(&sysroot);
let libs = manager.build_library_paths(&sysroot);
let total_paths = includes.len() + libs.len();
if cfg!(target_os = "linux") {
assert!(total_paths > 0);
}
}
#[test]
fn test_full_android_pipeline() {
let manager = X86SysrootManager::new("x86_64-linux-android");
assert_eq!(manager.triple.environment, Environment::Android);
let cl = X86CrossLibrary::new(&manager.triple);
assert_eq!(cl.libc_type, X86LibC::Bionic);
}
#[test]
fn test_full_cross_compilation_pipeline() {
let mut cc = X86CrossCompilation::new("aarch64-unknown-linux-gnu");
let result = cc.setup_canadian_cross(
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
);
assert!(result.is_ok());
assert!(cc.is_canadian_cross);
}
#[test]
fn test_multiarch_search_order() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
let sysroot = Sysroot::host();
let paths = ma.library_paths(&sysroot);
assert!(!paths.is_empty());
let order = ma.search_order_priority();
assert!(!order.is_empty());
}
#[test]
fn test_triple_roundtrip() {
let triples = vec![
"x86_64-unknown-linux-gnu",
"i386-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-pc-windows-msvc",
"x86_64-apple-darwin",
"i686-linux-android",
];
for triple_str in triples {
let parser = X86TripleParser::new(triple_str);
assert!(parser.is_valid, "{} should be valid", triple_str);
}
}
#[test]
fn test_compiler_rt_integration() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let crt = X86CompilerRT::new(&triple);
let name = X86CompilerRT::builtins_library_name(&triple);
assert!(name.contains("builtins"));
let mut crt = X86CompilerRT::new(&triple);
crt.preferred_runtime = X86RuntimeLib::CompilerRT;
let libs = crt.runtime_libraries();
assert!(!libs.is_empty());
}
#[test]
fn test_resource_dir_integration() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let headers = X86ResourceDir::expected_builtin_headers();
assert!(headers.len() > 50, "Should have many builtin headers");
}
#[test]
fn test_cross_library_all_libcs() {
let test_cases = vec![
("x86_64-unknown-linux-gnu", X86LibC::Glibc),
("x86_64-unknown-linux-musl", X86LibC::Musl),
("x86_64-linux-android", X86LibC::Bionic),
("x86_64-apple-darwin", X86LibC::LibSystem),
("x86_64-pc-windows-msvc", X86LibC::Msvcrt),
];
for (triple_str, expected_libc) in test_cases {
let triple = Triple::parse(triple_str);
let libc = X86CrossLibrary::detect_libc_type(&triple);
assert_eq!(
libc, expected_libc,
"Wrong libc for {}: expected {:?}, got {:?}",
triple_str, expected_libc, libc
);
}
}
#[test]
fn test_x86_triple_parser_all_modern_cpus() {
let modern_cpus = vec![
"znver1",
"znver2",
"znver3",
"znver4",
"znver5",
"alderlake",
"raptorlake",
"meteorlake",
"arrowlake",
"lunarlake",
"pantherlake",
"sapphirerapids",
"graniterapids",
"crestmont",
"skymont",
];
for cpu in modern_cpus {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
let info = info.unwrap();
assert!(info.has_sse2, "CPU {} should have SSE2", cpu);
assert_eq!(info.family, X86ArchFamily::X86_64);
}
}
#[test]
fn test_x86_64_v_levels() {
for cpu in &["x86-64", "x86-64-v2", "x86-64-v3", "x86-64-v4"] {
let features = X86TripleParser::default_features_for_cpu(cpu);
assert!(features.contains(&"+sse2"));
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
}
}
#[test]
fn test_cross_compilation_toolchain_list() {
let toolchains = X86CrossCompilation::list_toolchains();
let _ = toolchains.len();
}
#[test]
fn test_multiarch_known_tuples() {
let tuples = X86Multiarch::known_multiarch_tuples();
let _ = tuples.len();
}
#[test]
fn test_sysroot_manager_env_vars() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let _ = manager.env_vars.len();
}
#[test]
fn test_sysroot_detector_custom_search_paths() {
let mut detector = X86SysrootDetector::new("x86_64-unknown-linux-gnu");
detector
.custom_search_paths
.push(PathBuf::from("/tmp/nonexistent"));
assert_eq!(detector.custom_search_paths.len(), 1);
}
#[test]
fn test_cross_library_crt_dir() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
if cfg!(target_os = "linux") {
let crt = cl.crt_dir;
let _ = crt;
}
}
#[test]
fn test_x86_libc_display() {
let variants = vec![
X86LibC::Glibc,
X86LibC::Musl,
X86LibC::Bionic,
X86LibC::LibSystem,
X86LibC::Msvcrt,
X86LibC::UCRT,
X86LibC::Newlib,
X86LibC::Picolibc,
X86LibC::None_,
];
assert_eq!(variants.len(), 9);
}
#[test]
fn test_x86_arch_family_display() {
let families = vec![
X86ArchFamily::X86_16,
X86ArchFamily::X86_32,
X86ArchFamily::X86_64,
X86ArchFamily::X32,
];
assert_eq!(families.len(), 4);
}
#[test]
fn test_x86_object_format_display() {
let formats = vec![
X86ObjectFormat::ELF,
X86ObjectFormat::MachO,
X86ObjectFormat::COFF,
X86ObjectFormat::Wasm,
X86ObjectFormat::XCOFF,
];
assert_eq!(formats.len(), 5);
}
#[test]
fn test_remote_build_config_new() {
let config = X86RemoteBuildConfig::new();
assert!(!config.use_distcc);
assert!(!config.use_icecc);
assert_eq!(config.remote_jobs, 4);
}
#[test]
fn test_remote_build_config_distcc() {
let config = X86RemoteBuildConfig::new().with_distcc(&["host1", "host2"]);
assert!(config.use_distcc);
assert_eq!(config.compiler_wrapper(), Some("distcc".to_string()));
}
#[test]
fn test_remote_build_config_icecc() {
let config = X86RemoteBuildConfig::new().with_icecc();
assert!(config.use_icecc);
assert_eq!(config.compiler_wrapper(), Some("icecc".to_string()));
}
#[test]
fn test_remote_build_config_no_wrapper() {
let config = X86RemoteBuildConfig::new();
assert_eq!(config.compiler_wrapper(), None);
}
#[test]
fn test_cross_package_manager_new() {
let pm = X86CrossPackageManager::new("x86_64-unknown-linux-gnu");
assert!(pm.cross_arch_suffix.contains("amd64"));
let pm = X86CrossPackageManager::new("i386-unknown-linux-gnu");
assert!(pm.cross_arch_suffix.contains("i386"));
let pm = X86CrossPackageManager::new("aarch64-unknown-linux-gnu");
assert!(pm.cross_arch_suffix.contains("arm64"));
}
#[test]
fn test_cross_package_manager_install_command() {
let pm = X86CrossPackageManager::new("x86_64-unknown-linux-gnu");
let cmd = pm.install_cross_deps_command();
if let Some(cmd) = cmd {
assert!(!cmd.is_empty());
}
}
#[test]
fn test_qemu_emulation_detect_x86_64() {
let qemu = X86QemuEmulation::detect(Arch::X86_64);
assert_eq!(qemu.qemu_arch_name, "x86_64");
}
#[test]
fn test_qemu_emulation_detect_i386() {
let qemu = X86QemuEmulation::detect(Arch::X86);
assert_eq!(qemu.qemu_arch_name, "i386");
}
#[test]
fn test_qemu_emulation_detect_arm() {
let qemu = X86QemuEmulation::detect(Arch::ARM);
assert_eq!(qemu.qemu_arch_name, "arm");
}
#[test]
fn test_qemu_emulation_detect_aarch64() {
let qemu = X86QemuEmulation::detect(Arch::AArch64);
assert_eq!(qemu.qemu_arch_name, "aarch64");
}
#[test]
fn test_qemu_emulation_detect_riscv64() {
let qemu = X86QemuEmulation::detect(Arch::RISCV64);
assert_eq!(qemu.qemu_arch_name, "riscv64");
}
#[test]
fn test_qemu_emulation_command() {
let qemu = X86QemuEmulation::detect(Arch::AArch64);
let cmd = qemu.emulation_command("/test/binary");
if qemu.available {
assert!(cmd.is_some());
let cmd_str = cmd.unwrap();
assert!(cmd_str.contains("/test/binary"));
}
}
#[test]
fn test_multiarch_detect_distribution() {
let distro = X86Multiarch::detect_distribution();
let _ = distro.as_str();
}
#[test]
fn test_multiarch_all_debian_tuples() {
let tuples = X86Multiarch::all_debian_multiarch_tuples();
assert!(tuples.len() > 10, "Should have many Debian tuples");
assert!(tuples.contains(&"x86_64-linux-gnu"));
assert!(tuples.contains(&"i386-linux-gnu"));
assert!(tuples.contains(&"aarch64-linux-gnu"));
}
#[test]
fn test_multiarch_libc_dir_suffix() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
assert_eq!(X86Multiarch::libc_dir_suffix(&triple), "-gnu");
let triple = Triple::parse("x86_64-unknown-linux-musl");
assert_eq!(X86Multiarch::libc_dir_suffix(&triple), "-musl");
let triple = Triple::parse("x86_64-linux-android");
assert_eq!(X86Multiarch::libc_dir_suffix(&triple), "-android");
}
#[test]
fn test_linux_distro_as_str() {
let distros = vec![
X86LinuxDistro::Debian,
X86LinuxDistro::RHEL,
X86LinuxDistro::Fedora,
X86LinuxDistro::Arch,
X86LinuxDistro::Gentoo,
X86LinuxDistro::Alpine,
X86LinuxDistro::NixOS,
];
for distro in distros {
let s = distro.as_str();
assert!(!s.is_empty());
assert_ne!(s, "unknown");
}
}
#[test]
fn test_linux_distro_uses_multiarch() {
assert!(X86LinuxDistro::Debian.uses_multiarch());
assert!(!X86LinuxDistro::RHEL.uses_multiarch());
assert!(!X86LinuxDistro::Arch.uses_multiarch());
}
#[test]
fn test_linux_distro_uses_lib64() {
assert!(X86LinuxDistro::RHEL.uses_lib64());
assert!(X86LinuxDistro::Fedora.uses_lib64());
assert!(X86LinuxDistro::Arch.uses_lib64());
assert!(X86LinuxDistro::Gentoo.uses_lib64());
}
#[test]
fn test_ubuntu_variant_detection() {
let variant = X86LinuxDistro::detect_ubuntu_variant();
let _ = variant;
}
#[test]
fn test_compiler_rt_available_components() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let crt = X86CompilerRT::new(&triple);
let components = crt.available_components();
assert!(components.contains_key("builtins"));
assert!(components.contains_key("asan"));
assert!(components.contains_key("ubsan_standalone"));
}
#[test]
fn test_compiler_rt_hwasan_support() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
assert!(!X86CompilerRT::supports_hwasan(&triple));
let triple = Triple::parse("aarch64-unknown-linux-gnu");
assert!(X86CompilerRT::supports_hwasan(&triple));
}
#[test]
fn test_compiler_rt_mte_support() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
assert!(!X86CompilerRT::supports_mte(&triple));
let triple = Triple::parse("aarch64-unknown-linux-gnu");
assert!(X86CompilerRT::supports_mte(&triple));
}
#[test]
fn test_compiler_rt_recommended_sanitizer_flags() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let crt = X86CompilerRT::new(&triple);
let flags = crt.recommended_sanitizer_flags();
let _ = flags;
}
#[test]
fn test_compiler_rt_version_detection() {
let version = X86CompilerRT::detect_compiler_rt_version();
let _ = version;
}
#[test]
fn test_compiler_rt_safestack() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let crt = X86CompilerRT::new(&triple);
let has_ss = crt.has_safestack();
let has_cfi = crt.has_cfi();
let has_scs = crt.has_shadow_call_stack();
let _ = (has_ss, has_cfi, has_scs);
}
#[test]
fn test_cross_library_crt_objects_glibc() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let objects = cl.crt_objects(false, false);
if cl.found && cl.crt_dir.is_some() {
assert!(objects.iter().any(|o| o.contains("crt1.o")));
}
}
#[test]
fn test_cross_library_crt_objects_pie() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let objects = cl.crt_objects(false, true);
if cl.found && cl.crt_dir.is_some() {
assert!(objects.iter().any(|o| o.contains("Scrt1.o")));
}
}
#[test]
fn test_cross_library_crt_end_objects() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let objects = cl.crt_end_objects();
if cl.found && cl.crt_dir.is_some() {
assert!(objects.iter().any(|o| o.contains("crtend.o")));
assert!(objects.iter().any(|o| o.contains("crtn.o")));
}
}
#[test]
fn test_cross_library_standard_headers() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let headers = cl.standard_headers_available();
if cfg!(target_os = "linux") && cl.found {
assert!(headers.contains(&"stdio.h".to_string()));
}
}
#[test]
fn test_cross_library_libc_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let path = cl.libc_path();
if cfg!(target_os = "linux") && cl.found {
assert!(path.is_some());
}
}
#[test]
fn test_cross_library_libm_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let path = cl.libm_path();
if cfg!(target_os = "linux") && cl.found {
assert!(path.is_some());
}
}
#[test]
fn test_cross_library_libpthread_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let path = cl.libpthread_path();
if cfg!(target_os = "linux") && cl.found {
assert!(path.is_some());
}
}
#[test]
fn test_cross_library_validate() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
let issues = cl.validate();
if !cl.found {
assert!(!issues.is_empty());
}
}
#[test]
fn test_cross_library_supports_shared() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
assert!(cl.supports_shared_libraries());
let triple = Triple::parse("x86_64-unknown-linux-musl");
let cl = X86CrossLibrary::new(&triple);
assert!(cl.supports_shared_libraries());
let triple = Triple::parse("i686-elf");
let cl = X86CrossLibrary::new(&triple);
assert!(!cl.supports_shared_libraries());
}
#[test]
fn test_cross_library_supports_pie() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let cl = X86CrossLibrary::new(&triple);
assert!(cl.supports_pie());
let triple = Triple::parse("x86_64-unknown-linux-musl");
let cl = X86CrossLibrary::new(&triple);
assert!(cl.supports_pie());
}
#[test]
fn test_triple_parser_amd_athlon_variants() {
for cpu in &[
"athlon",
"athlon64",
"athlon-fx",
"k8",
"opteron",
"k8-sse3",
"amdfam10",
"barcelona",
"btver1",
"btver2",
"bdver1",
"bdver2",
"bdver3",
"bdver4",
] {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
assert!(info.unwrap().has_sse2, "CPU {} should have SSE2", cpu);
}
}
#[test]
fn test_triple_parser_atom_variants() {
for cpu in &[
"silvermont",
"goldmont",
"goldmont-plus",
"tremont",
"gracemont",
"crestmont",
"skymont",
] {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
assert!(info.unwrap().has_sse2);
}
}
#[test]
fn test_triple_parser_x86_64_v_level_features() {
let f2 = X86TripleParser::default_features_for_cpu("x86-64-v2");
assert!(f2.contains(&"+sse4.2"));
assert!(f2.contains(&"+popcnt"));
let f3 = X86TripleParser::default_features_for_cpu("x86-64-v3");
assert!(f3.contains(&"+avx2"));
assert!(f3.contains(&"+bmi2"));
let f4 = X86TripleParser::default_features_for_cpu("x86-64-v4");
assert!(f4.contains(&"+avx512f"));
assert!(f4.contains(&"+avx512vl"));
}
#[test]
fn test_triple_parser_znver_variants_features() {
for (cpu, expected_avx2) in &[
("znver1", true),
("znver2", true),
("znver3", true),
("znver4", true),
("znver5", true),
] {
let features = X86TripleParser::default_features_for_cpu(cpu);
let has_avx2 = features.contains(&"+avx2");
assert_eq!(has_avx2, *expected_avx2, "{} avx2 mismatch", cpu);
}
}
#[test]
fn test_triple_parser_intel_core_features() {
let f_sb = X86TripleParser::default_features_for_cpu("sandybridge");
assert!(f_sb.contains(&"+avx"));
assert!(!f_sb.contains(&"+avx2"));
let f_hw = X86TripleParser::default_features_for_cpu("haswell");
assert!(f_hw.contains(&"+avx2"));
let f_skx = X86TripleParser::default_features_for_cpu("skylake-avx512");
assert!(f_skx.contains(&"+avx512f"));
}
#[test]
fn test_triple_parser_os_details_linux() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let os = X86TripleParser::parse_os_details(&triple);
assert_eq!(os.default_object_format, X86ObjectFormat::ELF);
assert!(os.supports_multiarch);
}
#[test]
fn test_triple_parser_os_details_darwin() {
let triple = Triple::parse("x86_64-apple-darwin");
let os = X86TripleParser::parse_os_details(&triple);
assert_eq!(os.default_object_format, X86ObjectFormat::MachO);
assert_eq!(os.default_libc, X86LibC::LibSystem);
}
#[test]
fn test_triple_parser_os_details_windows_msvc() {
let triple = Triple::parse("x86_64-pc-windows-msvc");
let os = X86TripleParser::parse_os_details(&triple);
assert_eq!(os.default_object_format, X86ObjectFormat::COFF);
}
#[test]
fn test_triple_parser_os_details_haiku() {
let triple = Triple::parse("x86_64-unknown-haiku");
let os = X86TripleParser::parse_os_details(&triple);
assert_eq!(os.default_object_format, X86ObjectFormat::ELF);
}
#[test]
fn test_triple_parser_os_details_wasi() {
let triple = Triple::parse("wasm32-unknown-wasi");
let os = X86TripleParser::parse_os_details(&triple);
assert_eq!(os.default_object_format, X86ObjectFormat::Wasm);
}
#[test]
fn test_resource_dir_sanitizer_library() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let path = rd.sanitizer_library_path("asan");
let _ = path;
}
#[test]
fn test_resource_dir_builtins_path() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let path = rd.builtins_path();
let _ = path;
}
#[test]
fn test_resource_dir_has_builtin_header() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let has_stddef = rd.has_builtin_header("stddef.h");
let _ = has_stddef;
}
#[test]
fn test_resource_dir_list_builtin_headers() {
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let rd = X86ResourceDir::new(&triple);
let headers = rd.list_builtin_headers();
let _ = headers.len();
}
#[test]
fn test_edge_case_unknown_architecture() {
let parser = X86TripleParser::new("unknown-unknown-unknown");
assert!(!parser.is_valid);
assert!(!parser.warnings.is_empty());
}
#[test]
fn test_edge_case_empty_triple() {
let manager = X86SysrootManager::new("");
assert_eq!(manager.triple.arch, Arch::Unknown);
}
#[test]
fn test_edge_case_all_cpu_names_valid_arch_family() {
let cpus = X86TripleParser::known_x86_cpus();
for cpu in &cpus {
if let Some(info) = X86TripleParser::parse_x86_arch(cpu) {
assert!(
info.family == X86ArchFamily::X86_32
|| info.family == X86ArchFamily::X86_64
|| info.family == X86ArchFamily::X32,
"CPU {} has unexpected family {:?}",
cpu,
info.family
);
}
}
}
#[test]
fn test_edge_case_cross_compilation_non_x86() {
let cc = X86CrossCompilation::new("sparc64-unknown-linux-gnu");
assert!(cc.is_cross); }
#[test]
fn test_edge_case_multiarch_mips() {
let triple = Triple::parse("mips-unknown-linux-gnu");
let ma = X86Multiarch::new(&triple);
assert_eq!(
ma.multiarch_dir,
None );
}
#[test]
fn test_edge_case_android_x86_32bit() {
let detector = X86SysrootDetector::new("i686-linux-android");
assert!(detector.search_android_ndk);
assert_eq!(detector.android_arch_name(), "x86");
}
#[test]
fn test_edge_case_mingw_detection_flag() {
let detector = X86SysrootDetector::new("x86_64-w64-mingw32");
assert!(detector.search_mingw);
}
#[test]
fn test_edge_case_tvos_triple() {
let detector = X86SysrootDetector::new("x86_64-apple-tvos");
assert!(detector.search_xcode);
}
#[test]
fn test_edge_case_watchos_triple() {
let detector = X86SysrootDetector::new("x86_64-apple-watchos");
assert!(detector.search_xcode);
}
#[test]
fn test_sysroot_cache_new() {
let cache = X86SysrootCache::new();
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
}
#[test]
fn test_sysroot_cache_get_sysroot() {
let mut cache = X86SysrootCache::new();
let sysroot = cache.get_sysroot("x86_64-unknown-linux-gnu");
assert!(!cache.is_empty());
let sysroot2 = cache.get_sysroot("x86_64-unknown-linux-gnu");
assert_eq!(sysroot.path, sysroot2.path);
}
#[test]
fn test_sysroot_cache_get_gcc() {
let mut cache = X86SysrootCache::new();
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let gcc = cache.get_gcc(&triple);
let _ = gcc.is_valid;
let gcc2 = cache.get_gcc(&triple);
assert_eq!(gcc.is_valid, gcc2.is_valid);
}
#[test]
fn test_sysroot_cache_get_libc() {
let mut cache = X86SysrootCache::new();
let triple = Triple::parse("x86_64-unknown-linux-gnu");
let libc = cache.get_libc(&triple);
assert_eq!(libc.libc_type, X86LibC::Glibc);
let libc2 = cache.get_libc(&triple);
assert_eq!(libc2.libc_type, X86LibC::Glibc);
}
#[test]
fn test_sysroot_cache_clear() {
let mut cache = X86SysrootCache::new();
cache.get_sysroot("x86_64-unknown-linux-gnu");
assert!(!cache.is_empty());
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn test_sysroot_cache_multiple_triples() {
let mut cache = X86SysrootCache::new();
cache.get_sysroot("x86_64-unknown-linux-gnu");
cache.get_sysroot("i386-unknown-linux-gnu");
cache.get_sysroot("aarch64-unknown-linux-gnu");
assert!(cache.len() >= 3);
}
#[test]
fn test_sysroot_cache_default() {
let cache = X86SysrootCache::default();
assert!(cache.is_empty());
}
#[test]
fn test_sysroot_manager_add_include() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.add_include(PathBuf::from("/custom/include"));
assert_eq!(manager.extra_includes.len(), 1);
manager.add_include(PathBuf::from("/custom/include"));
assert_eq!(manager.extra_includes.len(), 1);
}
#[test]
fn test_sysroot_manager_add_library() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.add_library(PathBuf::from("/custom/lib"));
assert_eq!(manager.extra_libraries.len(), 1);
manager.add_library(PathBuf::from("/custom/lib"));
assert_eq!(manager.extra_libraries.len(), 1);
}
#[test]
fn test_sysroot_manager_arch_directory_name() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let dir = manager.arch_directory_name();
assert!(dir.contains("x86_64"));
}
#[test]
fn test_sysroot_manager_recommended_libc() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
assert_eq!(manager.recommended_libc(), X86LibC::Glibc);
let manager = X86SysrootManager::new("x86_64-unknown-linux-musl");
assert_eq!(manager.recommended_libc(), X86LibC::Musl);
}
#[test]
fn test_sysroot_manager_is_ready() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let ready = manager.is_ready();
if cfg!(target_arch = "x86_64") && cfg!(target_os = "linux") {
assert!(ready);
}
}
#[test]
fn test_sysroot_manager_summary() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let summary = manager.summary();
assert!(summary.contains("x86_64"));
assert!(summary.contains("Target:"));
assert!(summary.contains("Arch:"));
}
#[test]
fn test_sysroot_manager_detect_cxx_libraries() {
let mut manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
manager.detect_gcc();
let libs = manager.detect_cxx_libraries();
let _ = libs;
}
#[test]
fn test_sysroot_manager_configure_lto() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_lto();
assert!(flags.iter().any(|f| f.contains("lto")));
}
#[test]
fn test_sysroot_manager_configure_sanitizers() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_sanitizers(&["address", "undefined"]);
assert!(flags.iter().any(|f| f.contains("sanitize")));
assert!(flags.iter().any(|f| f.contains("address")));
}
#[test]
fn test_sysroot_manager_configure_sanitizers_empty() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_sanitizers(&[]);
assert!(flags.is_empty());
}
#[test]
fn test_sysroot_manager_configure_coverage() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_coverage();
assert!(flags.iter().any(|f| f.contains("coverage")));
}
#[test]
fn test_sysroot_manager_configure_pgo() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_pgo("/path/to/profiles");
assert!(flags.iter().any(|f| f.contains("/path/to/profiles")));
}
#[test]
fn test_sysroot_manager_configure_bolt() {
let manager = X86SysrootManager::new("x86_64-unknown-linux-gnu");
let flags = manager.configure_bolt("/path/to/perf.data");
assert!(flags.iter().any(|f| f.contains("/path/to/perf.data")));
}
#[test]
fn test_triple_parser_xeon_phi() {
for cpu in &["knl", "knightslanding", "knm", "knightsmill"] {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
assert!(info.unwrap().has_sse2);
}
}
#[test]
fn test_triple_parser_via_cpus() {
let c3 = X86TripleParser::parse_x86_arch("c3").unwrap();
assert!(!c3.has_sse2);
assert_eq!(c3.family, X86ArchFamily::X86_32);
let c3_2 = X86TripleParser::parse_x86_arch("c3-2").unwrap();
assert!(c3_2.has_sse2);
let c7 = X86TripleParser::parse_x86_arch("c7").unwrap();
assert_eq!(c7.family, X86ArchFamily::X86_64);
}
#[test]
fn test_triple_parser_geode() {
let info = X86TripleParser::parse_x86_arch("geode").unwrap();
assert_eq!(info.family, X86ArchFamily::X86_32);
assert!(!info.has_sse2);
}
#[test]
fn test_triple_parser_winchip() {
for cpu in &["winchip-c6", "winchip2"] {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
assert!(!info.unwrap().has_sse2);
}
}
#[test]
fn test_triple_parser_prescott_nocona() {
let prescott = X86TripleParser::parse_x86_arch("prescott").unwrap();
assert!(prescott.has_sse2);
assert_eq!(prescott.family, X86ArchFamily::X86_32);
let nocona = X86TripleParser::parse_x86_arch("nocona").unwrap();
assert_eq!(nocona.family, X86ArchFamily::X86_64);
}
#[test]
fn test_triple_parser_intel_server_cpus() {
for cpu in &["cascadelake", "cooperlake", "emeraldrapids", "sierraforest"] {
let info = X86TripleParser::parse_x86_arch(cpu);
assert!(info.is_some(), "CPU {} should be recognized", cpu);
assert!(info.unwrap().has_sse2);
}
}
#[test]
fn test_triple_parser_extended_vendor_names() {
for vendor in &[
"pc",
"apple",
"ibm",
"amd",
"nvidia",
"sony",
"freescale",
"qualcomm",
"samsung",
"marvell",
"broadcom",
"allwinner",
"rockchip",
"mediatek",
"hisilicon",
"ampere",
"phytium",
] {
let result = X86TripleParser::parse_x86_vendor(vendor);
assert!(result.is_some(), "Vendor '{}' should be recognized", vendor);
}
}
}