Skip to content

Commit

Permalink
introduce the concept of language backend
Browse files Browse the repository at this point in the history
  • Loading branch information
fredszaq committed Jun 29, 2023
1 parent 0b43f0b commit 48416dc
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 214 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

228 changes: 17 additions & 211 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{
Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef,
};
use crate::bindgen::language_backend::{
CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, NamespaceOperation,
};
use crate::bindgen::writer::{Source, SourceWriter};

/// A bindings header that can be written.
Expand All @@ -35,12 +38,6 @@ pub struct Bindings {
noop: bool,
}

#[derive(PartialEq, Eq)]
enum NamespaceOperation {
Open,
Close,
}

impl Bindings {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
Expand Down Expand Up @@ -204,146 +201,29 @@ impl Bindings {
}
}

pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
if self.noop {
return;
}

if let Some(ref f) = self.config.header {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}
if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
write!(out, "#ifndef {}", f);
out.new_line();
write!(out, "#define {}", f);
out.new_line();
}
if self.config.pragma_once && self.config.language != Language::Cython {
out.new_line_if_not_start();
write!(out, "#pragma once");
out.new_line();
}
if self.config.include_version {
out.new_line_if_not_start();
write!(
out,
"/* Generated with cbindgen:{} */",
crate::bindgen::config::VERSION
);
out.new_line();
}
if let Some(ref f) = self.config.autogen_warning {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}

if self.config.no_includes
&& self.config.sys_includes().is_empty()
&& self.config.includes().is_empty()
&& (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython)
&& self.config.after_includes.is_none()
{
return;
}

out.new_line_if_not_start();

if !self.config.no_includes {
match self.config.language {
Language::C => {
out.write("#include <stdarg.h>");
out.new_line();
out.write("#include <stdbool.h>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <stddef.h>");
out.new_line();
}
out.write("#include <stdint.h>");
out.new_line();
out.write("#include <stdlib.h>");
out.new_line();
}
Language::Cxx => {
out.write("#include <cstdarg>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <cstddef>");
out.new_line();
}
out.write("#include <cstdint>");
out.new_line();
out.write("#include <cstdlib>");
out.new_line();
out.write("#include <ostream>");
out.new_line();
out.write("#include <new>");
out.new_line();
if self.config.enumeration.cast_assert_name.is_none()
&& (self.config.enumeration.derive_mut_casts
|| self.config.enumeration.derive_const_casts)
{
out.write("#include <cassert>");
out.new_line();
}
}
Language::Cython => {
out.write(
"from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t",
);
out.new_line();
out.write(
"from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t",
);
out.new_line();
out.write("cdef extern from *");
out.open_brace();
out.write("ctypedef bint bool");
out.new_line();
out.write("ctypedef struct va_list");
out.new_line();
out.close_brace(false);
}
pub fn write<F: Write>(&self, file: F) {
match self.config.language {
Language::Cxx | Language::C => {
self.write_with_backend(file, CLikeLanguageBackend::new(self.config.clone()))
}
}

for include in self.config.sys_includes() {
write!(out, "#include <{}>", include);
out.new_line();
}

for include in self.config.includes() {
write!(out, "#include \"{}\"", include);
out.new_line();
}

if self.config.language == Language::Cython {
for (module, names) in &self.config.cython.cimports {
write!(out, "from {} cimport {}", module, names.join(", "));
out.new_line();
Language::Cython => {
self.write_with_backend(file, CythonLanguageBackend::new(self.config.clone()))
}
}

if let Some(ref line) = self.config.after_includes {
write!(out, "{}", line);
out.new_line();
}
}

pub fn write<F: Write>(&self, file: F) {
pub fn write_with_backend<F: Write, LB: LanguageBackend>(&self, file: F, language_backend: LB) {
if self.noop {
return;
}

let mut out = SourceWriter::new(file, self);
let writer = Box::new(file);

self.write_headers(&mut out);
let mut out = SourceWriter::new(writer, self);

self.open_namespaces(&mut out);
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() {
Expand Down Expand Up @@ -449,17 +329,9 @@ impl Bindings {
out.write("pass");
}

self.close_namespaces(&mut out);
language_backend.open_close_namespaces(NamespaceOperation::Close, &mut out);

if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
if self.config.language == Language::C {
write!(out, "#endif /* {} */", f);
} else {
write!(out, "#endif // {}", f);
}
out.new_line();
}
language_backend.write_footers(&mut out);
if let Some(ref f) = self.config.trailer {
out.new_line_if_not_start();
write!(out, "{}", f);
Expand All @@ -468,70 +340,4 @@ impl Bindings {
}
}
}

fn all_namespaces(&self) -> Vec<&str> {
if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
return vec![];
}
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
}

fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
if self.config.language == Language::Cython {
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);
}
return;
}

let mut namespaces = self.all_namespaces();
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),
}
}

out.new_line();
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
}

pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Open, out);
}

pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Close, out);
}
}
Loading

0 comments on commit 48416dc

Please sign in to comment.