From 9213da566984afff76d54854b3104ab33c6a84cd Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 29 Aug 2025 22:42:10 +0200 Subject: [PATCH] use rustc for making cfg from target triple --- Cargo.toml | 1 - prebindgen/Cargo.toml | 1 - prebindgen/src/api/source.rs | 13 +- prebindgen/src/api/utils/target_triple.rs | 205 ++++++++++++++++------ 4 files changed, 159 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5da31e..d020bf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ if_rust_version = "1.0" konst = "0.3.0" # old one for for rust 1.75 toml = "0.9.5" rand = "0.9.2" -target-lexicon = "0.13.2" [patch] diff --git a/prebindgen/Cargo.toml b/prebindgen/Cargo.toml index 3e4156e..5c28a05 100644 --- a/prebindgen/Cargo.toml +++ b/prebindgen/Cargo.toml @@ -25,7 +25,6 @@ if_rust_version = { workspace = true } konst = { workspace = true, features = ["cmp"] } itertools = { workspace = true } toml = { workspace = true } -target-lexicon = { workspace = true } [features] debug = [] diff --git a/prebindgen/src/api/source.rs b/prebindgen/src/api/source.rs index 427a7e1..ada8ffe 100644 --- a/prebindgen/src/api/source.rs +++ b/prebindgen/src/api/source.rs @@ -285,10 +285,15 @@ impl Source { if let Some(target) = &self.target_triple { let target_triple = TargetTriple::parse(target) .unwrap_or_else(|e| panic!("Failed to parse target triple '{}': {}", target, e)); - builder = builder - .enable_target_arch(target_triple.arch()) - .enable_target_os(target_triple.os()) - .enable_target_vendor(target_triple.vendor()); + if let Some(arch) = target_triple.arch() { + builder = builder.enable_target_arch(arch); + } + if let Some(vendor) = target_triple.vendor() { + builder = builder.enable_target_vendor(vendor); + } + if let Some(os) = target_triple.os() { + builder = builder.enable_target_os(os); + } if let Some(env) = target_triple.env() { builder = builder.enable_target_env(env); } diff --git a/prebindgen/src/api/utils/target_triple.rs b/prebindgen/src/api/utils/target_triple.rs index 28f7f0e..fdb0dc3 100644 --- a/prebindgen/src/api/utils/target_triple.rs +++ b/prebindgen/src/api/utils/target_triple.rs @@ -1,81 +1,117 @@ use proc_macro2::TokenStream; use quote::quote; use syn::LitStr; -use target_lexicon::{OperatingSystem, Triple}; /// TargetTriple is a small utility around `target_lexicon::Triple` with helpers /// to access parts and to convert into Rust cfg tokens. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct TargetTriple(Triple); +pub struct TargetTriple { + triple: String, + arch: Option, + vendor: Option, + os: Option, + env: Option, +} + +fn extract_cfg_condition(s: &str, name: &str) -> Option { + let prefix = format!("{}=\"", name); + if s.starts_with(&prefix) && s.ends_with('"') { + let s = &s[prefix.len()..s.len() - 1]; + if s.is_empty() { + None + } else { + Some(s.to_string()) + } + } else { + None + } +} impl TargetTriple { /// Parse from a string like "aarch64-apple-darwin". - pub fn parse(s: &str) -> Result { - s.parse::() - .map(TargetTriple) - .map_err(|e| format!("Failed to parse target triple '{s}': {e}")) - } + pub fn parse(s: impl Into) -> Result { + let triple = s.into(); + // run `rustc --print cfg --target ` and capture the output + let output = std::process::Command::new("rustc") + .args(["--print", "cfg", "--target", &triple]) + .output() + .map_err(|e| format!("Failed to run rustc: {e}"))?; + + if !output.status.success() { + return Err(format!("rustc failed with status: {}", output.status)); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let mut arch = None; + let mut vendor = None; + let mut os = None; + let mut env = None; + + for line in stdout.lines() { + if let Some(a) = extract_cfg_condition(line, "target_arch") { + arch = Some(a); + } else if let Some(v) = extract_cfg_condition(line, "target_vendor") { + vendor = Some(v); + } else if let Some(o) = extract_cfg_condition(line, "target_os") { + os = Some(o); + } else if let Some(e) = extract_cfg_condition(line, "target_env") { + env = Some(e); + } + } - /// Create from an existing target_lexicon Triple. - pub fn from_triple(triple: Triple) -> Self { - Self(triple) + Ok(Self { + triple, + arch, + vendor, + os, + env, + }) } /// Get the architecture as a canonical string used by Rust cfg target_arch. - pub fn arch(&self) -> String { - self.0.architecture.to_string() + pub fn arch(&self) -> Option<&str> { + self.arch.as_deref() } /// Get the vendor as string used by Rust cfg target_vendor. - pub fn vendor(&self) -> String { - self.0.vendor.to_string() + pub fn vendor(&self) -> Option<&str> { + self.vendor.as_deref() } /// Get the operating system as string used by Rust cfg target_os. - /// Maps Darwin to "macos" to match Rust cfg semantics. - pub fn os(&self) -> String { - match self.0.operating_system { - OperatingSystem::Darwin(_) => "macos".to_string(), - ref os => os.to_string(), - } + pub fn os(&self) -> Option<&str> { + self.os.as_deref() } /// Get the environment as string used by Rust cfg target_env (may be "unknown"). - pub fn env(&self) -> Option { - if self.0.environment == target_lexicon::Environment::Unknown { - None - } else { - Some(self.0.environment.to_string()) - } - } - - /// Access the inner Triple. - pub fn as_triple(&self) -> &Triple { - &self.0 - } - - /// Decompose into the inner Triple. - pub fn into_triple(self) -> Triple { - self.0 + pub fn env(&self) -> Option<&str> { + self.env.as_deref() } /// Build a cfg expression TokenStream like: /// all(target_arch = "aarch64", target_vendor = "apple", target_os = "macos", target_env = "gnu") /// Omits target_env when unknown/empty. pub fn to_cfg_tokens(&self) -> TokenStream { - let arch = LitStr::new(&self.arch(), proc_macro2::Span::call_site()); - let vendor = LitStr::new(&self.vendor(), proc_macro2::Span::call_site()); - let os = LitStr::new(&self.os(), proc_macro2::Span::call_site()); - let env = self.env(); let mut parts: Vec = Vec::with_capacity(4); - parts.push(quote! { target_arch = #arch }); - parts.push(quote! { target_vendor = #vendor }); - parts.push(quote! { target_os = #os }); - if let Some(env) = env { - let env_lit = LitStr::new(&env, proc_macro2::Span::call_site()); - parts.push(quote! { target_env = #env_lit }); + if let Some(ref arch) = self.arch { + let arch = LitStr::new(arch, proc_macro2::Span::call_site()); + parts.push(quote! { target_arch = #arch }); } - if parts.len() == 1 { + if let Some(ref vendor) = self.vendor { + let vendor = LitStr::new(vendor, proc_macro2::Span::call_site()); + parts.push(quote! { target_vendor = #vendor }); + } + if let Some(ref os) = self.os { + let os = LitStr::new(os, proc_macro2::Span::call_site()); + parts.push(quote! { target_os = #os }); + } + if let Some(ref env) = self.env { + let env = LitStr::new(env, proc_macro2::Span::call_site()); + parts.push(quote! { target_env = #env }); + } + if parts.is_empty() { + quote! { true } + } else if parts.len() == 1 { parts.remove(0) } else { quote! { all( #(#parts),* ) } @@ -103,18 +139,77 @@ mod tests { use super::*; #[test] - fn maps_darwin_to_macos() { + fn aarch64_apple_darwin() { let tt = TargetTriple::parse("aarch64-apple-darwin").unwrap(); - assert_eq!(tt.os(), "macos"); + assert!( + tt.arch() == Some("aarch64"), + "Unexpected architecture found {:?}", + tt.arch() + ); + assert!( + tt.vendor() == Some("apple"), + "Unexpected vendor found {:?}", + tt.vendor() + ); + assert!( + tt.os() == Some("macos"), + "Unexpected OS found {:?}", + tt.os() + ); + assert!( + tt.env().is_none(), + "Unexpected environment found {:?}", + tt.env() + ); } #[test] - fn builds_cfg_without_unknown_env() { + fn x86_64_unknown_linux() { let tt = TargetTriple::parse("x86_64-unknown-linux-gnu").unwrap(); - let ts = tt.to_cfg_tokens().to_string(); - assert!(ts.contains("target_arch = \"x86_64\"")); - assert!(ts.contains("target_vendor = \"unknown\"")); - assert!(ts.contains("target_os = \"linux\"")); - assert!(ts.contains("target_env = \"gnu\"")); + assert!( + tt.arch() == Some("x86_64"), + "Unexpected architecture found {:?}", + tt.arch() + ); + assert!( + tt.vendor() == Some("unknown"), + "Unexpected vendor found {:?}", + tt.vendor() + ); + assert!( + tt.os() == Some("linux"), + "Unexpected OS found {:?}", + tt.os() + ); + assert!( + tt.env() == Some("gnu"), + "Unexpected environment found {:?}", + tt.env() + ); + } + + #[test] + fn armv7_unknown_linux_gnueabihf() { + let tt = TargetTriple::parse("armv7-unknown-linux-gnueabihf").unwrap(); + assert!( + tt.arch() == Some("arm"), + "Unexpected architecture found {:?}", + tt.arch() + ); + assert!( + tt.vendor() == Some("unknown"), + "Unexpected vendor found {:?}", + tt.vendor() + ); + assert!( + tt.os() == Some("linux"), + "Unexpected OS found {:?}", + tt.os() + ); + assert!( + tt.env() == Some("gnu"), + "Unexpected environment found {:?}", + tt.env() + ); } }