Skip to content

Commit

Permalink
[WIP] Support generation of Cython bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Oct 27, 2020
1 parent ef05231 commit b8ef6fb
Show file tree
Hide file tree
Showing 375 changed files with 6,559 additions and 272 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/cbindgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ jobs:
toolchain: nightly
override: true

- name: Install Python
uses: actions/setup-python@v2
with:
python-version: '3.8.*'

- name: Install Cython
run: |
python -m pip install --upgrade pip wheel
pip install Cython==0.29.*
- name: Build
run: |
cargo build --verbose
Expand Down
153 changes: 101 additions & 52 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,16 @@ impl Bindings {
write!(out, "{}", f);
out.new_line();
}
if let Some(ref 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.language != Language::Cython {
if let Some(ref 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 {
if self.config.pragma_once && self.config.language != Language::Cython {
out.new_line_if_not_start();
write!(out, "#pragma once");
out.new_line();
Expand Down Expand Up @@ -164,52 +166,74 @@ impl Bindings {
out.new_line_if_not_start();

if !self.config.no_includes {
if 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>");
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();
}
out.write("#include <stdint.h>");
out.new_line();
out.write("#include <stdlib.h>");
out.new_line();
} else {
out.write("#include <cstdarg>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <cstddef>");
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();
}
}
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>");
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);
}
}
}

for include in &self.config.sys_includes {
write!(out, "#include <{}>", include);
out.new_line();
}
if self.config.language != Language::Cython {
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();
for include in &self.config.includes {
write!(out, "#include \"{}\"", include);
out.new_line();
}
}

if let Some(ref line) = self.config.after_includes {
Expand Down Expand Up @@ -280,7 +304,9 @@ impl Bindings {
}
}

if self.config.language == Language::Cxx || self.config.cpp_compat {
if self.config.language == Language::Cxx
|| self.config.language == Language::C && self.config.cpp_compat
{
out.new_line();
out.write("extern \"C\" {");
out.new_line();
Expand Down Expand Up @@ -308,7 +334,9 @@ impl Bindings {
out.write("#ifdef __cplusplus");
}

if self.config.language == Language::Cxx || self.config.cpp_compat {
if self.config.language == Language::Cxx
|| self.config.language == Language::C && self.config.cpp_compat
{
out.new_line();
out.write("} // extern \"C\"");
out.new_line();
Expand All @@ -320,16 +348,27 @@ impl Bindings {
}
}

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");
}

self.close_namespaces(&mut out);

if let Some(ref 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);
if self.config.language != Language::Cython {
if let Some(ref f) = self.config.include_guard {
out.new_line_if_not_start();
match self.config.language {
Language::C => write!(out, "#endif /* {} */", f),
Language::Cxx => write!(out, "#endif // {}", f),
Language::Cython => {}
}
out.new_line();
}
out.new_line();
}
if let Some(ref f) = self.config.trailer {
out.new_line_if_not_start();
Expand Down Expand Up @@ -357,6 +396,16 @@ impl Bindings {
}

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();
out.write("cdef extern from *");
out.open_brace();
} else {
out.close_brace(false);
}
}

let mut namespaces = self.all_namespaces();
if namespaces.is_empty() {
return;
Expand Down
8 changes: 5 additions & 3 deletions src/bindgen/cdecl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ impl CDecl {
write!(out, "{} ", self.type_qualifers);
}

if let Some(ref ctype) = self.type_ctype {
write!(out, "{} ", ctype.to_str());
if config.language != Language::Cython {
if let Some(ref ctype) = self.type_ctype {
write!(out, "{} ", ctype.to_str());
}
}

write!(out, "{}", self.type_name);
Expand Down Expand Up @@ -214,7 +216,7 @@ impl CDecl {
if is_const {
out.write("const ");
}
if !is_nullable && !is_ref {
if !is_nullable && !is_ref && config.language != Language::Cython {
if let Some(attr) = &config.pointer.non_null_attribute {
write!(out, "{} ", attr);
}
Expand Down
12 changes: 12 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub enum Language {
Cxx,
C,
Cython,
}

impl FromStr for Language {
Expand All @@ -39,6 +40,8 @@ impl FromStr for Language {
"C++" => Ok(Language::Cxx),
"c" => Ok(Language::C),
"C" => Ok(Language::C),
"cython" => Ok(Language::Cython),
"Cython" => Ok(Language::Cython),
_ => Err(format!("Unrecognized Language: '{}'.", s)),
}
}
Expand Down Expand Up @@ -200,6 +203,15 @@ impl Style {
Style::Tag => false,
}
}

// https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#styles-of-struct-union-and-enum-declaration
pub fn cython_def(self) -> &'static str {
if self.generate_tag() {
"cdef "
} else {
"ctypedef "
}
}
}

impl FromStr for Style {
Expand Down
60 changes: 43 additions & 17 deletions src/bindgen/ir/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::fmt;
use std::io::Write;

use crate::bindgen::cargo::cargo_metadata::Dependency;
use crate::bindgen::config::Config;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::writer::SourceWriter;

#[derive(PartialEq, Eq)]
Expand Down Expand Up @@ -283,15 +283,23 @@ impl Condition {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match *self {
Condition::Define(ref define) => {
out.write("defined(");
write!(out, "{}", define);
out.write(")");
if config.language == Language::Cython {
write!(out, "{}", define);
} else {
out.write("defined(");
write!(out, "{}", define);
out.write(")");
}
}
Condition::Any(ref conditions) => {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(" || ");
out.write(if config.language == Language::Cython {
" or "
} else {
" || "
});
}
condition.write(config, out);
}
Expand All @@ -301,14 +309,22 @@ impl Condition {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(" && ");
out.write(if config.language == Language::Cython {
" and "
} else {
" && "
});
}
condition.write(config, out);
}
out.write(")");
}
Condition::Not(ref condition) => {
out.write("!");
out.write(if config.language == Language::Cython {
"not "
} else {
"!"
});
condition.write(config, out);
}
}
Expand All @@ -323,20 +339,30 @@ pub trait ConditionWrite {
impl ConditionWrite for Option<Condition> {
fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if let Some(ref cfg) = *self {
out.push_set_spaces(0);
out.write("#if ");
cfg.write(config, out);
out.pop_set_spaces();
out.new_line();
if config.language == Language::Cython {
out.write("IF ");
cfg.write(config, out);
out.open_brace();
} else {
out.push_set_spaces(0);
out.write("#if ");
cfg.write(config, out);
out.pop_set_spaces();
out.new_line();
}
}
}

fn write_after<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.is_some() {
out.new_line();
out.push_set_spaces(0);
out.write("#endif");
out.pop_set_spaces();
if config.language == Language::Cython {
out.close_brace(false);
} else {
out.new_line();
out.push_set_spaces(0);
out.write("#endif");
out.pop_set_spaces();
}
}
}
}
Loading

0 comments on commit b8ef6fb

Please sign in to comment.