From 108c1b303488c421bf4b3151a5a16716e4e38319 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 22 Aug 2025 17:32:04 +0200 Subject: [PATCH 1/8] cfg filtering added --- prebindgen/src/api/batching/feature_filter.rs | 78 ++++++++++- prebindgen/src/codegen/cfg_expr.rs | 131 +++++++++++++++++- prebindgen/src/codegen/process_features.rs | 65 ++++++++- 3 files changed, 270 insertions(+), 4 deletions(-) diff --git a/prebindgen/src/api/batching/feature_filter.rs b/prebindgen/src/api/batching/feature_filter.rs index 386c68f..3c22c5c 100644 --- a/prebindgen/src/api/batching/feature_filter.rs +++ b/prebindgen/src/api/batching/feature_filter.rs @@ -32,6 +32,11 @@ pub struct Builder { pub(crate) disable_unknown_features: bool, // Source crate features constant name and features list in format "crate/f1 crate/f2" pub(crate) features_assert: Option<(String, String)>, + // Selected target configuration parameters. When Some, only the selected value is enabled and others are disabled. + pub(crate) enabled_target_arch: Option, + pub(crate) enabled_target_vendor: Option, + pub(crate) enabled_target_os: Option, + pub(crate) enabled_target_env: Option, } impl Builder { @@ -49,6 +54,10 @@ impl Builder { feature_mappings: HashMap::new(), disable_unknown_features: false, features_assert: None, + enabled_target_arch: None, + enabled_target_vendor: None, + enabled_target_os: None, + enabled_target_env: None, } } @@ -95,6 +104,65 @@ impl Builder { self.enabled_features.insert(feature.into()); self } + + /// Enable a specific target architecture. All other architectures are treated as disabled. + /// + /// Only one architecture can be enabled. Calling this again overwrites the previous choice. + /// + /// # Example + /// + /// ``` + /// let builder = prebindgen::batching::feature_filter::Builder::new() + /// .enable_target_arch("x86_64"); + /// ``` + #[roxygen] + pub fn enable_target_arch>( + mut self, + /// The target architecture value to enable (e.g., "x86_64", "aarch64") + arch: S, + ) -> Self { + self.enabled_target_arch = Some(arch.into()); + self + } + + /// Enable a specific target vendor. All other vendors are treated as disabled. + /// + /// Only one vendor can be enabled. Calling this again overwrites the previous choice. + #[roxygen] + pub fn enable_target_vendor>( + mut self, + /// The target vendor value to enable (e.g., "apple", "pc") + vendor: S, + ) -> Self { + self.enabled_target_vendor = Some(vendor.into()); + self + } + + /// Enable a specific target operating system. All other OS values are treated as disabled. + /// + /// Only one OS can be enabled. Calling this again overwrites the previous choice. + #[roxygen] + pub fn enable_target_os>( + mut self, + /// The target operating system to enable (e.g., "macos", "linux", "windows") + os: S, + ) -> Self { + self.enabled_target_os = Some(os.into()); + self + } + + /// Enable a specific target environment. All other environments are treated as disabled. + /// + /// Only one environment can be enabled. Calling this again overwrites the previous choice. + #[roxygen] + pub fn enable_target_env>( + mut self, + /// The target environment to enable (e.g., "gnu", "musl", "msvc") + env: S, + ) -> Self { + self.enabled_target_env = Some(env.into()); + self + } /// Map a feature name to a different name in the generated code /// /// When processing code with `#[cfg(feature="...")]` attributes, features @@ -189,7 +257,11 @@ impl Builder { || self.disable_unknown_features || !self.disabled_features.is_empty() || !self.enabled_features.is_empty() - || !self.feature_mappings.is_empty(); + || !self.feature_mappings.is_empty() + || self.enabled_target_arch.is_some() + || self.enabled_target_vendor.is_some() + || self.enabled_target_os.is_some() + || self.enabled_target_env.is_some(); // Optionally create a prelude assertion comparing FEATURES const path with expected features string let mut prelude_item: Option<(syn::Item, SourceLocation)> = None; @@ -312,6 +384,10 @@ impl FeatureFilter { &self.builder.enabled_features, &self.builder.feature_mappings, self.builder.disable_unknown_features, + &self.builder.enabled_target_arch, + &self.builder.enabled_target_vendor, + &self.builder.enabled_target_os, + &self.builder.enabled_target_env, &source_location, ) { return Some((item, source_location)); diff --git a/prebindgen/src/codegen/cfg_expr.rs b/prebindgen/src/codegen/cfg_expr.rs index 860259e..f1970a0 100644 --- a/prebindgen/src/codegen/cfg_expr.rs +++ b/prebindgen/src/codegen/cfg_expr.rs @@ -19,6 +19,12 @@ pub enum CfgExpr { All(Vec), /// Logical OR: `any(expr1, expr2, ...)` Any(Vec), + /// Target vendor check: `target_vendor = "vendor"` + TargetVendor(String), + /// Target OS check: `target_os = "os"` + TargetOs(String), + /// Target environment check: `target_env = "env"` + TargetEnv(String), /// Logical NOT: `not(expr)` Not(Box), /// Any other cfg expression we don't specifically handle @@ -63,6 +69,20 @@ impl CfgExpr { if let Some(arch) = extract_target_arch(input) { return Ok(CfgExpr::TargetArch(arch)); } + // Handle target_vendor expressions + if let Some(vendor) = extract_target_vendor(input) { + return Ok(CfgExpr::TargetVendor(vendor)); + } + + // Handle target_os expressions + if let Some(os) = extract_target_os(input) { + return Ok(CfgExpr::TargetOs(os)); + } + + // Handle target_env expressions + if let Some(env) = extract_target_env(input) { + return Ok(CfgExpr::TargetEnv(env)); + } // If we can't parse it, store it as "Other" Ok(CfgExpr::Other(input.to_string())) @@ -79,6 +99,10 @@ impl CfgExpr { disabled_features: &HashSet, feature_mappings: &std::collections::HashMap, disable_unknown_features: bool, + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, source_location: &SourceLocation, ) -> Option { match self { @@ -100,7 +124,20 @@ impl CfgExpr { panic!("unmapped feature: {name} (at {source_location})"); } } - CfgExpr::TargetArch(_) => Some(self.clone()), + CfgExpr::TargetVendor(val) => { + if let Some(sel) = enabled_target_vendor.as_ref() { + if val == sel { None } else { Some(CfgExpr::False) } + } else { + Some(self.clone()) + } + } + CfgExpr::TargetArch(val) => { + if let Some(sel) = enabled_target_arch.as_ref() { + if val == sel { None } else { Some(CfgExpr::False) } + } else { + Some(self.clone()) + } + } CfgExpr::All(exprs) => { let mut processed_exprs = Vec::new(); for expr in exprs { @@ -109,6 +146,10 @@ impl CfgExpr { disabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { Some(CfgExpr::False) => { @@ -136,6 +177,10 @@ impl CfgExpr { disabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { Some(CfgExpr::False) => { @@ -164,6 +209,10 @@ impl CfgExpr { disabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { Some(CfgExpr::False) => None, // not(false) = true @@ -171,6 +220,20 @@ impl CfgExpr { None => Some(CfgExpr::False), // not(true) = false } } + CfgExpr::TargetOs(val) => { + if let Some(sel) = enabled_target_os.as_ref() { + if val == sel { None } else { Some(CfgExpr::False) } + } else { + Some(self.clone()) + } + } + CfgExpr::TargetEnv(val) => { + if let Some(sel) = enabled_target_env.as_ref() { + if val == sel { None } else { Some(CfgExpr::False) } + } else { + Some(self.clone()) + } + } CfgExpr::Other(_) => Some(self.clone()), CfgExpr::False => Some(CfgExpr::False), } @@ -185,6 +248,15 @@ impl CfgExpr { CfgExpr::TargetArch(arch) => { quote::quote! { target_arch = #arch } } + CfgExpr::TargetVendor(vendor) => { + quote::quote! { target_vendor = #vendor } + } + CfgExpr::TargetOs(os) => { + quote::quote! { target_os = #os } + } + CfgExpr::TargetEnv(env) => { + quote::quote! { target_env = #env } + } CfgExpr::All(exprs) => { let tokens: Vec<_> = exprs.iter().map(|e| e.to_tokens()).collect(); quote::quote! { all(#(#tokens),*) } @@ -235,6 +307,27 @@ fn extract_target_arch(input: &str) -> Option { .map(|captures| captures[1].to_string()) } +/// Extract target vendor from expressions like `target_vendor = "apple"` +fn extract_target_vendor(input: &str) -> Option { + use regex::Regex; + let re = Regex::new(r#"target_vendor\s*=\s*\"([^\"]+)\""#).unwrap(); + re.captures(input).map(|c| c[1].to_string()) +} + +/// Extract target OS from expressions like `target_os = "macos"` +fn extract_target_os(input: &str) -> Option { + use regex::Regex; + let re = Regex::new(r#"target_os\s*=\s*\"([^\"]+)\""#).unwrap(); + re.captures(input).map(|c| c[1].to_string()) +} + +/// Extract target env from expressions like `target_env = "gnu"` +fn extract_target_env(input: &str) -> Option { + use regex::Regex; + let re = Regex::new(r#"target_env\s*=\s*\"([^\"]+)\""#).unwrap(); + re.captures(input).map(|c| c[1].to_string()) +} + /// Strip a function call wrapper, returning the inner content /// For example: `not(feature = "test")` -> `feature = "test"` fn strip_function_call(input: &str, function_name: &str) -> Option { @@ -387,6 +480,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), None @@ -400,6 +497,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), Some(CfgExpr::False) @@ -413,6 +514,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), Some(CfgExpr::Feature("new_feature".to_string())) @@ -429,6 +534,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), None @@ -445,6 +554,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), Some(CfgExpr::False) @@ -458,6 +571,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), None @@ -471,6 +588,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default() ), Some(CfgExpr::False) @@ -493,6 +614,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default(), ); } @@ -518,6 +643,10 @@ mod tests { &disabled_features, &feature_mappings, false, + &None, + &None, + &None, + &None, &SourceLocation::default(), ); } diff --git a/prebindgen/src/codegen/process_features.rs b/prebindgen/src/codegen/process_features.rs index f7401a4..c76e851 100644 --- a/prebindgen/src/codegen/process_features.rs +++ b/prebindgen/src/codegen/process_features.rs @@ -29,6 +29,11 @@ pub(crate) fn process_item_features( feature_mappings: &HashMap, /// If true, unknown features are treated as disabled (skipped) instead of causing an error disable_unknown_features: bool, + /// Selected target parameters to evaluate target_* cfgs + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, /// Source location information for error reporting source_location: &SourceLocation, ) -> bool { @@ -42,6 +47,10 @@ pub(crate) fn process_item_features( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ); &mut s.attrs @@ -54,6 +63,10 @@ pub(crate) fn process_item_features( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ); &mut e.attrs @@ -66,6 +79,10 @@ pub(crate) fn process_item_features( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ); &mut u.attrs @@ -85,8 +102,12 @@ pub(crate) fn process_item_features( attrs, disabled_features, enabled_features, - feature_mappings, - disable_unknown_features, + feature_mappings, + disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) } @@ -98,6 +119,10 @@ fn process_struct_fields( enabled_features: &HashSet, feature_mappings: &HashMap, disable_unknown_features: bool, + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, source_location: &SourceLocation, ) { match fields { @@ -112,6 +137,10 @@ fn process_struct_fields( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { new_fields.push(field); @@ -130,6 +159,10 @@ fn process_struct_fields( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { new_fields.push(field); @@ -150,6 +183,10 @@ fn process_enum_variants( enabled_features: &HashSet, feature_mappings: &HashMap, disable_unknown_features: bool, + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, source_location: &SourceLocation, ) { // Manual filtering since Punctuated doesn't have retain_mut @@ -163,6 +200,10 @@ fn process_enum_variants( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ); @@ -174,6 +215,10 @@ fn process_enum_variants( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ); new_variants.push(variant); @@ -189,6 +234,10 @@ fn process_union_fields( enabled_features: &HashSet, feature_mappings: &HashMap, disable_unknown_features: bool, + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, source_location: &SourceLocation, ) { // Manual filtering since Punctuated doesn't have retain_mut @@ -201,6 +250,10 @@ fn process_union_fields( enabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { new_fields.push(field); @@ -216,6 +269,10 @@ fn process_attributes( enabled_features: &HashSet, feature_mappings: &HashMap, disable_unknown_features: bool, + enabled_target_arch: &Option, + enabled_target_vendor: &Option, + enabled_target_os: &Option, + enabled_target_env: &Option, source_location: &SourceLocation, ) -> bool { let mut keep_item = true; @@ -235,6 +292,10 @@ fn process_attributes( disabled_features, feature_mappings, disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) { Some(processed_expr) => { From 73d8d298e92341a38a3738994ca6a685ae649a49 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 22 Aug 2025 17:36:08 +0200 Subject: [PATCH 2/8] CfgExpr reorg, tests --- prebindgen/src/codegen/cfg_expr.rs | 143 ++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/prebindgen/src/codegen/cfg_expr.rs b/prebindgen/src/codegen/cfg_expr.rs index f1970a0..160a8bf 100644 --- a/prebindgen/src/codegen/cfg_expr.rs +++ b/prebindgen/src/codegen/cfg_expr.rs @@ -11,22 +11,27 @@ use crate::SourceLocation; /// Represents a cfg expression that can be evaluated against a set of enabled/disabled features #[derive(Debug, Clone, PartialEq)] pub enum CfgExpr { + // Simple predicates first /// A simple feature check: `feature = "name"` Feature(String), /// Target architecture check: `target_arch = "arch"` TargetArch(String), - /// Logical AND: `all(expr1, expr2, ...)` - All(Vec), - /// Logical OR: `any(expr1, expr2, ...)` - Any(Vec), /// Target vendor check: `target_vendor = "vendor"` TargetVendor(String), /// Target OS check: `target_os = "os"` TargetOs(String), /// Target environment check: `target_env = "env"` TargetEnv(String), + + // Logical operators next /// Logical NOT: `not(expr)` Not(Box), + /// Logical AND: `all(expr1, expr2, ...)` + All(Vec), + /// Logical OR: `any(expr1, expr2, ...)` + Any(Vec), + + // Fallbacks /// Any other cfg expression we don't specifically handle Other(String), /// Explicit false value (for feature processing) @@ -422,6 +427,136 @@ mod tests { assert_eq!(expr, CfgExpr::TargetArch("x86_64".to_string())); } + #[test] + fn test_target_vendor_os_env_parse() { + let vendor = CfgExpr::parse_from_string(r#"target_vendor = "apple""#).unwrap(); + assert_eq!(vendor, CfgExpr::TargetVendor("apple".to_string())); + + let os = CfgExpr::parse_from_string(r#"target_os = "macos""#).unwrap(); + assert_eq!(os, CfgExpr::TargetOs("macos".to_string())); + + let env = CfgExpr::parse_from_string(r#"target_env = "gnu""#).unwrap(); + assert_eq!(env, CfgExpr::TargetEnv("gnu".to_string())); + } + + #[test] + fn test_target_filters_processing() { + use std::collections::HashMap; + let enabled_features = HashSet::new(); + let disabled_features = HashSet::new(); + let feature_mappings: HashMap = HashMap::new(); + let src = SourceLocation::default(); + + // With no selection, keep predicates as-is + let expr = CfgExpr::TargetOs("macos".into()); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &src, + ), + Some(CfgExpr::TargetOs("macos".into())) + ); + + // Select OS = macos: becomes true (None) + assert_eq!( + CfgExpr::TargetOs("macos".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &Some("macos".into()), + &None, + &src, + ), + None + ); + + // Non-matching becomes False + assert_eq!( + CfgExpr::TargetOs("linux".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &Some("macos".into()), + &None, + &src, + ), + Some(CfgExpr::False) + ); + + // Arch selection + assert_eq!( + CfgExpr::TargetArch("x86_64".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &Some("x86_64".into()), + &None, + &None, + &None, + &src, + ), + None + ); + assert_eq!( + CfgExpr::TargetArch("aarch64".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &Some("x86_64".into()), + &None, + &None, + &None, + &src, + ), + Some(CfgExpr::False) + ); + + // Vendor and Env selection + assert_eq!( + CfgExpr::TargetVendor("apple".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &Some("apple".into()), + &None, + &None, + &src, + ), + None + ); + assert_eq!( + CfgExpr::TargetEnv("gnu".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &Some("gnu".into()), + &src, + ), + None + ); + } + #[test] fn test_not_expression() { let expr = CfgExpr::parse_from_string(r#"not(feature = "test")"#).unwrap(); From be2582cd5d6020c95099cc15737d74bf7d0ba8a9 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 22 Aug 2025 17:42:04 +0200 Subject: [PATCH 3/8] cfgexpr tests in separate module --- prebindgen/src/codegen/cfg_expr.rs | 375 +----------------- .../src/codegen/cfg_expr/cfg_expr_tests.rs | 372 +++++++++++++++++ 2 files changed, 373 insertions(+), 374 deletions(-) create mode 100644 prebindgen/src/codegen/cfg_expr/cfg_expr_tests.rs diff --git a/prebindgen/src/codegen/cfg_expr.rs b/prebindgen/src/codegen/cfg_expr.rs index 160a8bf..62c3453 100644 --- a/prebindgen/src/codegen/cfg_expr.rs +++ b/prebindgen/src/codegen/cfg_expr.rs @@ -412,377 +412,4 @@ fn parse_comma_separated(input: &str) -> Result, String> { } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_simple_feature() { - let expr = CfgExpr::parse_from_string(r#"feature = "test""#).unwrap(); - assert_eq!(expr, CfgExpr::Feature("test".to_string())); - } - - #[test] - fn test_target_arch() { - let expr = CfgExpr::parse_from_string(r#"target_arch = "x86_64""#).unwrap(); - assert_eq!(expr, CfgExpr::TargetArch("x86_64".to_string())); - } - - #[test] - fn test_target_vendor_os_env_parse() { - let vendor = CfgExpr::parse_from_string(r#"target_vendor = "apple""#).unwrap(); - assert_eq!(vendor, CfgExpr::TargetVendor("apple".to_string())); - - let os = CfgExpr::parse_from_string(r#"target_os = "macos""#).unwrap(); - assert_eq!(os, CfgExpr::TargetOs("macos".to_string())); - - let env = CfgExpr::parse_from_string(r#"target_env = "gnu""#).unwrap(); - assert_eq!(env, CfgExpr::TargetEnv("gnu".to_string())); - } - - #[test] - fn test_target_filters_processing() { - use std::collections::HashMap; - let enabled_features = HashSet::new(); - let disabled_features = HashSet::new(); - let feature_mappings: HashMap = HashMap::new(); - let src = SourceLocation::default(); - - // With no selection, keep predicates as-is - let expr = CfgExpr::TargetOs("macos".into()); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &src, - ), - Some(CfgExpr::TargetOs("macos".into())) - ); - - // Select OS = macos: becomes true (None) - assert_eq!( - CfgExpr::TargetOs("macos".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &Some("macos".into()), - &None, - &src, - ), - None - ); - - // Non-matching becomes False - assert_eq!( - CfgExpr::TargetOs("linux".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &Some("macos".into()), - &None, - &src, - ), - Some(CfgExpr::False) - ); - - // Arch selection - assert_eq!( - CfgExpr::TargetArch("x86_64".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &Some("x86_64".into()), - &None, - &None, - &None, - &src, - ), - None - ); - assert_eq!( - CfgExpr::TargetArch("aarch64".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &Some("x86_64".into()), - &None, - &None, - &None, - &src, - ), - Some(CfgExpr::False) - ); - - // Vendor and Env selection - assert_eq!( - CfgExpr::TargetVendor("apple".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &Some("apple".into()), - &None, - &None, - &src, - ), - None - ); - assert_eq!( - CfgExpr::TargetEnv("gnu".into()).process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &Some("gnu".into()), - &src, - ), - None - ); - } - - #[test] - fn test_not_expression() { - let expr = CfgExpr::parse_from_string(r#"not(feature = "test")"#).unwrap(); - match expr { - CfgExpr::Not(inner) => { - assert_eq!(*inner, CfgExpr::Feature("test".to_string())); - } - _ => panic!("Expected Not expression, got: {expr:?}"), - } - } - - #[test] - fn test_any_expression() { - let expr = CfgExpr::parse_from_string(r#"any(feature = "a", feature = "b")"#).unwrap(); - match expr { - CfgExpr::Any(exprs) => { - assert_eq!(exprs.len(), 2); - assert_eq!(exprs[0], CfgExpr::Feature("a".to_string())); - assert_eq!(exprs[1], CfgExpr::Feature("b".to_string())); - } - _ => panic!("Expected Any expression"), - } - } - - #[test] - fn test_all_expression() { - let expr = CfgExpr::parse_from_string(r#"all(feature = "a", feature = "b")"#).unwrap(); - match expr { - CfgExpr::All(exprs) => { - assert_eq!(exprs.len(), 2); - assert_eq!(exprs[0], CfgExpr::Feature("a".to_string())); - assert_eq!(exprs[1], CfgExpr::Feature("b".to_string())); - } - _ => panic!("Expected All expression"), - } - } - - #[test] - fn test_strict_feature_processing() { - use std::collections::HashMap; - - let mut enabled_features = HashSet::new(); - enabled_features.insert("feature1".to_string()); - - let mut disabled_features = HashSet::new(); - disabled_features.insert("feature2".to_string()); - - let mut feature_mappings = HashMap::new(); - feature_mappings.insert("old_feature".to_string(), "new_feature".to_string()); - - // Test enabled feature - should be removed (None = always true) - let expr = CfgExpr::Feature("feature1".to_string()); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - None - ); - - // Test disabled feature - should become False - let expr = CfgExpr::Feature("feature2".to_string()); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - Some(CfgExpr::False) - ); - - // Test mapped feature - should be renamed - let expr = CfgExpr::Feature("old_feature".to_string()); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - Some(CfgExpr::Feature("new_feature".to_string())) - ); - - // Test any() with enabled feature - should be removed (None = always true) - let expr = CfgExpr::Any(vec![ - CfgExpr::Feature("feature1".to_string()), - CfgExpr::Feature("feature2".to_string()), - ]); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - None - ); - - // Test all() with disabled feature - should become False - let expr = CfgExpr::All(vec![ - CfgExpr::Feature("feature1".to_string()), - CfgExpr::Feature("feature2".to_string()), - ]); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - Some(CfgExpr::False) - ); - - // Test not() with disabled feature - should be removed (not(false) = true) - let expr = CfgExpr::Not(Box::new(CfgExpr::Feature("feature2".to_string()))); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - None - ); - - // Test not() with enabled feature - should become False (not(true) = false) - let expr = CfgExpr::Not(Box::new(CfgExpr::Feature("feature1".to_string()))); - assert_eq!( - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default() - ), - Some(CfgExpr::False) - ); - } - - #[test] - #[should_panic(expected = "unmapped feature: unknown")] - fn test_strict_feature_processing_unmapped_panic() { - use std::collections::HashMap; - - let enabled_features = HashSet::new(); - let disabled_features = HashSet::new(); - let feature_mappings = HashMap::new(); - - // Test unmapped feature - should panic - let expr = CfgExpr::Feature("unknown".to_string()); - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default(), - ); - } - - #[test] - #[should_panic(expected = "unmapped feature: unknown")] - fn test_strict_feature_processing_unmapped_in_any_panic() { - use std::collections::HashMap; - - let mut enabled_features = HashSet::new(); - enabled_features.insert("feature1".to_string()); - - let disabled_features = HashSet::new(); - let feature_mappings = HashMap::new(); - - // Test unmapped feature in any() - should panic - let expr = CfgExpr::Any(vec![ - CfgExpr::Feature("feature1".to_string()), - CfgExpr::Feature("unknown".to_string()), - ]); - expr.process_features_strict( - &enabled_features, - &disabled_features, - &feature_mappings, - false, - &None, - &None, - &None, - &None, - &SourceLocation::default(), - ); - } -} +mod cfg_expr_tests; diff --git a/prebindgen/src/codegen/cfg_expr/cfg_expr_tests.rs b/prebindgen/src/codegen/cfg_expr/cfg_expr_tests.rs new file mode 100644 index 0000000..c7dfcc0 --- /dev/null +++ b/prebindgen/src/codegen/cfg_expr/cfg_expr_tests.rs @@ -0,0 +1,372 @@ +use super::*; + +#[test] +fn test_simple_feature() { + let expr = CfgExpr::parse_from_string(r#"feature = "test""#).unwrap(); + assert_eq!(expr, CfgExpr::Feature("test".to_string())); +} + +#[test] +fn test_target_arch() { + let expr = CfgExpr::parse_from_string(r#"target_arch = "x86_64""#).unwrap(); + assert_eq!(expr, CfgExpr::TargetArch("x86_64".to_string())); +} + +#[test] +fn test_target_vendor_os_env_parse() { + let vendor = CfgExpr::parse_from_string(r#"target_vendor = "apple""#).unwrap(); + assert_eq!(vendor, CfgExpr::TargetVendor("apple".to_string())); + + let os = CfgExpr::parse_from_string(r#"target_os = "macos""#).unwrap(); + assert_eq!(os, CfgExpr::TargetOs("macos".to_string())); + + let env = CfgExpr::parse_from_string(r#"target_env = "gnu""#).unwrap(); + assert_eq!(env, CfgExpr::TargetEnv("gnu".to_string())); +} + +#[test] +fn test_target_filters_processing() { + use std::collections::HashMap; + let enabled_features = HashSet::new(); + let disabled_features = HashSet::new(); + let feature_mappings: HashMap = HashMap::new(); + let src = SourceLocation::default(); + + // With no selection, keep predicates as-is + let expr = CfgExpr::TargetOs("macos".into()); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &src, + ), + Some(CfgExpr::TargetOs("macos".into())) + ); + + // Select OS = macos: becomes true (None) + assert_eq!( + CfgExpr::TargetOs("macos".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &Some("macos".into()), + &None, + &src, + ), + None + ); + + // Non-matching becomes False + assert_eq!( + CfgExpr::TargetOs("linux".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &Some("macos".into()), + &None, + &src, + ), + Some(CfgExpr::False) + ); + + // Arch selection + assert_eq!( + CfgExpr::TargetArch("x86_64".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &Some("x86_64".into()), + &None, + &None, + &None, + &src, + ), + None + ); + assert_eq!( + CfgExpr::TargetArch("aarch64".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &Some("x86_64".into()), + &None, + &None, + &None, + &src, + ), + Some(CfgExpr::False) + ); + + // Vendor and Env selection + assert_eq!( + CfgExpr::TargetVendor("apple".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &Some("apple".into()), + &None, + &None, + &src, + ), + None + ); + assert_eq!( + CfgExpr::TargetEnv("gnu".into()).process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &Some("gnu".into()), + &src, + ), + None + ); +} + +#[test] +fn test_not_expression() { + let expr = CfgExpr::parse_from_string(r#"not(feature = "test")"#).unwrap(); + match expr { + CfgExpr::Not(inner) => { + assert_eq!(*inner, CfgExpr::Feature("test".to_string())); + } + _ => panic!("Expected Not expression, got: {expr:?}"), + } +} + +#[test] +fn test_any_expression() { + let expr = CfgExpr::parse_from_string(r#"any(feature = "a", feature = "b")"#).unwrap(); + match expr { + CfgExpr::Any(exprs) => { + assert_eq!(exprs.len(), 2); + assert_eq!(exprs[0], CfgExpr::Feature("a".to_string())); + assert_eq!(exprs[1], CfgExpr::Feature("b".to_string())); + } + _ => panic!("Expected Any expression"), + } +} + +#[test] +fn test_all_expression() { + let expr = CfgExpr::parse_from_string(r#"all(feature = "a", feature = "b")"#).unwrap(); + match expr { + CfgExpr::All(exprs) => { + assert_eq!(exprs.len(), 2); + assert_eq!(exprs[0], CfgExpr::Feature("a".to_string())); + assert_eq!(exprs[1], CfgExpr::Feature("b".to_string())); + } + _ => panic!("Expected All expression"), + } +} + +#[test] +fn test_strict_feature_processing() { + use std::collections::HashMap; + + let mut enabled_features = HashSet::new(); + enabled_features.insert("feature1".to_string()); + + let mut disabled_features = HashSet::new(); + disabled_features.insert("feature2".to_string()); + + let mut feature_mappings = HashMap::new(); + feature_mappings.insert("old_feature".to_string(), "new_feature".to_string()); + + // Test enabled feature - should be removed (None = always true) + let expr = CfgExpr::Feature("feature1".to_string()); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + None + ); + + // Test disabled feature - should become False + let expr = CfgExpr::Feature("feature2".to_string()); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + Some(CfgExpr::False) + ); + + // Test mapped feature - should be renamed + let expr = CfgExpr::Feature("old_feature".to_string()); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + Some(CfgExpr::Feature("new_feature".to_string())) + ); + + // Test any() with enabled feature - should be removed (None = always true) + let expr = CfgExpr::Any(vec![ + CfgExpr::Feature("feature1".to_string()), + CfgExpr::Feature("feature2".to_string()), + ]); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + None + ); + + // Test all() with disabled feature - should become False + let expr = CfgExpr::All(vec![ + CfgExpr::Feature("feature1".to_string()), + CfgExpr::Feature("feature2".to_string()), + ]); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + Some(CfgExpr::False) + ); + + // Test not() with disabled feature - should be removed (not(false) = true) + let expr = CfgExpr::Not(Box::new(CfgExpr::Feature("feature2".to_string()))); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + None + ); + + // Test not() with enabled feature - should become False (not(true) = false) + let expr = CfgExpr::Not(Box::new(CfgExpr::Feature("feature1".to_string()))); + assert_eq!( + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default() + ), + Some(CfgExpr::False) + ); +} + +#[test] +#[should_panic(expected = "unmapped feature: unknown")] +fn test_strict_feature_processing_unmapped_panic() { + use std::collections::HashMap; + + let enabled_features = HashSet::new(); + let disabled_features = HashSet::new(); + let feature_mappings = HashMap::new(); + + // Test unmapped feature - should panic + let expr = CfgExpr::Feature("unknown".to_string()); + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default(), + ); +} + +#[test] +#[should_panic(expected = "unmapped feature: unknown")] +fn test_strict_feature_processing_unmapped_in_any_panic() { + use std::collections::HashMap; + + let mut enabled_features = HashSet::new(); + enabled_features.insert("feature1".to_string()); + + let disabled_features = HashSet::new(); + let feature_mappings = HashMap::new(); + + // Test unmapped feature in any() - should panic + let expr = CfgExpr::Any(vec![ + CfgExpr::Feature("feature1".to_string()), + CfgExpr::Feature("unknown".to_string()), + ]); + expr.process_features_strict( + &enabled_features, + &disabled_features, + &feature_mappings, + false, + &None, + &None, + &None, + &None, + &SourceLocation::default(), + ); +} From 3e077d2f312e3a7430cf20420e43db8d4b697d4d Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 22 Aug 2025 18:07:20 +0200 Subject: [PATCH 4/8] target triple object --- Cargo.toml | 1 + prebindgen/Cargo.toml | 1 + prebindgen/src/lib.rs | 1 + prebindgen/src/utils/mod.rs | 3 ++ prebindgen/src/utils/target.rs | 96 ++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 prebindgen/src/utils/target.rs diff --git a/Cargo.toml b/Cargo.toml index a7d0e6f..22e69db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ 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 5c28a05..3e4156e 100644 --- a/prebindgen/Cargo.toml +++ b/prebindgen/Cargo.toml @@ -25,6 +25,7 @@ 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/lib.rs b/prebindgen/src/lib.rs index c833012..e354422 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -134,6 +134,7 @@ pub use crate::api::buildrs::is_feature_enabled; pub use crate::api::record::SourceLocation; pub use crate::api::source::Source; pub use crate::utils::edition::RustEdition; +pub use crate::utils::target::TargetTriple; /// Filters for sequences of (syn::Item, SourceLocation) called by `itertools::batching` pub mod batching { diff --git a/prebindgen/src/utils/mod.rs b/prebindgen/src/utils/mod.rs index 835df1e..05b1d1a 100644 --- a/prebindgen/src/utils/mod.rs +++ b/prebindgen/src/utils/mod.rs @@ -1,2 +1,5 @@ pub(crate) mod edition; pub(crate) mod jsonl; + +// Public utilities +pub mod target; diff --git a/prebindgen/src/utils/target.rs b/prebindgen/src/utils/target.rs new file mode 100644 index 0000000..3a5002e --- /dev/null +++ b/prebindgen/src/utils/target.rs @@ -0,0 +1,96 @@ +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)] +pub struct TargetTriple(Triple); + +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}")) + } + + /// Create from an existing target_lexicon Triple. + pub fn from_triple(triple: Triple) -> Self { Self(triple) } + + /// Get the architecture as a canonical string used by Rust cfg target_arch. + pub fn arch(&self) -> String { self.0.architecture.to_string() } + + /// Get the vendor as string used by Rust cfg target_vendor. + pub fn vendor(&self) -> String { self.0.vendor.to_string() } + + /// 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(), + } + } + + /// Get the environment as string used by Rust cfg target_env (may be "unknown"). + pub fn env(&self) -> String { 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 } + + /// 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 !env.is_empty() && env != "unknown" { + let env_lit = LitStr::new(&env, proc_macro2::Span::call_site()); + parts.push(quote! { target_env = #env_lit }); + } + if parts.len() == 1 { + parts.remove(0) + } else { + quote! { all( #(#parts),* ) } + } + } +} + +/// Allow quoting a TargetTriple directly, yielding its cfg tokens. +impl quote::ToTokens for TargetTriple { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.to_cfg_tokens()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn maps_darwin_to_macos() { + let tt = TargetTriple::parse("aarch64-apple-darwin").unwrap(); + assert_eq!(tt.os(), "macos"); + } + + #[test] + fn builds_cfg_without_unknown_env() { + 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\"")); + } +} From 88e6d9497a8e908c5ed790fdaf0f0e9fceddc34c Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 22 Aug 2025 21:47:39 +0200 Subject: [PATCH 5/8] targewt triple fromstr --- prebindgen/src/lib.rs | 6 +++++- prebindgen/src/utils/mod.rs | 2 +- prebindgen/src/utils/{target.rs => target_triple.rs} | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) rename prebindgen/src/utils/{target.rs => target_triple.rs} (95%) diff --git a/prebindgen/src/lib.rs b/prebindgen/src/lib.rs index e354422..a59ccd3 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -134,7 +134,7 @@ pub use crate::api::buildrs::is_feature_enabled; pub use crate::api::record::SourceLocation; pub use crate::api::source::Source; pub use crate::utils::edition::RustEdition; -pub use crate::utils::target::TargetTriple; +pub use crate::utils::target_triple::TargetTriple; /// Filters for sequences of (syn::Item, SourceLocation) called by `itertools::batching` pub mod batching { @@ -174,6 +174,10 @@ pub mod collect { pub use crate::api::collect::destination::Destination; } +pub mod utils { + pub use crate::utils::target_triple::TargetTriple; +} + #[doc(hidden)] pub use utils::jsonl::{write_to_jsonl_file, read_jsonl_file}; #[doc(hidden)] diff --git a/prebindgen/src/utils/mod.rs b/prebindgen/src/utils/mod.rs index 05b1d1a..0a48b00 100644 --- a/prebindgen/src/utils/mod.rs +++ b/prebindgen/src/utils/mod.rs @@ -2,4 +2,4 @@ pub(crate) mod edition; pub(crate) mod jsonl; // Public utilities -pub mod target; +pub mod target_triple; diff --git a/prebindgen/src/utils/target.rs b/prebindgen/src/utils/target_triple.rs similarity index 95% rename from prebindgen/src/utils/target.rs rename to prebindgen/src/utils/target_triple.rs index 3a5002e..0cd3664 100644 --- a/prebindgen/src/utils/target.rs +++ b/prebindgen/src/utils/target_triple.rs @@ -67,6 +67,14 @@ impl TargetTriple { } } +impl std::str::FromStr for TargetTriple { + type Err = String; + + fn from_str(s: &str) -> Result { + TargetTriple::parse(s) + } +} + /// Allow quoting a TargetTriple directly, yielding its cfg tokens. impl quote::ToTokens for TargetTriple { fn to_tokens(&self, tokens: &mut TokenStream) { From bff561a2bfe95d081bd0b198378424923c80254e Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Sat, 23 Aug 2025 09:39:49 +0200 Subject: [PATCH 6/8] rename to CfgFilter --- .../{feature_filter.rs => cfg_filter.rs} | 45 ++++++++++--------- prebindgen/src/api/batching/mod.rs | 2 +- prebindgen/src/api/source.rs | 26 +++++------ prebindgen/src/lib.rs | 6 +-- 4 files changed, 40 insertions(+), 39 deletions(-) rename prebindgen/src/api/batching/{feature_filter.rs => cfg_filter.rs} (91%) diff --git a/prebindgen/src/api/batching/feature_filter.rs b/prebindgen/src/api/batching/cfg_filter.rs similarity index 91% rename from prebindgen/src/api/batching/feature_filter.rs rename to prebindgen/src/api/batching/cfg_filter.rs index 3c22c5c..4f03171 100644 --- a/prebindgen/src/api/batching/feature_filter.rs +++ b/prebindgen/src/api/batching/cfg_filter.rs @@ -4,21 +4,22 @@ use roxygen::roxygen; use crate::{api::record::SourceLocation, codegen::process_features::process_item_features}; -/// Builder for configuring FeatureFilter instances +/// Builder for configuring CfgFilter instances /// -/// Configures how feature flags in `#[cfg(feature="...")]` attributes +/// Configures how flags in `#[cfg(...)]` attributes /// are processed when generating FFI bindings. +/// Supports features and target architecture filtering. /// /// This filter is usually not necessary: the `Source` by default automatically reads /// features enabled in the crate and removes any code guarded by disabled features. /// -/// But if necessary this option can be disabled (see and FeatureFilter can be applied -/// explicitly. +/// But if necessary this filtering on `Source` level can be disabled and CfgFilter +/// can be applied explicitly. /// /// # Example /// /// ``` -/// let builder = prebindgen::batching::feature_filter::Builder::new() +/// let builder = prebindgen::batching::cfg_filter::Builder::new() /// .disable_feature("unstable") /// .enable_feature("std") /// .match_feature("internal", "public") @@ -40,12 +41,12 @@ pub struct Builder { } impl Builder { - /// Create a new Builder for configuring FeatureFilter + /// Create a new Builder for configuring CfgFilter /// /// # Example /// /// ``` - /// let builder = prebindgen::batching::feature_filter::Builder::new(); + /// let builder = prebindgen::batching::cfg_filter::Builder::new(); /// ``` pub fn new() -> Self { Self { @@ -69,7 +70,7 @@ impl Builder { /// # Example /// /// ``` - /// let builder = prebindgen::batching::feature_filter::Builder::new() + /// let builder = prebindgen::batching::cfg_filter::Builder::new() /// .disable_feature("experimental") /// .disable_feature("deprecated"); /// ``` @@ -92,7 +93,7 @@ impl Builder { /// # Example /// /// ``` - /// let builder = prebindgen::batching::feature_filter::Builder::new() + /// let builder = prebindgen::batching::cfg_filter::Builder::new() /// .enable_feature("experimental"); /// ``` #[roxygen] @@ -112,7 +113,7 @@ impl Builder { /// # Example /// /// ``` - /// let builder = prebindgen::batching::feature_filter::Builder::new() + /// let builder = prebindgen::batching::cfg_filter::Builder::new() /// .enable_target_arch("x86_64"); /// ``` #[roxygen] @@ -172,7 +173,7 @@ impl Builder { /// # Example /// /// ``` - /// let builder = prebindgen::batching::feature_filter::Builder::new() + /// let builder = prebindgen::batching::cfg_filter::Builder::new() /// .match_feature("unstable", "unstable") /// .match_feature("internal", "unstable"); /// ``` @@ -242,16 +243,16 @@ impl Builder { self } - /// Build the FeatureFilter instance with the configured options + /// Build the CfgFilter instance with the configured options /// /// # Example /// /// ``` - /// let filter = prebindgen::batching::feature_filter::Builder::new() + /// let filter = prebindgen::batching::cfg_filter::Builder::new() /// .disable_feature("internal") /// .build(); /// ``` - pub fn build(self) -> FeatureFilter { + pub fn build(self) -> CfgFilter { // Determine if this filter is active (i.e., not pass-through) let active = self.features_assert.is_some() || self.disable_unknown_features @@ -289,7 +290,7 @@ impl Builder { prelude_item = Some((item, SourceLocation::default())); } - FeatureFilter { + CfgFilter { builder: self, prelude_item, prelude_emitted: false, @@ -306,7 +307,7 @@ impl Default for Builder { /// Filters prebindgen items based on Rust feature flags /// -/// The `FeatureFilter` processes items with `#[cfg(feature="...")]` attributes, +/// The `CfgFilter` processes items with `#[cfg(feature="...")]` attributes, /// allowing selective inclusion, exclusion, or renaming of feature-gated code /// in the generated FFI bindings. /// @@ -321,7 +322,7 @@ impl Default for Builder { /// # prebindgen::Source::init_doctest_simulate(); /// let source = prebindgen::Source::new("source_ffi"); /// -/// let feature_filter = prebindgen::batching::FeatureFilter::builder() +/// let cfg_filter = prebindgen::batching::CfgFilter::builder() /// .disable_feature("unstable") /// .disable_feature("internal") /// .enable_feature("std") @@ -332,24 +333,24 @@ impl Default for Builder { /// # use itertools::Itertools; /// let filtered_items: Vec<_> = source /// .items_all() -/// .batching(feature_filter.into_closure()) +/// .batching(cfg_filter.into_closure()) /// .take(0) // Take 0 for doctest /// .collect(); /// ``` -pub struct FeatureFilter { +pub struct CfgFilter { builder: Builder, prelude_item: Option<(syn::Item, SourceLocation)>, prelude_emitted: bool, active: bool, } -impl FeatureFilter { - /// Create a builder for configuring a feature filter instance +impl CfgFilter { + /// Create a builder for configuring a cfg filter instance /// /// # Example /// /// ``` - /// let filter = prebindgen::batching::FeatureFilter::builder() + /// let filter = prebindgen::batching::CfgFilter::builder() /// .disable_feature("unstable") /// .enable_feature("std") /// .build(); diff --git a/prebindgen/src/api/batching/mod.rs b/prebindgen/src/api/batching/mod.rs index 954c5ac..8430112 100644 --- a/prebindgen/src/api/batching/mod.rs +++ b/prebindgen/src/api/batching/mod.rs @@ -1,2 +1,2 @@ -pub(crate) mod feature_filter; +pub(crate) mod cfg_filter; pub(crate) mod ffi_converter; diff --git a/prebindgen/src/api/source.rs b/prebindgen/src/api/source.rs index 8050e2f..ff2df7e 100644 --- a/prebindgen/src/api/source.rs +++ b/prebindgen/src/api/source.rs @@ -9,7 +9,7 @@ use itertools::Itertools; use roxygen::roxygen; use crate::{ - api::batching::feature_filter, api::record::Record, utils::jsonl::read_jsonl_file, + api::batching::cfg_filter, api::record::Record, utils::jsonl::read_jsonl_file, SourceLocation, CRATE_NAME_FILE, FEATURES_FILE, }; @@ -68,7 +68,7 @@ thread_local! { pub struct Source { crate_name: String, items: HashMap>, - // Configuration needed to build a FeatureFilter at iteration time + // Configuration needed to build a CfgFilter at iteration time features_constant: Option, features_list: Vec, // normalized list from features.txt } @@ -200,8 +200,8 @@ impl Source { &'a self, groups: &'a [&'a str], ) -> impl Iterator + 'a { - // Build a feature filter and apply it lazily with itertools::batching - let mut filter = self.build_feature_filter(); + // Build a cfg filter and apply it lazily with itertools::batching + let mut filter = self.build_cfg_filter(); groups .iter() .filter_map(|group| self.items.get(*group)) @@ -227,8 +227,8 @@ impl Source { &'a self, groups: &'a [&'a str], ) -> impl Iterator + 'a { - // Build a feature filter and apply it lazily with itertools::batching - let mut filter = self.build_feature_filter(); + // Build a cfg filter and apply it lazily with itertools::batching + let mut filter = self.build_cfg_filter(); self.items .iter() .filter(|(group, _)| !groups.contains(&group.as_str())) @@ -249,17 +249,17 @@ impl Source { /// assert_eq!(items.len(), 2); // should contain TestStruct and test_function /// ``` pub fn items_all<'a>(&'a self) -> impl Iterator + 'a { - // Build a feature filter and apply it lazily with itertools::batching - let mut filter = self.build_feature_filter(); + // Build a cfg filter and apply it lazily with itertools::batching + let mut filter = self.build_cfg_filter(); self.items .iter() .flat_map(|(_, records)| records.iter().cloned()) .batching(move |iter| filter.call(iter)) } - /// Internal: construct a FeatureFilter from the stored configuration and features file - fn build_feature_filter(&self) -> feature_filter::FeatureFilter { - let mut builder = feature_filter::FeatureFilter::builder(); + /// Internal: construct a CfgFilter from the stored configuration and features file + fn build_cfg_filter(&self) -> cfg_filter::CfgFilter { + let mut builder = cfg_filter::CfgFilter::builder(); if let Some(const_name) = &self.features_constant { // If the provided constant isn't fully qualified, qualify it with the crate name let qualified_const = if const_name.contains("::") { @@ -384,7 +384,7 @@ impl Builder { /// Enables or disables filtering by features when /// extracting collected data. /// - /// Pass `None` to disable feature filtering. + /// Pass `None` to disable cfg filtering. /// /// Pass, for example, `Some("source_crate::FEATURES")` or `Some("FEATURES")` /// to enable filtering. The value is the name of the features constant @@ -397,7 +397,7 @@ impl Builder { /// const FEATURES: &str = prebindgen_proc_macro::features!(); /// ``` #[roxygen] - pub fn enable_feature_filtering( + pub fn enable_cfg_filtering( mut self, /// Full name of the constant with features in the source crate name: Option>, diff --git a/prebindgen/src/lib.rs b/prebindgen/src/lib.rs index a59ccd3..9e5569c 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -141,10 +141,10 @@ pub mod batching { pub mod ffi_converter { pub use crate::api::batching::ffi_converter::Builder; } - pub use crate::api::batching::feature_filter::FeatureFilter; + pub use crate::api::batching::cfg_filter::CfgFilter; pub use crate::api::batching::ffi_converter::FfiConverter; - pub mod feature_filter { - pub use crate::api::batching::feature_filter::Builder; + pub mod cfg_filter { + pub use crate::api::batching::cfg_filter::Builder; } } From e2e7187e51bfc15cb59e4c5acf0a27303cac5cca Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Sun, 24 Aug 2025 00:08:46 +0200 Subject: [PATCH 7/8] renamed back enable_feature_filtering, modules reorg, doc update --- prebindgen-proc-macro/src/lib.rs | 2 +- prebindgen/src/api/mod.rs | 1 + prebindgen/src/api/source.rs | 23 ++++++++++++++----- prebindgen/src/{ => api}/utils/edition.rs | 0 prebindgen/src/{ => api}/utils/jsonl.rs | 0 prebindgen/src/{ => api}/utils/mod.rs | 0 .../src/{ => api}/utils/target_triple.rs | 0 prebindgen/src/codegen/replace_types.rs | 2 +- prebindgen/src/lib.rs | 11 ++++----- 9 files changed, 25 insertions(+), 14 deletions(-) rename prebindgen/src/{ => api}/utils/edition.rs (100%) rename prebindgen/src/{ => api}/utils/jsonl.rs (100%) rename prebindgen/src/{ => api}/utils/mod.rs (100%) rename prebindgen/src/{ => api}/utils/target_triple.rs (100%) diff --git a/prebindgen-proc-macro/src/lib.rs b/prebindgen-proc-macro/src/lib.rs index e4e8987..0320f6b 100644 --- a/prebindgen-proc-macro/src/lib.rs +++ b/prebindgen-proc-macro/src/lib.rs @@ -271,7 +271,7 @@ pub fn prebindgen(args: TokenStream, input: TokenStream) -> TokenStream { // Get the full path to the JSONL file let file_path = get_prebindgen_jsonl_path(&group); - if let Err(_) = prebindgen::write_to_jsonl_file(&file_path, &[&new_record]) { + if let Err(_) = prebindgen::utils::write_to_jsonl_file(&file_path, &[&new_record]) { return TokenStream::from(quote! { compile_error!("Failed to write prebindgen record"); }); diff --git a/prebindgen/src/api/mod.rs b/prebindgen/src/api/mod.rs index 581e5ba..89b8e35 100644 --- a/prebindgen/src/api/mod.rs +++ b/prebindgen/src/api/mod.rs @@ -5,3 +5,4 @@ pub(crate) mod filter_map; pub(crate) mod map; pub(crate) mod record; pub(crate) mod source; +pub(crate) mod utils; diff --git a/prebindgen/src/api/source.rs b/prebindgen/src/api/source.rs index ff2df7e..7002477 100644 --- a/prebindgen/src/api/source.rs +++ b/prebindgen/src/api/source.rs @@ -9,7 +9,7 @@ use itertools::Itertools; use roxygen::roxygen; use crate::{ - api::batching::cfg_filter, api::record::Record, utils::jsonl::read_jsonl_file, + api::batching::cfg_filter, api::record::Record, api::utils::jsonl::read_jsonl_file, SourceLocation, CRATE_NAME_FILE, FEATURES_FILE, }; @@ -381,23 +381,34 @@ impl Builder { } } - /// Enables or disables filtering by features when - /// extracting collected data. + /// Enables or disables filtering by features when extracting collected data. + /// Accepts name of the constant with the list of features in the source crate. /// - /// Pass `None` to disable cfg filtering. + /// Pass `None` to disable feature filtering. /// /// Pass, for example, `Some("source_crate::FEATURES")` or `Some("FEATURES")` /// to enable filtering. The value is the name of the features constant /// from the source crate. /// + /// It's important to note that the set of features to filter is determined + /// not by this constant, but by file "features.txt" in + /// prebindgen output directory. These are features which the source crate was + /// built with *as build.rs dependency*. The constant contains the features which + /// the source crate was built *as library dependency*. + /// The purpose of the constant is to use it in the assert in the + /// generated code to ensure that both feature lists are the same. + /// If it's not the case, compilation fails and it's developer's + /// job to ensure that the source crate is configured in the same way for + /// both `[dependencies]` and `[build-dependencies]`. + /// /// Filtering is enabled by default; the default constant name is `FEATURES`. /// - /// The source crate should contain the following line by default: + /// The features constant should be defined in the source crate as follows: /// ```rust,ignore /// const FEATURES: &str = prebindgen_proc_macro::features!(); /// ``` #[roxygen] - pub fn enable_cfg_filtering( + pub fn enable_feature_filtering( mut self, /// Full name of the constant with features in the source crate name: Option>, diff --git a/prebindgen/src/utils/edition.rs b/prebindgen/src/api/utils/edition.rs similarity index 100% rename from prebindgen/src/utils/edition.rs rename to prebindgen/src/api/utils/edition.rs diff --git a/prebindgen/src/utils/jsonl.rs b/prebindgen/src/api/utils/jsonl.rs similarity index 100% rename from prebindgen/src/utils/jsonl.rs rename to prebindgen/src/api/utils/jsonl.rs diff --git a/prebindgen/src/utils/mod.rs b/prebindgen/src/api/utils/mod.rs similarity index 100% rename from prebindgen/src/utils/mod.rs rename to prebindgen/src/api/utils/mod.rs diff --git a/prebindgen/src/utils/target_triple.rs b/prebindgen/src/api/utils/target_triple.rs similarity index 100% rename from prebindgen/src/utils/target_triple.rs rename to prebindgen/src/api/utils/target_triple.rs diff --git a/prebindgen/src/codegen/replace_types.rs b/prebindgen/src/codegen/replace_types.rs index a73577f..054f7cd 100644 --- a/prebindgen/src/codegen/replace_types.rs +++ b/prebindgen/src/codegen/replace_types.rs @@ -12,7 +12,7 @@ use std::{ collections::{HashMap, HashSet}, }; -use crate::{utils::edition::RustEdition, SourceLocation}; +use crate::{api::utils::edition::RustEdition, SourceLocation}; /// Configuration parameters for parsing records pub(crate) struct ParseConfig<'a> { diff --git a/prebindgen/src/lib.rs b/prebindgen/src/lib.rs index 9e5569c..677f5cb 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -123,7 +123,6 @@ pub const DEFAULT_GROUP_NAME: &str = "default"; pub(crate) mod api; pub(crate) mod codegen; -pub(crate) mod utils; pub use crate::api::buildrs::get_all_features; pub use crate::api::buildrs::get_enabled_features; @@ -133,8 +132,8 @@ pub use crate::api::buildrs::is_feature_enabled; pub use crate::api::record::SourceLocation; pub use crate::api::source::Source; -pub use crate::utils::edition::RustEdition; -pub use crate::utils::target_triple::TargetTriple; +pub use crate::api::utils::edition::RustEdition; +pub use crate::api::utils::target_triple::TargetTriple; /// Filters for sequences of (syn::Item, SourceLocation) called by `itertools::batching` pub mod batching { @@ -175,11 +174,11 @@ pub mod collect { } pub mod utils { - pub use crate::utils::target_triple::TargetTriple; + #[doc(hidden)] + pub use crate::api::utils::jsonl::{read_jsonl_file, write_to_jsonl_file}; + pub use crate::api::utils::target_triple::TargetTriple; } -#[doc(hidden)] -pub use utils::jsonl::{write_to_jsonl_file, read_jsonl_file}; #[doc(hidden)] pub use crate::api::record::Record; #[doc(hidden)] From 85e1e653b7134ae600a7e67962deb5dfcb9720aa Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Sun, 24 Aug 2025 00:41:26 +0200 Subject: [PATCH 8/8] target filtering added --- prebindgen-proc-macro/src/lib.rs | 2 +- prebindgen/src/api/source.rs | 87 ++++++++++++++++++++-- prebindgen/src/api/utils/target_triple.rs | 30 ++++++-- prebindgen/src/codegen/cfg_expr.rs | 24 +++++- prebindgen/src/codegen/process_features.rs | 12 +-- 5 files changed, 132 insertions(+), 23 deletions(-) diff --git a/prebindgen-proc-macro/src/lib.rs b/prebindgen-proc-macro/src/lib.rs index 0320f6b..1e8f356 100644 --- a/prebindgen-proc-macro/src/lib.rs +++ b/prebindgen-proc-macro/src/lib.rs @@ -128,7 +128,7 @@ fn get_prebindgen_jsonl_path(group: &str) -> std::path::PathBuf { }; let mut random_value = None; // Try to really create file and repeat until success - // to avoid collisions in extremely rare case when two threads got + // to avoid collisions in extremely rare case when two threads got // the same random value let new_path = loop { let postfix = if let Some(rv) = random_value { diff --git a/prebindgen/src/api/source.rs b/prebindgen/src/api/source.rs index 7002477..3b1c690 100644 --- a/prebindgen/src/api/source.rs +++ b/prebindgen/src/api/source.rs @@ -9,8 +9,8 @@ use itertools::Itertools; use roxygen::roxygen; use crate::{ - api::batching::cfg_filter, api::record::Record, api::utils::jsonl::read_jsonl_file, - SourceLocation, CRATE_NAME_FILE, FEATURES_FILE, + api::{batching::cfg_filter, record::Record, utils::jsonl::read_jsonl_file}, + SourceLocation, TargetTriple, CRATE_NAME_FILE, FEATURES_FILE, }; /// File extension for data files @@ -70,6 +70,7 @@ pub struct Source { items: HashMap>, // Configuration needed to build a CfgFilter at iteration time features_constant: Option, + target_triple_env_var: Option, features_list: Vec, // normalized list from features.txt } @@ -91,7 +92,11 @@ impl Source { } impl Source { - fn build_internal(input_dir: &Path, features_constant: Option) -> Self { + fn build_internal( + input_dir: &Path, + features_constant: Option, + target_triple_env_var: Option, + ) -> Self { if let Some(source) = DOCTEST_SOURCE.with(|source| (*source.borrow()).clone()) { return source; } @@ -125,6 +130,7 @@ impl Source { items, features_constant, features_list, + target_triple_env_var, } } @@ -158,6 +164,7 @@ impl Source { ), ]), features_constant: None, + target_triple_env_var: None, features_list: Vec::new(), }; DOCTEST_SOURCE.with(|cell| { @@ -275,6 +282,28 @@ impl Source { .join(" "); builder = builder.predefined_features(qualified_const, features_list); } + if let Some(env_var) = &self.target_triple_env_var { + let target = std::env::var(env_var).unwrap_or_else(|_| { + panic!( + "Environment variable {} is not set. \ + Ensure that the build script sets it to the target triple.", + env_var + ) + }); + let target_triple = TargetTriple::parse(&target).unwrap_or_else(|e| { + panic!( + "Failed to parse target triple '{}' from environment variable {}: {}", + target, env_var, e + ) + }); + builder = builder + .enable_target_arch(target_triple.arch()) + .enable_target_os(target_triple.os()) + .enable_target_vendor(target_triple.vendor()); + if let Some(env) = target_triple.env() { + builder = builder.enable_target_env(env); + } + } builder.build() } @@ -371,6 +400,7 @@ fn read_features_from_out_dir(input_dir: &Path) -> Vec { pub struct Builder { input_dir: PathBuf, features_constant: Option, + target_triple_env_var: Option, } impl Builder { @@ -378,6 +408,7 @@ impl Builder { Self { input_dir: input_dir.as_ref().to_path_buf(), features_constant: Some("FEATURES".to_string()), + target_triple_env_var: Some("TARGET".to_string()), } } @@ -393,7 +424,7 @@ impl Builder { /// It's important to note that the set of features to filter is determined /// not by this constant, but by file "features.txt" in /// prebindgen output directory. These are features which the source crate was - /// built with *as build.rs dependency*. The constant contains the features which + /// built with *as build.rs dependency*. The constant contains the features which /// the source crate was built *as library dependency*. /// The purpose of the constant is to use it in the assert in the /// generated code to ensure that both feature lists are the same. @@ -417,8 +448,54 @@ impl Builder { self } + /// Enables filtering source code by target-triple which is usually + /// passed to `build.rs` in `TARGET` environment variable. + /// Accepts the name of the environment variable as an optional string. + /// Default value is `TARGET`. + /// + /// Pass `None` to disable target filtering. + /// + /// When enabled, the code will be filtered by the target triple specified + /// (architecture, vendor, os, env). By default filtering is performed + /// by the value from the `TARGET` environment variable which contains + /// the target triple for the library. + /// + /// It's useful to mention that it's hard to imagine a scenario + /// when it's necessary to change the environment variable name. + /// The `Source` object is used in the final crate, the one which builds the + /// no-mangle library and the language bindings (e.g. C headers). On this + /// stage the `TARGET` environment variable contains real target triple + /// even in cross-compilation scenarios. Passing the real target in the + /// other variable's name, e.g: + /// ```ignore + /// CROSS_TARGET=x86_64-pc-windows-gnu cargo build --target x86_64-pc-windows-gnu + /// ``` + /// sometimes is necessary to inform the underlying "ffi" crate about the + /// real target triple, because it's compiled as a `build.rs` dependency on + /// the host platform and therefore it's target is the host. + /// But the `Source` is not called on this stage, so normally the access to + /// this `CROSS_TARGET` variable is not needed. + /// But for the sake of completeness the ability to use any environment variable + /// is provided. + /// It's also possible to just create standalone `CfgFilter` object + /// with necessary configuration and insert into the iterator chain. + #[roxygen] + pub fn enable_target_filtering( + mut self, + /// Full name of the environment variable which contains the target triple + /// to which the code is generated + name: Option>, + ) -> Self { + self.target_triple_env_var = name.map(|n| n.into()); + self + } + /// Build the `Source` instance pub fn build(self) -> Source { - Source::build_internal(&self.input_dir, self.features_constant) + Source::build_internal( + &self.input_dir, + self.features_constant, + self.target_triple_env_var, + ) } } diff --git a/prebindgen/src/api/utils/target_triple.rs b/prebindgen/src/api/utils/target_triple.rs index 0cd3664..58bac88 100644 --- a/prebindgen/src/api/utils/target_triple.rs +++ b/prebindgen/src/api/utils/target_triple.rs @@ -17,13 +17,19 @@ impl TargetTriple { } /// Create from an existing target_lexicon Triple. - pub fn from_triple(triple: Triple) -> Self { Self(triple) } + pub fn from_triple(triple: Triple) -> Self { + Self(triple) + } /// 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) -> String { + self.0.architecture.to_string() + } /// 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) -> String { + self.0.vendor.to_string() + } /// Get the operating system as string used by Rust cfg target_os. /// Maps Darwin to "macos" to match Rust cfg semantics. @@ -35,13 +41,23 @@ impl TargetTriple { } /// Get the environment as string used by Rust cfg target_env (may be "unknown"). - pub fn env(&self) -> String { self.0.environment.to_string() } + 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 } + pub fn as_triple(&self) -> &Triple { + &self.0 + } /// Decompose into the inner Triple. - pub fn into_triple(self) -> Triple { self.0 } + pub fn into_triple(self) -> Triple { + self.0 + } /// Build a cfg expression TokenStream like: /// all(target_arch = "aarch64", target_vendor = "apple", target_os = "macos", target_env = "gnu") @@ -55,7 +71,7 @@ impl TargetTriple { parts.push(quote! { target_arch = #arch }); parts.push(quote! { target_vendor = #vendor }); parts.push(quote! { target_os = #os }); - if !env.is_empty() && env != "unknown" { + if let Some(env) = env { let env_lit = LitStr::new(&env, proc_macro2::Span::call_site()); parts.push(quote! { target_env = #env_lit }); } diff --git a/prebindgen/src/codegen/cfg_expr.rs b/prebindgen/src/codegen/cfg_expr.rs index 62c3453..a0efaeb 100644 --- a/prebindgen/src/codegen/cfg_expr.rs +++ b/prebindgen/src/codegen/cfg_expr.rs @@ -131,14 +131,22 @@ impl CfgExpr { } CfgExpr::TargetVendor(val) => { if let Some(sel) = enabled_target_vendor.as_ref() { - if val == sel { None } else { Some(CfgExpr::False) } + if val == sel { + None + } else { + Some(CfgExpr::False) + } } else { Some(self.clone()) } } CfgExpr::TargetArch(val) => { if let Some(sel) = enabled_target_arch.as_ref() { - if val == sel { None } else { Some(CfgExpr::False) } + if val == sel { + None + } else { + Some(CfgExpr::False) + } } else { Some(self.clone()) } @@ -227,14 +235,22 @@ impl CfgExpr { } CfgExpr::TargetOs(val) => { if let Some(sel) = enabled_target_os.as_ref() { - if val == sel { None } else { Some(CfgExpr::False) } + if val == sel { + None + } else { + Some(CfgExpr::False) + } } else { Some(self.clone()) } } CfgExpr::TargetEnv(val) => { if let Some(sel) = enabled_target_env.as_ref() { - if val == sel { None } else { Some(CfgExpr::False) } + if val == sel { + None + } else { + Some(CfgExpr::False) + } } else { Some(self.clone()) } diff --git a/prebindgen/src/codegen/process_features.rs b/prebindgen/src/codegen/process_features.rs index c76e851..49ea6e3 100644 --- a/prebindgen/src/codegen/process_features.rs +++ b/prebindgen/src/codegen/process_features.rs @@ -102,12 +102,12 @@ pub(crate) fn process_item_features( attrs, disabled_features, enabled_features, - feature_mappings, - disable_unknown_features, - enabled_target_arch, - enabled_target_vendor, - enabled_target_os, - enabled_target_env, + feature_mappings, + disable_unknown_features, + enabled_target_arch, + enabled_target_vendor, + enabled_target_os, + enabled_target_env, source_location, ) }