diff --git a/src/bindgen/ir/cfg.rs b/src/bindgen/ir/cfg.rs index 07a2c83..062e31d 100644 --- a/src/bindgen/ir/cfg.rs +++ b/src/bindgen/ir/cfg.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::fmt; use std::io::Write; use syn; @@ -9,6 +10,7 @@ use syn; use bindgen::config::Config; use bindgen::writer::SourceWriter; +#[derive(PartialEq, Eq)] enum DefineKey<'a> { Boolean(&'a str), Named(&'a str, &'a str), @@ -52,6 +54,36 @@ pub enum Cfg { Not(Box), } +impl fmt::Display for Cfg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Cfg::Boolean(key) => write!(f, "{}", key), + Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value), + Cfg::Any(cfgs) => { + write!(f, "any(")?; + for (index, cfg) in cfgs.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", cfg)?; + } + write!(f, ")") + } + Cfg::All(cfgs) => { + write!(f, "all(")?; + for (index, cfg) in cfgs.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", cfg)?; + } + write!(f, ")") + } + Cfg::Not(cfg) => write!(f, "not({})", cfg), + } + } +} + impl Cfg { pub fn join(cfgs: &[Cfg]) -> Option { if cfgs.len() == 0 { @@ -166,139 +198,161 @@ impl Cfg { Some(configs) } } +} - fn has_defines(&self, config: &Config) -> bool { +pub trait ToCondition: Sized { + type Output; + + fn to_condition(self, config: &Config) -> Option; +} + +impl<'a> ToCondition for &'a Option { + type Output = Condition; + + fn to_condition(self, config: &Config) -> Option { + self.clone().and_then(|cfg| cfg.to_condition(config)) + } +} + +impl ToCondition for Option { + type Output = Condition; + + fn to_condition(self, config: &Config) -> Option { + self.and_then(|cfg| cfg.to_condition(config)) + } +} + +impl<'a> ToCondition for &'a Cfg { + type Output = Condition; + + fn to_condition(self, config: &Config) -> Option { + self.clone().to_condition(config) + } +} + +impl ToCondition for Cfg { + type Output = Condition; + + fn to_condition(self, config: &Config) -> Option { match self { - &Cfg::Boolean(ref cfg_name) => { - for (key, ..) in &config.defines { - let key = DefineKey::load(key); - - match key { - DefineKey::Boolean(key_name) => if cfg_name == key_name { - return true; - }, - DefineKey::Named(..) => {} - } + Cfg::Boolean(cfg_name) => { + let define = config + .defines + .iter() + .find(|(key, ..)| DefineKey::Boolean(&cfg_name) == DefineKey::load(key)); + if let Some((_, define)) = define { + Some(Condition::Define(define.to_owned())) + } else { + warn!( + "Missing `[defines]` entry for `{}` in cbindgen config.", + Cfg::Boolean(cfg_name) + ); + None } - - false } - &Cfg::Named(ref cfg_name, ref cfg_value) => { - for (key, ..) in &config.defines { - let key = DefineKey::load(key); - - match key { - DefineKey::Boolean(..) => {} - DefineKey::Named(key_name, key_value) => { - if cfg_name == key_name && cfg_value == key_value { - return true; - } - } - } + Cfg::Named(cfg_name, cfg_value) => { + let define = config.defines.iter().find(|(key, ..)| { + DefineKey::Named(&cfg_name, &cfg_value) == DefineKey::load(key) + }); + if let Some((_, define)) = define { + Some(Condition::Define(define.to_owned())) + } else { + warn!( + "Missing `[defines]` entry for `{}` in cbindgen config.", + Cfg::Named(cfg_name, cfg_value) + ); + None } - - false } - &Cfg::Any(ref cfgs) => cfgs.iter().all(|x| x.has_defines(config)), - &Cfg::All(ref cfgs) => cfgs.iter().all(|x| x.has_defines(config)), - &Cfg::Not(ref cfg) => cfg.has_defines(config), + Cfg::Any(children) => { + let conditions: Vec<_> = children + .into_iter() + .filter_map(|x| x.to_condition(config)) + .collect(); + match conditions.len() { + 0 => None, + 1 => conditions.into_iter().next(), + _ => Some(Condition::Any(conditions)), + } + } + Cfg::All(children) => { + let cfgs: Vec<_> = children + .into_iter() + .filter_map(|x| x.to_condition(config)) + .collect(); + match cfgs.len() { + 0 => None, + 1 => cfgs.into_iter().next(), + _ => Some(Condition::All(cfgs)), + } + } + Cfg::Not(child) => child + .to_condition(config) + .map(|cfg| Condition::Not(Box::new(cfg))), } } +} - fn write_condition(&self, config: &Config, out: &mut SourceWriter) { +#[derive(Debug, Clone)] +pub enum Condition { + Define(String), + Any(Vec), + All(Vec), + Not(Box), +} + +impl Condition { + fn write(&self, config: &Config, out: &mut SourceWriter) { match self { - &Cfg::Boolean(ref cfg_name) => { - let mut define: &str = cfg_name; - - for (key, define_value) in &config.defines { - let key = DefineKey::load(key); - - match key { - DefineKey::Boolean(key_name) => if cfg_name == key_name { - define = define_value; - }, - DefineKey::Named(..) => {} - } - } - + &Condition::Define(ref define) => { out.write("defined("); write!(out, "{}", define); out.write(")"); } - &Cfg::Named(ref cfg_name, ref cfg_value) => { - let mut define: &str = cfg_name; - - for (key, define_value) in &config.defines { - let key = DefineKey::load(key); - - match key { - DefineKey::Boolean(..) => {} - DefineKey::Named(key_name, key_value) => { - if cfg_name == key_name && cfg_value == key_value { - define = define_value; - } - } - } - } - - out.write("defined("); - write!(out, "{}", define); - out.write(")"); - } - &Cfg::Any(ref cfgs) => { + &Condition::Any(ref conditions) => { out.write("("); - for (i, cfg) in cfgs.iter().enumerate() { + for (i, condition) in conditions.iter().enumerate() { if i != 0 { out.write(" || "); } - cfg.write_condition(config, out); + condition.write(config, out); } out.write(")"); } - &Cfg::All(ref cfgs) => { + &Condition::All(ref conditions) => { out.write("("); - for (i, cfg) in cfgs.iter().enumerate() { + for (i, condition) in conditions.iter().enumerate() { if i != 0 { out.write(" && "); } - cfg.write_condition(config, out); + condition.write(config, out); } out.write(")"); } - &Cfg::Not(ref cfg) => { + &Condition::Not(ref condition) => { out.write("!"); - cfg.write_condition(config, out); + condition.write(config, out); } } } } -pub trait CfgWrite { +pub trait ConditionWrite { fn write_before(&self, config: &Config, out: &mut SourceWriter); - fn write_after(&self, config: &Config, out: &mut SourceWriter); } -impl CfgWrite for Option { +impl ConditionWrite for Option { fn write_before(&self, config: &Config, out: &mut SourceWriter) { if let &Some(ref cfg) = self { - if !cfg.has_defines(config) { - return; - } - out.write("#if "); - cfg.write_condition(config, out); + cfg.write(config, out); out.new_line(); } } - fn write_after(&self, config: &Config, out: &mut SourceWriter) { - if let &Some(ref cfg) = self { - // TODO - if !cfg.has_defines(config) { - return; - } - + fn write_after(&self, _config: &Config, out: &mut SourceWriter) { + if self.is_some() { out.new_line(); out.write("#endif"); } diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index bf3f4ac..fe41631 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -9,7 +9,9 @@ use syn; use bindgen::config::{Config, Language}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Item, ItemContainer, Type}; +use bindgen::ir::{ + AnnotationSet, Cfg, ConditionWrite, Documentation, Item, ItemContainer, ToCondition, Type, +}; use bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] @@ -137,7 +139,8 @@ impl Item for Constant { impl Source for Constant { fn write(&self, config: &Config, out: &mut SourceWriter) { - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + condition.write_before(config, out); if config.constant.allow_static_const && config.language == Language::Cxx { if let Type::ConstPtr(..) = self.ty { out.write("static "); @@ -149,6 +152,6 @@ impl Source for Constant { } else { write!(out, "#define {} {}", self.name, self.value.0) } - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 4d450ae..73f91d9 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -10,8 +10,8 @@ use bindgen::config::{Config, Language}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; use bindgen::ir::{ - AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, GenericPath, Item, ItemContainer, - Repr, ReprStyle, ReprType, Struct, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, GenericPath, Item, + ItemContainer, Repr, ReprStyle, ReprType, Struct, ToCondition, Type, }; use bindgen::library::Library; use bindgen::rename::{IdentifierType, RenameRule}; @@ -317,7 +317,9 @@ impl Source for Enum { ReprType::I8 => "int8_t", }); - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + + condition.write_before(config, out); self.documentation.write(config, out); @@ -543,6 +545,6 @@ impl Source for Enum { } } - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index 4feac88..8d82365 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -10,7 +10,9 @@ use bindgen::cdecl; use bindgen::config::{Config, Language, Layout}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; -use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, PrimitiveType, Type}; +use bindgen::ir::{ + AnnotationSet, Cfg, ConditionWrite, Documentation, PrimitiveType, ToCondition, Type, +}; use bindgen::library::Library; use bindgen::monomorph::Monomorphs; use bindgen::rename::{IdentifierType, RenameRule}; @@ -126,7 +128,8 @@ impl Source for Function { let prefix = config.function.prefix(&func.annotations); let postfix = config.function.postfix(&func.annotations); - func.cfg.write_before(config, out); + let condition = (&func.cfg).to_condition(config); + condition.write_before(config, out); func.documentation.write(config, out); @@ -147,7 +150,7 @@ impl Source for Function { } out.write(";"); - func.cfg.write_after(config, out); + condition.write_after(config, out); } fn write_2(func: &Function, config: &Config, out: &mut SourceWriter) { @@ -155,7 +158,9 @@ impl Source for Function { let prefix = config.function.prefix(&func.annotations); let postfix = config.function.postfix(&func.annotations); - func.cfg.write_before(config, out); + let condition = (&func.cfg).to_condition(config); + + condition.write_before(config, out); func.documentation.write(config, out); @@ -176,7 +181,7 @@ impl Source for Function { } out.write(";"); - func.cfg.write_after(config, out); + condition.write_after(config, out); }; let option_1 = out.measure(|out| write_1(self, config, out)); diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index cddcb1b..0e4cdc8 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -10,7 +10,8 @@ use bindgen::config::{Config, Language}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; use bindgen::ir::{ - AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer, Path, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, + ToCondition, Type, }; use bindgen::library::Library; use bindgen::mangle; @@ -107,7 +108,8 @@ impl Item for OpaqueItem { impl Source for OpaqueItem { fn write(&self, config: &Config, out: &mut SourceWriter) { - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + condition.write_before(config, out); self.documentation.write(config, out); @@ -119,6 +121,6 @@ impl Source for OpaqueItem { write!(out, "struct {};", self.name); } - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index b597805..883e495 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -10,7 +10,8 @@ use bindgen::config::{Config, Language}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; use bindgen::ir::{ - AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer, Repr, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Repr, + ToCondition, Type, }; use bindgen::library::Library; use bindgen::mangle; @@ -234,7 +235,8 @@ impl Item for Struct { impl Source for Struct { fn write(&self, config: &Config, out: &mut SourceWriter) { - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + condition.write_before(config, out); self.documentation.write(config, out); @@ -396,7 +398,7 @@ impl Source for Struct { out.close_brace(true); } - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index 121b1bf..c1524da 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -11,7 +11,8 @@ use bindgen::config::{Config, Language}; use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; use bindgen::ir::{ - AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer, Path, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, + ToCondition, Type, }; use bindgen::library::Library; use bindgen::mangle; @@ -168,7 +169,8 @@ impl Item for Typedef { impl Source for Typedef { fn write(&self, config: &Config, out: &mut SourceWriter) { - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + condition.write_before(config, out); self.documentation.write(config, out); @@ -183,6 +185,6 @@ impl Source for Typedef { } out.write(";"); - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs index 154b6b1..098d7ec 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -11,7 +11,8 @@ use bindgen::declarationtyperesolver::DeclarationTypeResolver; use bindgen::dependencies::Dependencies; use bindgen::ir::SynFieldHelpers; use bindgen::ir::{ - AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, Item, ItemContainer, Repr, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Repr, + ToCondition, Type, }; use bindgen::library::Library; use bindgen::mangle; @@ -220,7 +221,8 @@ impl Item for Union { impl Source for Union { fn write(&self, config: &Config, out: &mut SourceWriter) { - self.cfg.write_before(config, out); + let condition = (&self.cfg).to_condition(config); + condition.write_before(config, out); self.documentation.write(config, out); @@ -265,6 +267,6 @@ impl Source for Union { out.close_brace(true); } - self.cfg.write_after(config, out); + condition.write_after(config, out); } } diff --git a/tests/expectations/both/mod_attr.c b/tests/expectations/both/mod_attr.c new file mode 100644 index 0000000..f271973 --- /dev/null +++ b/tests/expectations/both/mod_attr.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#if defined(BAR) +#define BAR 2 +#endif + +#if defined(FOO) +#define FOO 1 +#endif + +#if defined(BAR) +typedef struct Bar { + +} Bar; +#endif + +#if defined(FOO) +typedef struct Foo { + +} Foo; +#endif + +#if defined(BAR) +void bar(const Bar *bar); +#endif + +#if defined(FOO) +void foo(const Foo *foo); +#endif diff --git a/tests/expectations/mod_attr.c b/tests/expectations/mod_attr.c new file mode 100644 index 0000000..b5c648c --- /dev/null +++ b/tests/expectations/mod_attr.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#if defined(BAR) +#define BAR 2 +#endif + +#if defined(FOO) +#define FOO 1 +#endif + +#if defined(BAR) +typedef struct { + +} Bar; +#endif + +#if defined(FOO) +typedef struct { + +} Foo; +#endif + +#if defined(BAR) +void bar(const Bar *bar); +#endif + +#if defined(FOO) +void foo(const Foo *foo); +#endif diff --git a/tests/expectations/mod_attr.cpp b/tests/expectations/mod_attr.cpp new file mode 100644 index 0000000..14de059 --- /dev/null +++ b/tests/expectations/mod_attr.cpp @@ -0,0 +1,34 @@ +#include +#include + +#if defined(BAR) +static const int32_t BAR = 2; +#endif + +#if defined(FOO) +static const int32_t FOO = 1; +#endif + +#if defined(BAR) +struct Bar { + +}; +#endif + +#if defined(FOO) +struct Foo { + +}; +#endif + +extern "C" { + +#if defined(BAR) +void bar(const Bar *bar); +#endif + +#if defined(FOO) +void foo(const Foo *foo); +#endif + +} // extern "C" diff --git a/tests/expectations/tag/mod_attr.c b/tests/expectations/tag/mod_attr.c new file mode 100644 index 0000000..6d7113f --- /dev/null +++ b/tests/expectations/tag/mod_attr.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#if defined(BAR) +#define BAR 2 +#endif + +#if defined(FOO) +#define FOO 1 +#endif + +#if defined(BAR) +struct Bar { + +}; +#endif + +#if defined(FOO) +struct Foo { + +}; +#endif + +#if defined(BAR) +void bar(const struct Bar *bar); +#endif + +#if defined(FOO) +void foo(const struct Foo *foo); +#endif diff --git a/tests/rust/mod_attr/Cargo.lock b/tests/rust/mod_attr/Cargo.lock new file mode 100644 index 0000000..5506a83 --- /dev/null +++ b/tests/rust/mod_attr/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "mod_attr" +version = "0.1.0" + diff --git a/tests/rust/mod_attr/Cargo.toml b/tests/rust/mod_attr/Cargo.toml new file mode 100644 index 0000000..9966829 --- /dev/null +++ b/tests/rust/mod_attr/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mod_attr" +version = "0.1.0" +authors = ["cbindgen"] + +[lib] +name = "mod_attr" +crate-type = ["lib", "dylib"] + +[features] +foobar = [] diff --git a/tests/rust/mod_attr/cbindgen.toml b/tests/rust/mod_attr/cbindgen.toml new file mode 100644 index 0000000..0e04673 --- /dev/null +++ b/tests/rust/mod_attr/cbindgen.toml @@ -0,0 +1,4 @@ +[defines] +"foo" = "FOO" +"bar" = "BAR" +# "feature = foobar" = "FOOBAR" diff --git a/tests/rust/mod_attr/src/lib.rs b/tests/rust/mod_attr/src/lib.rs new file mode 100644 index 0000000..63cef35 --- /dev/null +++ b/tests/rust/mod_attr/src/lib.rs @@ -0,0 +1,24 @@ +#[cfg(foo)] +pub const FOO: i32 = 1; + +#[cfg(foo)] +#[no_mangle] +pub unsafe extern "C" fn foo(foo: &Foo) {} + +#[cfg(foo)] +#[repr(C)] +pub struct Foo {} + +#[cfg(feature = "foobar")] +pub mod foo { + #[cfg(bar)] + pub const BAR: i32 = 2; + + #[cfg(bar)] + #[no_mangle] + pub unsafe extern "C" fn bar(bar: &Bar) {} + + #[cfg(bar)] + #[repr(C)] + pub struct Bar {} +}