From 83346c9b210fc601053fc468392c6207b00a3331 Mon Sep 17 00:00:00 2001 From: Thibaut Lorrain Date: Fri, 17 Nov 2023 11:58:46 +0100 Subject: [PATCH] add write_bindings to LanguageBackend for more flexible LB impls --- src/bindgen/bindings.rs | 135 +-------------- src/bindgen/cdecl.rs | 12 +- src/bindgen/ir/constant.rs | 4 +- src/bindgen/ir/enumeration.rs | 16 +- src/bindgen/ir/generic_path.rs | 4 +- src/bindgen/language_backend/clike.rs | 218 +++++++++++++++++-------- src/bindgen/language_backend/cython.rs | 69 ++++---- src/bindgen/language_backend/mod.rs | 148 ++++++++++++++--- src/bindgen/writer.rs | 10 +- 9 files changed, 347 insertions(+), 269 deletions(-) diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 329860ddf..319522c6a 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -16,7 +16,7 @@ use crate::bindgen::ir::{ Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef, }; use crate::bindgen::language_backend::{ - CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, NamespaceOperation, + CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, }; use crate::bindgen::writer::SourceWriter; @@ -28,10 +28,10 @@ pub struct Bindings { struct_map: ItemMap, typedef_map: ItemMap, struct_fileds_memo: RefCell>>>, - globals: Vec, - constants: Vec, - items: Vec, - functions: Vec, + pub globals: Vec, + pub constants: Vec, + pub items: Vec, + pub functions: Vec, source_files: Vec, /// Bindings are generated by a recursive call to cbindgen /// and shouldn't do anything when written anywhere. @@ -202,10 +202,10 @@ impl Bindings { pub fn write(&self, file: F) { match self.config.language { Language::Cxx | Language::C => { - self.write_with_backend(file, &CLikeLanguageBackend::new(&self.config)) + self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config)) } Language::Cython => { - self.write_with_backend(file, &CythonLanguageBackend::new(&self.config)) + self.write_with_backend(file, &mut CythonLanguageBackend::new(&self.config)) } } } @@ -213,7 +213,7 @@ impl Bindings { pub fn write_with_backend( &self, file: F, - language_backend: &LB, + language_backend: &mut LB, ) { if self.noop { return; @@ -221,123 +221,6 @@ impl Bindings { let mut out = SourceWriter::new(file, self); - language_backend.write_headers(&mut out); - - language_backend.open_close_namespaces(NamespaceOperation::Open, &mut out); - - for constant in &self.constants { - if constant.uses_only_primitive_types() { - out.new_line_if_not_start(); - constant.write(&self.config, language_backend, &mut out, None); - out.new_line(); - } - } - - for item in &self.items { - if item - .deref() - .annotations() - .bool("no-export") - .unwrap_or(false) - { - continue; - } - - out.new_line_if_not_start(); - match *item { - ItemContainer::Constant(..) => unreachable!(), - ItemContainer::Static(..) => unreachable!(), - ItemContainer::Enum(ref x) => language_backend.write_enum(&mut out, x), - ItemContainer::Struct(ref x) => language_backend.write_struct(&mut out, x), - ItemContainer::Union(ref x) => language_backend.write_union(&mut out, x), - ItemContainer::OpaqueItem(ref x) => language_backend.write_opaque_item(&mut out, x), - ItemContainer::Typedef(ref x) => language_backend.write_type_def(&mut out, x), - } - out.new_line(); - } - - for constant in &self.constants { - if !constant.uses_only_primitive_types() { - out.new_line_if_not_start(); - constant.write(&self.config, language_backend, &mut out, None); - out.new_line(); - } - } - - if !self.functions.is_empty() || !self.globals.is_empty() { - if self.config.cpp_compatible_c() { - out.new_line_if_not_start(); - out.write("#ifdef __cplusplus"); - } - - if self.config.language == Language::Cxx { - if let Some(ref using_namespaces) = self.config.using_namespaces { - for namespace in using_namespaces { - out.new_line(); - write!(out, "using namespace {};", namespace); - } - out.new_line(); - } - } - - if self.config.language == Language::Cxx || self.config.cpp_compatible_c() { - out.new_line(); - out.write("extern \"C\" {"); - out.new_line(); - } - - if self.config.cpp_compatible_c() { - out.write("#endif // __cplusplus"); - out.new_line(); - } - - for global in &self.globals { - out.new_line_if_not_start(); - language_backend.write_static(&mut out, global); - out.new_line(); - } - - for function in &self.functions { - out.new_line_if_not_start(); - language_backend.write_function(&mut out, function); - out.new_line(); - } - - if self.config.cpp_compatible_c() { - out.new_line(); - out.write("#ifdef __cplusplus"); - } - - if self.config.language == Language::Cxx || self.config.cpp_compatible_c() { - out.new_line(); - out.write("} // extern \"C\""); - out.new_line(); - } - - if self.config.cpp_compatible_c() { - out.write("#endif // __cplusplus"); - out.new_line(); - } - } - - if self.config.language == Language::Cython - && self.globals.is_empty() - && self.constants.is_empty() - && self.items.is_empty() - && self.functions.is_empty() - { - out.write("pass"); - } - - language_backend.open_close_namespaces(NamespaceOperation::Close, &mut out); - - language_backend.write_footers(&mut out); - if let Some(ref f) = self.config.trailer { - out.new_line_if_not_start(); - write!(out, "{}", f); - if !f.ends_with('\n') { - out.new_line(); - } - } + language_backend.write_bindings(&mut out, self); } } diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index 836453c1e..c4bef9346 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -194,7 +194,7 @@ impl CDecl { fn write( &self, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, ident: Option<&str>, config: &Config, @@ -305,7 +305,7 @@ impl CDecl { } fn write_vertical( - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], @@ -327,7 +327,7 @@ impl CDecl { } fn write_horizontal( - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], @@ -372,7 +372,7 @@ impl CDecl { } pub fn write_func( - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, f: &Function, layout: Layout, @@ -382,7 +382,7 @@ pub fn write_func( } pub fn write_field( - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, t: &Type, ident: &str, @@ -392,7 +392,7 @@ pub fn write_field( } pub fn write_type( - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, t: &Type, config: &Config, diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 3f2473f31..c563054a2 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -595,7 +595,7 @@ impl Constant { pub fn write_declaration( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: &Struct, ) { @@ -617,7 +617,7 @@ impl Constant { pub fn write( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, associated_to_struct: Option<&Struct>, ) { diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 6eccfe85d..a52fd7044 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -637,11 +637,11 @@ impl Enum { pub(crate) fn write_tag_enum< F: Write, LB: LanguageBackend, - WV: Fn(&LB, &mut SourceWriter, &EnumVariant), + WV: Fn(&mut LB, &mut SourceWriter, &EnumVariant), >( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, size: Option<&str>, write_variant: WV, @@ -809,7 +809,7 @@ impl Enum { pub(crate) fn write_variant_defs( &self, config: &Config, - language_backend: &LB, // TODO probably need only one of Config/LanguageBackend + language_backend: &mut LB, // TODO probably need only one of Config/LanguageBackend out: &mut SourceWriter, ) { for variant in &self.variants { @@ -870,11 +870,11 @@ impl Enum { pub(crate) fn write_variant_fields< F: Write, LB: LanguageBackend, - WF: Fn(&LB, &mut SourceWriter, &Field), + WF: Fn(&mut LB, &mut SourceWriter, &Field), >( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, inline_tag_field: bool, write_field: WF, @@ -933,7 +933,7 @@ impl Enum { fn write_derived_functions_enum( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, ) { let has_data = self.tag.is_some(); @@ -1089,11 +1089,11 @@ impl Enum { pub(crate) fn write_derived_functions_data< F: Write, LB: LanguageBackend, - WF: Fn(&LB, &mut SourceWriter, &Field), + WF: Fn(&mut LB, &mut SourceWriter, &Field), >( &self, config: &Config, - language_backend: &LB, + language_backend: &mut LB, out: &mut SourceWriter, tag_name: &str, write_field: WF, diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index 760f590ac..60bd3f944 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -97,7 +97,7 @@ impl GenericParams { pub(crate) fn write_internal( &self, - language_backend: &LB, + language_backend: &mut LB, config: &Config, out: &mut SourceWriter, with_default: bool, @@ -130,7 +130,7 @@ impl GenericParams { pub fn write_with_default( &self, - language_backend: &LB, + language_backend: &mut LB, config: &Config, out: &mut SourceWriter, ) { diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index 7d5a55280..1db7d66d6 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -3,10 +3,10 @@ use crate::bindgen::ir::{ Field, Function, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union, }; -use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::rename::IdentifierType; use crate::bindgen::writer::{ListType, SourceWriter}; -use crate::bindgen::{cdecl, Config, Language, Layout}; +use crate::bindgen::{cdecl, Bindings, Config, Language, Layout}; use crate::bindgen::{DocumentationLength, DocumentationStyle}; use std::io::Write; @@ -19,7 +19,7 @@ impl<'a> CLikeLanguageBackend<'a> { Self { config } } - fn write_enum_variant(&self, out: &mut SourceWriter, u: &EnumVariant) { + fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -35,7 +35,7 @@ impl<'a> CLikeLanguageBackend<'a> { condition.write_after(self.config, out); } - fn write_field(&self, out: &mut SourceWriter, f: &Field) { + fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { let condition = f.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -55,13 +55,59 @@ impl<'a> CLikeLanguageBackend<'a> { } } - fn write_generic_param(&self, out: &mut SourceWriter, g: &GenericParams) { + fn write_generic_param(&mut self, out: &mut SourceWriter, g: &GenericParams) { g.write_internal(self, self.config, out, false); } + + fn open_close_namespaces(&mut self, out: &mut SourceWriter, open: bool) { + let mut namespaces = + if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { + vec![] + } else { + let mut ret = vec![]; + if let Some(ref namespace) = self.config.namespace { + ret.push(&**namespace); + } + if let Some(ref namespaces) = self.config.namespaces { + for namespace in namespaces { + ret.push(&**namespace); + } + } + ret + }; + + if namespaces.is_empty() { + return; + } + + if !open { + namespaces.reverse(); + } + + if self.config.cpp_compatible_c() { + out.new_line_if_not_start(); + out.write("#ifdef __cplusplus"); + } + + for namespace in namespaces { + out.new_line(); + if open { + write!(out, "namespace {} {{", namespace) + } else { + write!(out, "}} // namespace {}", namespace) + } + } + + out.new_line(); + if self.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + } } impl LanguageBackend for CLikeLanguageBackend<'_> { - fn write_headers(&self, out: &mut SourceWriter) { + fn write_headers(&mut self, out: &mut SourceWriter) { if let Some(ref f) = self.config.header { out.new_line_if_not_start(); write!(out, "{}", f); @@ -163,52 +209,15 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } - fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter) { - let mut namespaces = - if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { - vec![] - } else { - let mut ret = vec![]; - if let Some(ref namespace) = self.config.namespace { - ret.push(&**namespace); - } - if let Some(ref namespaces) = self.config.namespaces { - for namespace in namespaces { - ret.push(&**namespace); - } - } - ret - }; - - if namespaces.is_empty() { - return; - } - - if op == NamespaceOperation::Close { - namespaces.reverse(); - } - - if self.config.cpp_compatible_c() { - out.new_line_if_not_start(); - out.write("#ifdef __cplusplus"); - } - - for namespace in namespaces { - out.new_line(); - match op { - NamespaceOperation::Open => write!(out, "namespace {} {{", namespace), - NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace), - } - } + fn open_namespaces(&mut self, out: &mut SourceWriter) { + self.open_close_namespaces(out, true); + } - out.new_line(); - if self.config.cpp_compatible_c() { - out.write("#endif // __cplusplus"); - out.new_line(); - } + fn close_namespaces(&mut self, out: &mut SourceWriter) { + self.open_close_namespaces(out, false) } - fn write_footers(&self, out: &mut SourceWriter) { + fn write_footers(&mut self, out: &mut SourceWriter) { if let Some(f) = self.config.include_guard() { out.new_line_if_not_start(); if self.config.language == Language::C { @@ -220,7 +229,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } - fn write_enum(&self, out: &mut SourceWriter, e: &Enum) { + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); let has_data = e.tag.is_some(); let inline_tag_field = Enum::inline_tag_field(&e.repr); @@ -294,7 +303,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_struct(&self, out: &mut SourceWriter, s: &Struct) { + fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { if s.is_transparent { let typedef = Typedef { path: s.path.clone(), @@ -390,21 +399,26 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { out.new_line(); - let arg_renamer = |name: &str| { - self.config - .function - .rename_args - .apply(name, IdentifierType::FunctionArg) - .into_owned() - }; + let renamed_fields: Vec<_> = s + .fields + .iter() + .map(|field| { + self.config + .function + .rename_args + .apply(&field.name, IdentifierType::FunctionArg) + .into_owned() + }) + .collect(); write!(out, "{}(", s.export_name()); let vec: Vec<_> = s .fields .iter() - .map(|field| { + .zip(&renamed_fields) + .map(|(field, renamed)| { Field::from_name_and_type( // const-ref args to constructor - format!("const& {}", arg_renamer(&field.name)), + format!("const& {}", renamed), field.ty.clone(), ) }) @@ -421,7 +435,8 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { let vec: Vec<_> = s .fields .iter() - .map(|field| format!("{}({})", field.name, arg_renamer(&field.name))) + .zip(&renamed_fields) + .map(|(field, renamed)| format!("{}({})", field.name, renamed)) .collect(); out.write_vertical_source_list(self, &vec[..], ListType::Join(","), |_, out, s| { write!(out, "{}", s) @@ -631,7 +646,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_union(&self, out: &mut SourceWriter, u: &Union) { + fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -699,7 +714,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_opaque_item(&self, out: &mut SourceWriter, o: &OpaqueItem) { + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { let condition = o.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -725,7 +740,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_type_def(&self, out: &mut SourceWriter, t: &Typedef) { + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { let condition = t.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -753,7 +768,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_static(&self, out: &mut SourceWriter, s: &Static) { + fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { @@ -763,10 +778,10 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { out.write(";"); } - fn write_function(&self, out: &mut SourceWriter, f: &Function) { + fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { fn write_1( func: &Function, - language_backend: &CLikeLanguageBackend, + language_backend: &mut CLikeLanguageBackend, out: &mut SourceWriter, ) { let prefix = language_backend.config.function.prefix(&func.annotations); @@ -822,7 +837,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { fn write_2( func: &Function, - language_backend: &CLikeLanguageBackend, + language_backend: &mut CLikeLanguageBackend, out: &mut SourceWriter, ) { let prefix = language_backend.config.function.prefix(&func.annotations); @@ -884,18 +899,19 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { Layout::Horizontal => write_1(f, self, out), Layout::Vertical => write_2(f, self, out), Layout::Auto => { - if !out.try_write(|out| write_1(f, self, out), self.config.line_length) { + let max_line_length = self.config.line_length; + if !out.try_write(|out| write_1(f, self, out), max_line_length) { write_2(f, self, out) } } } } - fn write_type(&self, out: &mut SourceWriter, t: &Type) { + fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } - fn write_documentation(&self, out: &mut SourceWriter, d: &Documentation) { + fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { if d.doc_comment.is_empty() || !self.config.documentation { return; } @@ -961,7 +977,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } - fn write_literal(&self, out: &mut SourceWriter, l: &Literal) { + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { Literal::Expr(v) => match (&**v, self.config.language) { ("true", Language::Cython) => write!(out, "True"), @@ -1053,4 +1069,62 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { } } } + + 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 + if !b.functions.is_empty() || !b.globals.is_empty() { + if b.config.cpp_compatible_c() { + out.new_line_if_not_start(); + out.write("#ifdef __cplusplus"); + } + + if b.config.language == Language::Cxx { + if let Some(ref using_namespaces) = b.config.using_namespaces { + for namespace in using_namespaces { + out.new_line(); + write!(out, "using namespace {};", namespace); + } + out.new_line(); + } + } + + if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { + out.new_line(); + out.write("extern \"C\" {"); + out.new_line(); + } + + if b.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + + self.write_globals_default(out, b); + } + } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + // Override default method to close various blocs containing both globals and functions + // these blocks are opened in [`write_globals`] that is also overridden + if !b.functions.is_empty() || !b.globals.is_empty() { + self.write_functions_default(out, b); + + if b.config.cpp_compatible_c() { + out.new_line(); + out.write("#ifdef __cplusplus"); + } + + if b.config.language == Language::Cxx || b.config.cpp_compatible_c() { + out.new_line(); + out.write("} // extern \"C\""); + out.new_line(); + } + + if b.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + } + } } diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index dded7c071..23dcbbb54 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -3,11 +3,11 @@ use crate::bindgen::ir::{ Field, Function, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, Typedef, Union, }; -use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::writer::{ListType, SourceWriter}; use crate::bindgen::DocumentationLength; use crate::bindgen::Layout; -use crate::bindgen::{cdecl, Config}; +use crate::bindgen::{cdecl, Bindings, Config}; use std::io::Write; pub struct CythonLanguageBackend<'a> { @@ -19,7 +19,7 @@ impl<'a> CythonLanguageBackend<'a> { Self { config } } - fn write_enum_variant(&self, out: &mut SourceWriter, u: &EnumVariant) { + fn write_enum_variant(&mut self, out: &mut SourceWriter, u: &EnumVariant) { self.write_documentation(out, &u.documentation); write!(out, "{}", u.export_name); if let Some(discriminant) = &u.discriminant { @@ -34,7 +34,7 @@ impl<'a> CythonLanguageBackend<'a> { out.write(","); } - fn write_field(&self, out: &mut SourceWriter, f: &Field) { + fn write_field(&mut self, out: &mut SourceWriter, f: &Field) { // Cython doesn't support conditional fields. // let condition = f.cfg.to_condition(self.config); @@ -50,7 +50,7 @@ impl<'a> CythonLanguageBackend<'a> { } impl LanguageBackend for CythonLanguageBackend<'_> { - fn write_headers(&self, out: &mut SourceWriter) { + fn write_headers(&mut self, out: &mut SourceWriter) { if let Some(ref f) = self.config.header { out.new_line_if_not_start(); write!(out, "{}", f); @@ -108,20 +108,20 @@ impl LanguageBackend for CythonLanguageBackend<'_> { } } - fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter) { - if op == NamespaceOperation::Open { - out.new_line(); - let header = &self.config.cython.header.as_deref().unwrap_or("*"); - write!(out, "cdef extern from {}", header); - out.open_brace(); - } else { - out.close_brace(false); - } + fn open_namespaces(&mut self, out: &mut SourceWriter) { + out.new_line(); + let header = &self.config.cython.header.as_deref().unwrap_or("*"); + write!(out, "cdef extern from {}", header); + out.open_brace(); + } + + fn close_namespaces(&mut self, out: &mut SourceWriter) { + out.close_brace(false); } - fn write_footers(&self, _out: &mut SourceWriter) {} + fn write_footers(&mut self, _out: &mut SourceWriter) {} - fn write_enum(&self, out: &mut SourceWriter, e: &Enum) { + fn write_enum(&mut self, out: &mut SourceWriter, e: &Enum) { let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config)); let has_data = e.tag.is_some(); let inline_tag_field = Enum::inline_tag_field(&e.repr); @@ -162,7 +162,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_struct(&self, out: &mut SourceWriter, s: &Struct) { + fn write_struct(&mut self, out: &mut SourceWriter, s: &Struct) { if s.is_transparent { let typedef = Typedef { path: s.path.clone(), @@ -243,7 +243,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_union(&self, out: &mut SourceWriter, u: &Union) { + fn write_union(&mut self, out: &mut SourceWriter, u: &Union) { let condition = u.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -279,7 +279,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_opaque_item(&self, out: &mut SourceWriter, o: &OpaqueItem) { + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem) { let condition = o.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -300,7 +300,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_type_def(&self, out: &mut SourceWriter, t: &Typedef) { + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef) { let condition = t.cfg.to_condition(self.config); condition.write_before(self.config, out); @@ -318,7 +318,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { condition.write_after(self.config, out); } - fn write_static(&self, out: &mut SourceWriter, s: &Static) { + fn write_static(&mut self, out: &mut SourceWriter, s: &Static) { out.write("extern "); if let Type::Ptr { is_const: true, .. } = s.ty { } else if !s.mutable { @@ -328,10 +328,10 @@ impl LanguageBackend for CythonLanguageBackend<'_> { out.write(";"); } - fn write_function(&self, out: &mut SourceWriter, f: &Function) { + fn write_function(&mut self, out: &mut SourceWriter, f: &Function) { fn write_1( func: &Function, - language_backend: &CythonLanguageBackend, + language_backend: &mut CythonLanguageBackend, out: &mut SourceWriter, ) { let prefix = language_backend.config.function.prefix(&func.annotations); @@ -387,7 +387,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { fn write_2( func: &Function, - language_backend: &CythonLanguageBackend, + language_backend: &mut CythonLanguageBackend, out: &mut SourceWriter, ) { let prefix = language_backend.config.function.prefix(&func.annotations); @@ -448,18 +448,19 @@ impl LanguageBackend for CythonLanguageBackend<'_> { Layout::Horizontal => write_1(f, self, out), Layout::Vertical => write_2(f, self, out), Layout::Auto => { - if !out.try_write(|out| write_1(f, self, out), self.config.line_length) { + let max_line_length = self.config.line_length; + if !out.try_write(|out| write_1(f, self, out), max_line_length) { write_2(f, self, out) } } } } - fn write_type(&self, out: &mut SourceWriter, t: &Type) { + fn write_type(&mut self, out: &mut SourceWriter, t: &Type) { cdecl::write_type(self, out, t, self.config); } - fn write_documentation(&self, out: &mut SourceWriter, d: &Documentation) { + fn write_documentation(&mut self, out: &mut SourceWriter, d: &Documentation) { if d.doc_comment.is_empty() || !&self.config.documentation { return; } @@ -476,7 +477,7 @@ impl LanguageBackend for CythonLanguageBackend<'_> { } } - fn write_literal(&self, out: &mut SourceWriter, l: &Literal) { + fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { Literal::Expr(v) => match &**v { "true" => write!(out, "True"), @@ -549,4 +550,16 @@ impl LanguageBackend for CythonLanguageBackend<'_> { } } } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_functions_default(out, b); + + if b.globals.is_empty() + && b.constants.is_empty() + && b.items.is_empty() + && b.functions.is_empty() + { + out.write("pass"); + } + } } diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs index 2cbe768ac..bac3999c6 100644 --- a/src/bindgen/language_backend/mod.rs +++ b/src/bindgen/language_backend/mod.rs @@ -1,7 +1,9 @@ use crate::bindgen::ir::{ - Documentation, Enum, Function, Literal, OpaqueItem, Static, Struct, Type, Typedef, Union, + Documentation, Enum, Function, ItemContainer, Literal, OpaqueItem, Static, Struct, Type, + Typedef, Union, }; use crate::bindgen::writer::SourceWriter; +use crate::bindgen::Bindings; use std::io::Write; @@ -11,24 +13,130 @@ mod cython; pub use clike::CLikeLanguageBackend; pub use cython::CythonLanguageBackend; -#[derive(PartialEq, Eq)] -pub enum NamespaceOperation { - Open, - Close, -} +pub trait LanguageBackend: Sized { + fn write_headers(&mut self, out: &mut SourceWriter); + + fn open_namespaces(&mut self, out: &mut SourceWriter); + fn close_namespaces(&mut self, out: &mut SourceWriter); + 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); + fn write_union(&mut self, out: &mut SourceWriter, u: &Union); + fn write_opaque_item(&mut self, out: &mut SourceWriter, o: &OpaqueItem); + fn write_type_def(&mut self, out: &mut SourceWriter, t: &Typedef); + fn write_static(&mut self, out: &mut SourceWriter, s: &Static); + fn write_function(&mut self, out: &mut SourceWriter, f: &Function); + 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_bindings(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + self.write_headers(out); + + self.open_namespaces(out); + + self.write_primitive_constants(out, b); + + self.write_items(out, b); + + self.write_non_primitive_constants(out, b); + + self.write_globals(out, b); + + self.write_functions(out, b); + + self.close_namespaces(out); + + self.write_footers(out); + + self.write_trailer(out, b); + } + + fn write_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + for constant in &b.constants { + if constant.uses_only_primitive_types() { + out.new_line_if_not_start(); + constant.write(&b.config, self, out, None); + out.new_line(); + } + } + } + + fn write_items(&mut self, out: &mut SourceWriter, b: &Bindings) { + for item in &b.items { + if item + .deref() + .annotations() + .bool("no-export") + .unwrap_or(false) + { + continue; + } + + out.new_line_if_not_start(); + match *item { + ItemContainer::Constant(..) => unreachable!(), + ItemContainer::Static(..) => unreachable!(), + ItemContainer::Enum(ref x) => self.write_enum(out, x), + ItemContainer::Struct(ref x) => self.write_struct(out, x), + ItemContainer::Union(ref x) => self.write_union(out, x), + ItemContainer::OpaqueItem(ref x) => self.write_opaque_item(out, x), + ItemContainer::Typedef(ref x) => self.write_type_def(out, x), + } + out.new_line(); + } + } + + fn write_non_primitive_constants(&mut self, out: &mut SourceWriter, b: &Bindings) + where + Self: Sized, + { + for constant in &b.constants { + if !constant.uses_only_primitive_types() { + out.new_line_if_not_start(); + constant.write(&b.config, self, out, None); + out.new_line(); + } + } + } + + fn write_globals(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_globals_default(out, b) + } + + fn write_globals_default(&mut self, out: &mut SourceWriter, b: &Bindings) { + for global in &b.globals { + out.new_line_if_not_start(); + self.write_static(out, global); + out.new_line(); + } + } + + fn write_functions(&mut self, out: &mut SourceWriter, b: &Bindings) { + self.write_functions_default(out, b) + } + + fn write_functions_default(&mut self, out: &mut SourceWriter, b: &Bindings) { + for function in &b.functions { + out.new_line_if_not_start(); + self.write_function(out, function); + out.new_line(); + } + } -pub trait LanguageBackend { - fn write_headers(&self, out: &mut SourceWriter); - fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter); - fn write_footers(&self, out: &mut SourceWriter); - fn write_enum(&self, out: &mut SourceWriter, e: &Enum); - fn write_struct(&self, out: &mut SourceWriter, s: &Struct); - fn write_union(&self, out: &mut SourceWriter, u: &Union); - fn write_opaque_item(&self, out: &mut SourceWriter, o: &OpaqueItem); - fn write_type_def(&self, out: &mut SourceWriter, t: &Typedef); - fn write_static(&self, out: &mut SourceWriter, s: &Static); - fn write_function(&self, out: &mut SourceWriter, f: &Function); - fn write_type(&self, out: &mut SourceWriter, t: &Type); - fn write_documentation(&self, out: &mut SourceWriter, d: &Documentation); - fn write_literal(&self, out: &mut SourceWriter, l: &Literal); + fn write_trailer(&mut self, out: &mut SourceWriter, b: &Bindings) { + if let Some(ref f) = b.config.trailer { + out.new_line_if_not_start(); + write!(out, "{}", f); + if !f.ends_with('\n') { + out.new_line(); + } + } + } } diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index 051451cc3..f36757d26 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -78,7 +78,7 @@ impl<'a, F: Write> SourceWriter<'a, F> { /// written. pub fn try_write(&mut self, func: T, max_line_length: usize) -> bool where - T: Fn(&mut MeasureWriter), + T: FnOnce(&mut MeasureWriter), { if self.line_length > max_line_length { return false; @@ -211,10 +211,10 @@ impl<'a, F: Write> SourceWriter<'a, F> { pub fn write_horizontal_source_list< LB: LanguageBackend, S, - WF: Fn(&LB, &mut SourceWriter, &S), + WF: Fn(&mut LB, &mut SourceWriter, &S), >( &mut self, - language_backend: &LB, + language_backend: &mut LB, items: &[S], list_type: ListType<'_>, writer: WF, @@ -238,10 +238,10 @@ impl<'a, F: Write> SourceWriter<'a, F> { pub fn write_vertical_source_list< LB: LanguageBackend, S, - WF: Fn(&LB, &mut SourceWriter, &S), + WF: Fn(&mut LB, &mut SourceWriter, &S), >( &mut self, - language_backend: &LB, + language_backend: &mut LB, items: &[S], list_type: ListType<'_>, writer: WF,