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-proc-macro/src/lib.rs b/prebindgen-proc-macro/src/lib.rs index e4e8987..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 { @@ -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/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/api/batching/feature_filter.rs b/prebindgen/src/api/batching/cfg_filter.rs similarity index 71% rename from prebindgen/src/api/batching/feature_filter.rs rename to prebindgen/src/api/batching/cfg_filter.rs index 386c68f..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") @@ -32,15 +33,20 @@ 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 { - /// 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 { @@ -49,6 +55,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, } } @@ -60,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"); /// ``` @@ -83,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] @@ -95,6 +105,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::cfg_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 @@ -104,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"); /// ``` @@ -174,22 +243,26 @@ 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 || !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; @@ -217,7 +290,7 @@ impl Builder { prelude_item = Some((item, SourceLocation::default())); } - FeatureFilter { + CfgFilter { builder: self, prelude_item, prelude_emitted: false, @@ -234,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. /// @@ -249,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") @@ -260,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(); @@ -312,6 +385,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/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/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 8050e2f..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::feature_filter, api::record::Record, 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 @@ -68,8 +68,9 @@ 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, + 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| { @@ -200,8 +207,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 +234,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 +256,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("::") { @@ -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,11 +408,12 @@ 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()), } } - /// 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 feature filtering. /// @@ -390,9 +421,20 @@ impl Builder { /// 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!(); /// ``` @@ -406,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/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 51% rename from prebindgen/src/utils/mod.rs rename to prebindgen/src/api/utils/mod.rs index 835df1e..0a48b00 100644 --- a/prebindgen/src/utils/mod.rs +++ b/prebindgen/src/api/utils/mod.rs @@ -1,2 +1,5 @@ pub(crate) mod edition; pub(crate) mod jsonl; + +// Public utilities +pub mod target_triple; diff --git a/prebindgen/src/api/utils/target_triple.rs b/prebindgen/src/api/utils/target_triple.rs new file mode 100644 index 0000000..58bac88 --- /dev/null +++ b/prebindgen/src/api/utils/target_triple.rs @@ -0,0 +1,120 @@ +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) -> 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 + } + + /// 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 parts.len() == 1 { + parts.remove(0) + } else { + quote! { all( #(#parts),* ) } + } + } +} + +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) { + 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\"")); + } +} diff --git a/prebindgen/src/codegen/cfg_expr.rs b/prebindgen/src/codegen/cfg_expr.rs index 860259e..a0efaeb 100644 --- a/prebindgen/src/codegen/cfg_expr.rs +++ b/prebindgen/src/codegen/cfg_expr.rs @@ -11,16 +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), + /// 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), - /// Logical NOT: `not(expr)` - Not(Box), + + // Fallbacks /// Any other cfg expression we don't specifically handle Other(String), /// Explicit false value (for feature processing) @@ -63,6 +74,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 +104,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 +129,28 @@ 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 +159,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 +190,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 +222,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 +233,28 @@ 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 +269,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 +328,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 { @@ -314,211 +428,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_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, - &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, - &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, - &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, - &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, - &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, - &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, - &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, - &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, - &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(), + ); +} diff --git a/prebindgen/src/codegen/process_features.rs b/prebindgen/src/codegen/process_features.rs index f7401a4..49ea6e3 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 @@ -87,6 +104,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, ) } @@ -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) => { 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 c833012..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,17 +132,18 @@ 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::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 { 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; } } @@ -173,8 +173,12 @@ pub mod collect { pub use crate::api::collect::destination::Destination; } -#[doc(hidden)] -pub use utils::jsonl::{write_to_jsonl_file, read_jsonl_file}; +pub mod utils { + #[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 crate::api::record::Record; #[doc(hidden)]