diff --git a/CHANGES b/CHANGES index 6fd28678..9fcf57e4 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ * Emit `&str`, `&CStr`, and arrays thereof as C string literals, and byte-string (`b"..."`) constants as `uint8_t[]` arrays, instead of dropping them. + * Respect `#[cfg]` on struct fields in `#define` constants by emitting a + helper macro per condition (`#if` cannot appear inside a `#define`). # 0.29.4 diff --git a/docs.md b/docs.md index aa071ac6..ce2aae4f 100644 --- a/docs.md +++ b/docs.md @@ -254,6 +254,8 @@ As cbindgen spiders through your crate, it will make note of all the cfgs it fou However cbindgen has no way of knowing how you want to map those cfgs to defines. You will need to use the `[defines]` section in your cbindgen.toml to specify all the different mappings. It natively understands concepts like any() and all(), so you only need to tell it how you want to translate base concepts like `target_os = "freebsd"` or `feature = "serde"`. +When a struct-literal constant is emitted as a `#define`, `#[cfg]`-gated fields cannot be wrapped in `#if` because preprocessor directives are not allowed inside a macro replacement list. Instead, cbindgen emits one helper macro per condition (`__CBINDGEN_CFG_(...)`, which expands to its arguments when the condition holds and to nothing otherwise) and wraps each gated field initializer, including its trailing comma, in it. These helpers stay defined because the constants expand to them at their use sites. + Note that because cbindgen just parses the source of your crate, you mostly don't need to worry about what crate features or what platform you're targetting. Every possible configuration should be visible to the parser. Our primitive mappings should also be completely platform agnostic (i32 is int32_t regardless of your target). While modules within a crate form a tree with uniquely defined paths to each item, and therefore uniquely defined cfgs for those items, dependencies do not. If you depend on a crate in multiple ways, and those ways produce different cfgs, one of them will be arbitrarily chosen for any types found in that crate. diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs index 9bf632c1..04d74980 100644 --- a/src/bindgen/ir/cfg.rs +++ b/src/bindgen/ir/cfg.rs @@ -257,7 +257,7 @@ impl ToCondition for Cfg { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Condition { Define(String), Any(Vec), @@ -266,6 +266,63 @@ pub enum Condition { } impl Condition { + /// Name of the preprocessor helper macro that expands to its arguments iff this + /// condition holds. Used so `#define` constants can mention cfg-gated + /// struct fields (`#if` is not legal inside a `#define`). + pub fn match_macro_name(&self) -> String { + let mut name = String::from("__CBINDGEN_CFG_"); + self.append_macro_ident(&mut name); + name + } + + fn append_macro_ident(&self, out: &mut String) { + match self { + Condition::Define(define) => { + out.push('D'); + out.push_str(&define.len().to_string()); + out.push('_'); + out.push_str(define); + } + Condition::Not(inner) => { + out.push_str("N_"); + inner.append_macro_ident(out); + } + Condition::All(conditions) => { + out.push('A'); + out.push_str(&conditions.len().to_string()); + for condition in conditions { + out.push('_'); + condition.append_macro_ident(out); + } + } + Condition::Any(conditions) => { + out.push('O'); + out.push_str(&conditions.len().to_string()); + for condition in conditions { + out.push('_'); + condition.append_macro_ident(out); + } + } + } + } + + /// `#define NAME(...) __VA_ARGS__` when `self` holds, empty otherwise. + pub fn write_match_macro(&self, config: &Config, out: &mut SourceWriter) { + let name = self.match_macro_name(); + out.push_set_spaces(0); + out.write("#if "); + self.write(config, out); + out.pop_set_spaces(); + out.new_line(); + write!(out, "#define {name}(...) __VA_ARGS__"); + out.new_line(); + out.write("#else"); + out.new_line(); + write!(out, "#define {name}(...)"); + out.new_line(); + out.write("#endif"); + } + fn write(&self, config: &Config, out: &mut SourceWriter) { match *self { Condition::Define(ref define) => { @@ -352,3 +409,32 @@ impl ConditionWrite for Option { } } } + +#[cfg(test)] +mod tests { + use super::Condition; + + #[test] + fn match_macro_names_encode_condition_structure() { + let first = Condition::All(vec![ + Condition::Define("A".to_owned()), + Condition::Define("B_C".to_owned()), + ]); + let second = Condition::All(vec![ + Condition::Define("A_B".to_owned()), + Condition::Define("C".to_owned()), + ]); + + assert_eq!(first.match_macro_name(), "__CBINDGEN_CFG_A2_D1_A_D3_B_C"); + assert_eq!(second.match_macro_name(), "__CBINDGEN_CFG_A2_D3_A_B_D1_C"); + assert_ne!(first.match_macro_name(), second.match_macro_name()); + } + + #[test] + fn match_macro_names_distinguish_nodes_from_define_text() { + let define = Condition::Define("N_D1_A".to_owned()); + let negated = Condition::Not(Box::new(Condition::Define("A".to_owned()))); + + assert_ne!(define.match_macro_name(), negated.match_macro_name()); + } +} diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 3e041661..bcdc52ff 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -356,7 +356,7 @@ impl Literal { !self.has_pointer_casts() } - fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool { + pub(crate) fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool { if !visitor(self) { return false; } @@ -939,7 +939,7 @@ impl Constant { } Language::Cxx | Language::C => { write!(out, "#define {name} "); - language_backend.write_literal(out, value); + language_backend.write_macro_literal(out, value); } Language::Cython => { if !write_field_prepends_const(&self.ty) { diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index 4d927abe..004715cd 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -1,22 +1,27 @@ use crate::bindgen::ir::{ - to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant, - Field, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, - Typedef, Union, + to_known_assoc_constant, Condition, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, + EnumVariant, Field, GenericParams, Item, ItemContainer, Literal, OpaqueItem, ReprAlign, Static, + Struct, ToCondition, Type, Typedef, Union, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::rename::IdentifierType; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::{cdecl, Bindings, Config, Language}; use crate::bindgen::{DocumentationLength, DocumentationStyle}; +use std::collections::BTreeSet; use std::io::Write; pub struct CLikeLanguageBackend<'a> { config: &'a Config, + writing_macro: bool, } impl<'a> CLikeLanguageBackend<'a> { pub fn new(config: &'a Config) -> Self { - Self { config } + Self { + config, + writing_macro: false, + } } fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { @@ -445,6 +450,22 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } + fn write_cfg_macros(&mut self, out: &mut SourceWriter, b: &Bindings) { + let conditions = collect_cfg_field_conditions(b, self.config); + if conditions.is_empty() { + return; + } + + out.new_line_if_not_start(); + for (i, condition) in conditions.iter().enumerate() { + if i > 0 { + out.new_line(); + } + condition.write_match_macro(self.config, out); + } + out.new_line(); + } + fn open_namespaces(&mut self, out: &mut SourceWriter) { self.open_close_namespaces(out, true); } @@ -905,7 +926,8 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { path, } => { let allow_constexpr = self.config.constant.allow_constexpr && l.can_be_constexpr(); - let is_constexpr = self.config.language == Language::Cxx + let is_constexpr = !self.writing_macro + && self.config.language == Language::Cxx && (self.config.constant.allow_static_const || allow_constexpr); if self.config.language == Language::C { write!(out, "({export_name})"); @@ -921,9 +943,11 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } // In C++, same order as defined is required. let ordered_fields = out.bindings().struct_field_names(path); + let mut separator = ""; for (i, ordered_key) in ordered_fields.iter().enumerate() { if let Some(lit) = fields.get(ordered_key) { let condition = lit.cfg.to_condition(self.config); + let use_match_macro = condition.is_some() && !is_constexpr; if is_constexpr { out.new_line(); @@ -937,8 +961,9 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } condition.write_after(self.config, out); } else { - if i > 0 { - write!(out, ", "); + out.write(separator); + if use_match_macro { + write!(out, "{}(", condition.as_ref().unwrap().match_macro_name()); } if self.config.language == Language::Cxx { @@ -949,6 +974,14 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { write!(out, ".{ordered_key} = "); } self.write_literal(out, &lit.value); + // The trailing comma lives inside the macro so the + // initializer stays valid when the field is omitted. + separator = if use_match_macro { + write!(out, ",)"); + " " + } else { + ", " + }; } } } @@ -963,6 +996,13 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } + fn write_macro_literal(&mut self, out: &mut SourceWriter, l: &Literal) { + let was_writing_macro = self.writing_macro; + self.writing_macro = true; + self.write_literal(out, l); + self.writing_macro = was_writing_macro; + } + fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { // Override default method to open various blocs containing both globals and functions // these blocks are closed in [`write_functions`] that is also overridden @@ -1021,3 +1061,54 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } } + +/// Collect structurally unique field-level conditions needed by macro output. +fn collect_cfg_field_conditions(b: &Bindings, config: &Config) -> Vec { + let mut conditions = BTreeSet::new(); + + for item in &b.items { + if let ItemContainer::Struct(s) = item { + for constant in &s.associated_constants { + if constant_uses_macro(&constant.value, config) { + add_literal_cfg_conditions(&mut conditions, &constant.value, config); + } + } + } + } + + for constant in &b.constants { + if constant_uses_macro(&constant.value, config) { + add_literal_cfg_conditions(&mut conditions, &constant.value, config); + } + } + + conditions.into_iter().collect() +} + +fn constant_uses_macro(lit: &Literal, config: &Config) -> bool { + match config.language { + Language::C => true, + Language::Cxx => { + !config.constant.allow_static_const + && !(config.constant.allow_constexpr && lit.can_be_constexpr()) + } + Language::Cython => false, + } +} + +fn add_literal_cfg_conditions( + conditions: &mut BTreeSet, + lit: &Literal, + config: &Config, +) { + lit.visit(&mut |inner| { + if let Literal::Struct { fields, .. } = inner { + for field in fields.values() { + if let Some(condition) = field.cfg.to_condition(config) { + conditions.insert(condition); + } + } + } + true + }); +} diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs index 0e8f99f4..0f7df4f5 100644 --- a/src/bindgen/language_backend/mod.rs +++ b/src/bindgen/language_backend/mod.rs @@ -18,6 +18,7 @@ pub trait LanguageBackend: Sized { fn open_namespaces(&mut self, out: &mut SourceWriter); fn close_namespaces(&mut self, out: &mut SourceWriter); fn write_headers(&self, out: &mut SourceWriter, package_version: &str); + fn write_cfg_macros(&mut self, _out: &mut SourceWriter, _b: &Bindings) {} fn write_footers(&mut self, out: &mut SourceWriter); fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum); fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct); @@ -115,9 +116,13 @@ pub trait LanguageBackend: Sized { fn write_type(&mut self, out: &mut SourceWriter, t: &Type); fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation); fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal); + fn write_macro_literal(&mut self, out: &mut SourceWriter, l: &Literal) { + self.write_literal(out, l); + } fn write_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) { self.write_headers(out, &b.package_version); + self.write_cfg_macros(out, b); self.open_namespaces(out); self.write_primitive_constants(out, b); self.write_items(out, b); diff --git a/tests/expectations/cfg.c b/tests/expectations/cfg.c index 2cd41cea..ee5a3b13 100644 --- a/tests/expectations/cfg.c +++ b/tests/expectations/cfg.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if __STDC_VERSION__ >= 202311L @@ -123,8 +129,8 @@ typedef struct { #endif ; } ConditionalField; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } typedef struct { int32_t x; diff --git a/tests/expectations/cfg.compat.c b/tests/expectations/cfg.compat.c index 891e4ca0..e49073a6 100644 --- a/tests/expectations/cfg.compat.c +++ b/tests/expectations/cfg.compat.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if defined(__cplusplus) || __STDC_VERSION__ >= 202311L @@ -129,8 +135,8 @@ typedef struct { #endif ; } ConditionalField; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } typedef struct { int32_t x; diff --git a/tests/expectations/cfg_both.c b/tests/expectations/cfg_both.c index e0fd3b6b..b02a8afd 100644 --- a/tests/expectations/cfg_both.c +++ b/tests/expectations/cfg_both.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if __STDC_VERSION__ >= 202311L @@ -123,8 +129,8 @@ typedef struct ConditionalField { #endif ; } ConditionalField; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } typedef struct Normal { int32_t x; diff --git a/tests/expectations/cfg_both.compat.c b/tests/expectations/cfg_both.compat.c index b4bc7f7e..0e54721d 100644 --- a/tests/expectations/cfg_both.compat.c +++ b/tests/expectations/cfg_both.compat.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if defined(__cplusplus) || __STDC_VERSION__ >= 202311L @@ -129,8 +135,8 @@ typedef struct ConditionalField { #endif ; } ConditionalField; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } typedef struct Normal { int32_t x; diff --git a/tests/expectations/cfg_macro.compat.c b/tests/expectations/cfg_macro.compat.c new file mode 100644 index 00000000..07199c57 --- /dev/null +++ b/tests/expectations/cfg_macro.compat.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#if defined(PLATFORM_WIN) +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) +#endif +#if (defined(A) && defined(B_C)) +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) +#endif +#if (defined(A_B) && defined(C)) +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) +#endif + +typedef struct { +#if (defined(A) && defined(B_C)) + int32_t x +#endif + ; +#if (defined(A_B) && defined(C)) + int32_t y +#endif + ; +} Collision; + +typedef struct { + Collision collision; + const uint8_t *pointer; +} Nested; + +typedef struct { +#if defined(PLATFORM_WIN) + int32_t x +#endif + ; +} Foo; + +#define NESTED (Nested){ .collision = (Collision){ __CBINDGEN_CFG_A2_D1_A_D3_B_C(.x = 1,) __CBINDGEN_CFG_A2_D3_A_B_D1_C(.y = 2,) }, .pointer = (const uint8_t*)0 } + +#define FOO (Foo){ __CBINDGEN_CFG_D12_PLATFORM_WIN(.x = 0,) } diff --git a/tests/expectations/cfg_macro.cpp b/tests/expectations/cfg_macro.cpp new file mode 100644 index 00000000..9826a3c9 --- /dev/null +++ b/tests/expectations/cfg_macro.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +#if (defined(A) && defined(B_C)) +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) +#endif +#if (defined(A_B) && defined(C)) +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) +#endif + +struct Collision { +#if (defined(A) && defined(B_C)) + int32_t x +#endif + ; +#if (defined(A_B) && defined(C)) + int32_t y +#endif + ; +}; + +struct Nested { + Collision collision; + const uint8_t *pointer; +}; + +struct Foo { +#if defined(PLATFORM_WIN) + int32_t x +#endif + ; +}; + +#define NESTED Nested{ /* .collision = */ Collision{ __CBINDGEN_CFG_A2_D1_A_D3_B_C(/* .x = */ 1,) __CBINDGEN_CFG_A2_D3_A_B_D1_C(/* .y = */ 2,) }, /* .pointer = */ (const uint8_t*)0 } + +constexpr const Foo FOO = Foo{ +#if defined(PLATFORM_WIN) + /* .x = */ 0 +#endif +}; diff --git a/tests/expectations/cfg_macro.pyx b/tests/expectations/cfg_macro.pyx new file mode 100644 index 00000000..f03a0c13 --- /dev/null +++ b/tests/expectations/cfg_macro.pyx @@ -0,0 +1,22 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + ctypedef struct Collision: + int32_t x; + int32_t y; + + ctypedef struct Nested: + Collision collision; + const uint8_t *pointer; + + ctypedef struct Foo: + int32_t x; + + const Nested NESTED # = { { 1, 2 }, 0 } + + const Foo FOO # = { 0 } diff --git a/tests/expectations/cfg_macro_both.compat.c b/tests/expectations/cfg_macro_both.compat.c new file mode 100644 index 00000000..6c4c6e7c --- /dev/null +++ b/tests/expectations/cfg_macro_both.compat.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#if defined(PLATFORM_WIN) +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) +#endif +#if (defined(A) && defined(B_C)) +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) +#endif +#if (defined(A_B) && defined(C)) +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) +#endif + +typedef struct Collision { +#if (defined(A) && defined(B_C)) + int32_t x +#endif + ; +#if (defined(A_B) && defined(C)) + int32_t y +#endif + ; +} Collision; + +typedef struct Nested { + struct Collision collision; + const uint8_t *pointer; +} Nested; + +typedef struct Foo { +#if defined(PLATFORM_WIN) + int32_t x +#endif + ; +} Foo; + +#define NESTED (Nested){ .collision = (Collision){ __CBINDGEN_CFG_A2_D1_A_D3_B_C(.x = 1,) __CBINDGEN_CFG_A2_D3_A_B_D1_C(.y = 2,) }, .pointer = (const uint8_t*)0 } + +#define FOO (Foo){ __CBINDGEN_CFG_D12_PLATFORM_WIN(.x = 0,) } diff --git a/tests/expectations/cfg_macro_tag.compat.c b/tests/expectations/cfg_macro_tag.compat.c new file mode 100644 index 00000000..cf6a9362 --- /dev/null +++ b/tests/expectations/cfg_macro_tag.compat.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#if defined(PLATFORM_WIN) +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D12_PLATFORM_WIN(...) +#endif +#if (defined(A) && defined(B_C)) +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D1_A_D3_B_C(...) +#endif +#if (defined(A_B) && defined(C)) +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_A2_D3_A_B_D1_C(...) +#endif + +struct Collision { +#if (defined(A) && defined(B_C)) + int32_t x +#endif + ; +#if (defined(A_B) && defined(C)) + int32_t y +#endif + ; +}; + +struct Nested { + struct Collision collision; + const uint8_t *pointer; +}; + +struct Foo { +#if defined(PLATFORM_WIN) + int32_t x +#endif + ; +}; + +#define NESTED (Nested){ .collision = (Collision){ __CBINDGEN_CFG_A2_D1_A_D3_B_C(.x = 1,) __CBINDGEN_CFG_A2_D3_A_B_D1_C(.y = 2,) }, .pointer = (const uint8_t*)0 } + +#define FOO (Foo){ __CBINDGEN_CFG_D12_PLATFORM_WIN(.x = 0,) } diff --git a/tests/expectations/cfg_macro_tag.pyx b/tests/expectations/cfg_macro_tag.pyx new file mode 100644 index 00000000..f7247e0c --- /dev/null +++ b/tests/expectations/cfg_macro_tag.pyx @@ -0,0 +1,22 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef struct Collision: + int32_t x; + int32_t y; + + cdef struct Nested: + Collision collision; + const uint8_t *pointer; + + cdef struct Foo: + int32_t x; + + const Nested NESTED # = { { 1, 2 }, 0 } + + const Foo FOO # = { 0 } diff --git a/tests/expectations/cfg_tag.c b/tests/expectations/cfg_tag.c index aab076d6..2d47c4b1 100644 --- a/tests/expectations/cfg_tag.c +++ b/tests/expectations/cfg_tag.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if __STDC_VERSION__ >= 202311L @@ -123,8 +129,8 @@ struct ConditionalField { #endif ; }; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } struct Normal { int32_t x; diff --git a/tests/expectations/cfg_tag.compat.c b/tests/expectations/cfg_tag.compat.c index b10a46dc..b4567409 100644 --- a/tests/expectations/cfg_tag.compat.c +++ b/tests/expectations/cfg_tag.compat.c @@ -12,6 +12,12 @@ DEF M_32 = 0 #include #include +#if defined(X11) +#define __CBINDGEN_CFG_D3_X11(...) __VA_ARGS__ +#else +#define __CBINDGEN_CFG_D3_X11(...) +#endif + #if (defined(PLATFORM_UNIX) && defined(X11)) enum FooType #if defined(__cplusplus) || __STDC_VERSION__ >= 202311L @@ -129,8 +135,8 @@ struct ConditionalField { #endif ; }; -#define ConditionalField_ZERO (ConditionalField){ .field = 0 } -#define ConditionalField_ONE (ConditionalField){ .field = 1 } +#define ConditionalField_ZERO (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 0,) } +#define ConditionalField_ONE (ConditionalField){ __CBINDGEN_CFG_D3_X11(.field = 1,) } struct Normal { int32_t x; diff --git a/tests/rust/cfg_macro.rs b/tests/rust/cfg_macro.rs new file mode 100644 index 00000000..4658b767 --- /dev/null +++ b/tests/rust/cfg_macro.rs @@ -0,0 +1,34 @@ +#[repr(C)] +pub struct Collision { + #[cfg(all(a, b_c))] + pub x: i32, + #[cfg(all(a_b, c))] + pub y: i32, +} + +#[repr(C)] +pub struct Nested { + pub collision: Collision, + pub pointer: *const u8, +} + +pub const NESTED: Nested = Nested { + collision: Collision { + #[cfg(all(a, b_c))] + x: 1, + #[cfg(all(a_b, c))] + y: 2, + }, + pointer: 0 as *const u8, +}; + +#[repr(C)] +pub struct Foo { + #[cfg(windows)] + pub x: i32, +} + +pub const FOO: Foo = Foo { + #[cfg(windows)] + x: 0, +}; diff --git a/tests/rust/cfg_macro.toml b/tests/rust/cfg_macro.toml new file mode 100644 index 00000000..9675336e --- /dev/null +++ b/tests/rust/cfg_macro.toml @@ -0,0 +1,10 @@ +[defines] +"a" = "A" +"b_c" = "B_C" +"a_b" = "A_B" +"c" = "C" +"windows" = "PLATFORM_WIN" + +[const] +allow_static_const = false +allow_constexpr = true