From 60d95258ba0e5beb2b6bbca385b93903c8d7ab37 Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Thu, 4 Jan 2018 23:29:21 -0600 Subject: [PATCH] Add item renaming, prefixing, force including, and excluding --- README.md | 13 ++++++++ src/bindgen/builder.rs | 24 ++++++++++++++ src/bindgen/config.rs | 42 ++++++++++++++++++++++++ src/bindgen/ir/constant.rs | 4 +++ src/bindgen/ir/enumeration.rs | 2 ++ src/bindgen/ir/function.rs | 5 +++ src/bindgen/ir/global.rs | 4 +++ src/bindgen/ir/item.rs | 5 +++ src/bindgen/ir/opaque.rs | 4 +++ src/bindgen/ir/structure.rs | 9 ++++-- src/bindgen/ir/ty.rs | 27 ++++++++++++++++ src/bindgen/ir/typedef.rs | 5 +++ src/bindgen/ir/union.rs | 5 +++ src/bindgen/library.rs | 50 ++++++++++++++++++++++++++++- tests/expectations/include_item.c | 12 +++++++ tests/expectations/include_item.cpp | 15 +++++++++ tests/expectations/rename.c | 31 ++++++++++++++++++ tests/expectations/rename.cpp | 33 +++++++++++++++++++ tests/rust/include_item.rs | 10 ++++++ tests/rust/include_item.toml | 2 ++ tests/rust/rename.rs | 45 ++++++++++++++++++++++++++ tests/rust/rename.toml | 5 +++ 22 files changed, 349 insertions(+), 3 deletions(-) create mode 100644 tests/expectations/include_item.c create mode 100644 tests/expectations/include_item.cpp create mode 100644 tests/expectations/rename.c create mode 100644 tests/expectations/rename.cpp create mode 100644 tests/rust/include_item.rs create mode 100644 tests/rust/include_item.toml create mode 100644 tests/rust/rename.rs create mode 100644 tests/rust/rename.toml diff --git a/README.md b/README.md index dab0371..83dee3a 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,19 @@ exclude = ["libc"] # parsing to expand any macros expand = ["euclid"] +[export] +# A list of additional items not used by exported functions to include in +# the generated bindings +include = ["Foo", "Bar"] +# A list of items to not include in the generated bindings +exclude = ["Bad"] +# A prefix to add before the name of every item +prefix = "CAPI_" + +# Table of name conversions to apply to item names +[export.rename] +"Struct" = "CAPI_Struct" + [fn] # An optional prefix to put before every function declaration prefix = "string" diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index 5c06da6..39995ae 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -115,6 +115,30 @@ impl Builder { self } + #[allow(unused)] + pub fn include_item>(mut self, item_name: S) -> Builder { + self.config.export.include.push(String::from(item_name.as_ref())); + self + } + + #[allow(unused)] + pub fn exclude_item>(mut self, item_name: S) -> Builder { + self.config.export.exclude.push(String::from(item_name.as_ref())); + self + } + + #[allow(unused)] + pub fn rename_item>(mut self, from: S, to: S) -> Builder { + self.config.export.rename.insert(String::from(from.as_ref()), String::from(to.as_ref())); + self + } + + #[allow(unused)] + pub fn with_item_prefix>(mut self, prefix: S) -> Builder { + self.config.export.prefix = Some(String::from(prefix.as_ref())); + self + } + #[allow(unused)] pub fn with_parse_deps(mut self, parse_deps: bool) -> Builder { self.config.parse.parse_deps = parse_deps; diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index c1ad525..8d11eae 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -95,6 +95,45 @@ impl FromStr for Layout { deserialize_enum_str!(Layout); +/// Settings to apply when exporting items. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct ExportConfig { + /// A list of additional items not used by exported functions to include in + /// the generated bindings + pub include: Vec, + /// A list of items to not include in the generated bindings + pub exclude: Vec, + /// Table of name conversions to apply to item names + pub rename: HashMap, + /// A prefix to add before the name of every item + pub prefix: Option, +} + +impl Default for ExportConfig { + fn default() -> ExportConfig { + ExportConfig { + include: Vec::new(), + exclude: Vec::new(), + rename: HashMap::new(), + prefix: None, + } + } +} + +impl ExportConfig { + pub(crate) fn rename(&self, item_name: &mut String) { + if let Some(name) = self.rename.get(item_name) { + *item_name = name.clone(); + } + if let Some(ref prefix) = self.prefix { + item_name.insert_str(0, &prefix); + } + } +} + /// Settings to apply to generated functions. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] @@ -333,6 +372,8 @@ pub struct Config { pub language: Language, /// The configuration options for parsing pub parse: ParseConfig, + /// The configuration options for exporting + pub export: ExportConfig, /// The configuration options for functions #[serde(rename = "fn")] pub function: FunctionConfig, @@ -368,6 +409,7 @@ impl Default for Config { tab_width: 2, language: Language::Cxx, parse: ParseConfig::default(), + export: ExportConfig::default(), function: FunctionConfig::default(), structure: StructConfig::default(), enumeration: EnumConfig::default(), diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 2dc9213..c54ec9d 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -110,6 +110,10 @@ impl Item for Constant { fn container(&self) -> ItemContainer { ItemContainer::Constant(self.clone()) } + + fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.name); + } } impl Source for Constant { diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 2f7944f..79b7dc8 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -109,6 +109,8 @@ impl Item for Enum { } fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.name); + if config.language == Language::C && (config.enumeration.prefix_with_name || self.annotations.bool("prefix-with-name").unwrap_or(false)) diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index 7cc9453..58b0357 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -78,6 +78,11 @@ impl Function { } pub fn rename_for_config(&mut self, config: &Config) { + self.ret.rename_for_config(config); + for &mut (_, ref mut ty) in &mut self.args { + ty.rename_for_config(config); + } + let rules = [ self.annotations.parse_atom::("rename-all"), config.function.rename_args, diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs index f193853..8fa56f0 100644 --- a/src/bindgen/ir/global.rs +++ b/src/bindgen/ir/global.rs @@ -75,6 +75,10 @@ impl Item for Static { ItemContainer::Static(self.clone()) } + fn rename_for_config(&mut self, config: &Config) { + self.ty.rename_for_config(config); + } + fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ty.add_dependencies(library, out); } diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs index 10f3346..9960f82 100644 --- a/src/bindgen/ir/item.rs +++ b/src/bindgen/ir/item.rs @@ -76,6 +76,11 @@ impl ItemMap { } } + pub fn rebuild(&mut self) { + let old = mem::replace(self, ItemMap::new()); + old.for_all_items(|x| { self.try_insert(x.clone()); }); + } + pub fn try_insert(&mut self, item: T) -> bool { match (item.cfg().is_some(), self.data.get_mut(item.name())) { (true, Some(&mut ItemValue::Cfg(ref mut items))) => { diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index 29ab940..70e31e9 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -62,6 +62,10 @@ impl Item for OpaqueItem { ItemContainer::OpaqueItem(self.clone()) } + fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.name); + } + fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {} fn instantiate_monomorph( diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 604b2db..99c46ad 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -121,7 +121,12 @@ impl Item for Struct { } fn rename_for_config(&mut self, config: &Config) { - let rules = [ + config.export.rename(&mut self.name); + for &mut (_, ref mut ty, _) in &mut self.fields { + ty.rename_for_config(config); + } + + let field_rules = [ self.annotations.parse_atom::("rename-all"), config.structure.rename_fields, ]; @@ -138,7 +143,7 @@ impl Item for Struct { } self.fields = overriden_fields; - } else if let Some(r) = find_first_some(&rules) { + } else if let Some(r) = find_first_some(&field_rules) { self.fields = self.fields .iter() .map(|x| { diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index bf583fa..0ff9115 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -443,6 +443,33 @@ impl Type { } } + pub fn rename_for_config(&mut self, config: &Config) { + match self { + &mut Type::ConstPtr(ref mut ty) => { + ty.rename_for_config(config); + } + &mut Type::Ptr(ref mut ty) => { + ty.rename_for_config(config); + } + &mut Type::Path(ref mut path) => { + for generic in &mut path.generics { + generic.rename_for_config(config); + } + config.export.rename(&mut path.name); + } + &mut Type::Primitive(_) => {} + &mut Type::Array(ref mut ty, _) => { + ty.rename_for_config(config); + } + &mut Type::FuncPtr(ref mut ret, ref mut args) => { + ret.rename_for_config(config); + for arg in args { + arg.rename_for_config(config); + } + } + } + } + pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) { match self { &mut Type::ConstPtr(ref mut ty) => { diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index 3f0b004..61d2b47 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -115,6 +115,11 @@ impl Item for Typedef { ItemContainer::Typedef(self.clone()) } + fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.name); + self.aliased.rename_for_config(config); + } + fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.aliased .add_dependencies_ignoring_generics(&self.generic_params, library, out); diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs index 8033b4f..584f3f3 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -122,6 +122,11 @@ impl Item for Union { } fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.name); + for &mut (_, ref mut ty, _) in &mut self.fields { + ty.rename_for_config(config); + } + let rules = [ self.annotations.parse_atom::("rename-all"), config.structure.rename_fields, diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 1d61ef4..a6bad6d 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -52,8 +52,8 @@ impl Library { } pub fn generate(mut self) -> Result { + self.remove_excluded(); self.functions.sort_by(|x, y| x.name.cmp(&y.name)); - self.transfer_annotations(); self.rename_items(); self.simplify_option_to_ptr(); @@ -70,6 +70,20 @@ impl Library { self.globals.for_all_items(|global| { global.add_dependencies(&self, &mut dependencies); }); + for name in &self.config.export.include { + if let Some(items) = self.get_items(name) { + if !dependencies.items.contains(name) { + dependencies.items.insert(name.clone()); + + for item in &items { + item.deref().add_dependencies(&self, &mut dependencies); + } + for item in items { + dependencies.order.push(item); + } + } + } + } dependencies.sort(); @@ -107,6 +121,18 @@ impl Library { None } + fn remove_excluded(&mut self) { + let config = &self.config; + self.functions.retain(|x| !config.export.exclude.contains(&x.name)); + self.enums.filter(|x| config.export.exclude.contains(&x.name)); + self.structs.filter(|x| config.export.exclude.contains(&x.name)); + self.unions.filter(|x| config.export.exclude.contains(&x.name)); + self.opaque_items.filter(|x| config.export.exclude.contains(&x.name)); + self.typedefs.filter(|x| config.export.exclude.contains(&x.name)); + self.globals.filter(|x| config.export.exclude.contains(&x.name)); + self.constants.filter(|x| config.export.exclude.contains(&x.name)); + } + fn transfer_annotations(&mut self) { let mut annotations = HashMap::new(); @@ -198,12 +224,34 @@ impl Library { fn rename_items(&mut self) { let config = &self.config; + + self.globals + .for_all_items_mut(|x| x.rename_for_config(config)); + self.globals.rebuild(); + + self.constants + .for_all_items_mut(|x| x.rename_for_config(config)); + self.constants.rebuild(); + self.structs .for_all_items_mut(|x| x.rename_for_config(config)); + self.structs.rebuild(); + self.unions .for_all_items_mut(|x| x.rename_for_config(config)); + self.unions.rebuild(); + self.enums .for_all_items_mut(|x| x.rename_for_config(config)); + self.enums.rebuild(); + + self.opaque_items + .for_all_items_mut(|x| x.rename_for_config(config)); + self.opaque_items.rebuild(); + + self.typedefs + .for_all_items_mut(|x| x.rename_for_config(config)); + self.typedefs.rebuild(); for item in &mut self.functions { item.rename_for_config(&self.config); diff --git a/tests/expectations/include_item.c b/tests/expectations/include_item.c new file mode 100644 index 0000000..d394d00 --- /dev/null +++ b/tests/expectations/include_item.c @@ -0,0 +1,12 @@ +#include +#include +#include + +typedef struct { + int32_t x; + float y; +} A; + +typedef struct { + A data; +} B; diff --git a/tests/expectations/include_item.cpp b/tests/expectations/include_item.cpp new file mode 100644 index 0000000..268e44d --- /dev/null +++ b/tests/expectations/include_item.cpp @@ -0,0 +1,15 @@ +#include +#include + +struct A { + int32_t x; + float y; +}; + +struct B { + A data; +}; + +extern "C" { + +} // extern "C" diff --git a/tests/expectations/rename.c b/tests/expectations/rename.c new file mode 100644 index 0000000..c3fa160 --- /dev/null +++ b/tests/expectations/rename.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#define C_H 10 + +enum C_E { + x = 0, + y = 1, +}; +typedef uint8_t C_E; + +typedef struct C_A C_A; + +typedef struct C_C C_C; + +typedef struct { + int32_t x; + float y; +} C_AwesomeB; + +typedef union { + int32_t x; + float y; +} C_D; + +typedef C_A C_F; + +extern const int32_t G; + +void root(const C_A *a, C_AwesomeB b, C_C c, C_D d, C_E e, C_F f); diff --git a/tests/expectations/rename.cpp b/tests/expectations/rename.cpp new file mode 100644 index 0000000..f8ba1cc --- /dev/null +++ b/tests/expectations/rename.cpp @@ -0,0 +1,33 @@ +#include +#include + +static const int32_t C_H = 10; + +enum class C_E : uint8_t { + x = 0, + y = 1, +}; + +struct C_A; + +struct C_C; + +struct C_AwesomeB { + int32_t x; + float y; +}; + +union C_D { + int32_t x; + float y; +}; + +using C_F = C_A; + +extern "C" { + +extern const int32_t G; + +void root(const C_A *a, C_AwesomeB b, C_C c, C_D d, C_E e, C_F f); + +} // extern "C" diff --git a/tests/rust/include_item.rs b/tests/rust/include_item.rs new file mode 100644 index 0000000..2eb5a9e --- /dev/null +++ b/tests/rust/include_item.rs @@ -0,0 +1,10 @@ +#[repr(C)] +struct A { + x: i32, + y: f32, +} + +#[repr(C)] +struct B { + data: A, +} diff --git a/tests/rust/include_item.toml b/tests/rust/include_item.toml new file mode 100644 index 0000000..1dc6f25 --- /dev/null +++ b/tests/rust/include_item.toml @@ -0,0 +1,2 @@ +[export] +include = ["B"] diff --git a/tests/rust/rename.rs b/tests/rust/rename.rs new file mode 100644 index 0000000..b5e3004 --- /dev/null +++ b/tests/rust/rename.rs @@ -0,0 +1,45 @@ +struct A { + x: i32, + y: f32, +} + +#[repr(C)] +struct B { + x: i32, + y: f32, +} + +union C { + x: i32, + y: f32, +} + +#[repr(C)] +union D { + x: i32, + y: f32, +} + +#[repr(u8)] +enum E { + x = 0, + y = 1, +} + +type F = A; + +#[no_mangle] +pub static G: i32 = 10; + +const H: i32 = 10; + +#[no_mangle] +pub extern "C" fn root( + a: *const A, + b: B, + c: C, + d: D, + e: E, + f: F, +) { } + diff --git a/tests/rust/rename.toml b/tests/rust/rename.toml new file mode 100644 index 0000000..1b76559 --- /dev/null +++ b/tests/rust/rename.toml @@ -0,0 +1,5 @@ +[export] +prefix = "C_" + +[export.rename] +"B" = "AwesomeB"