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 10, 2020
1 parent e038301 commit ed50ebe
Show file tree
Hide file tree
Showing 137 changed files with 3,415 additions and 205 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Running test suite will update test expectations to use LF line endings,
# so they need to be checked out using LF as well.
* text=auto eol=lf
linestyle_crlf* -text
10 changes: 10 additions & 0 deletions .github/workflows/cbindgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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
80 changes: 51 additions & 29 deletions src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,31 +164,46 @@ 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();
out.write("#include <stdint.h>");
out.new_line();
out.write("#include <stdlib.h>");
out.new_line();
} else {
out.write("#include <cstdarg>");
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>");
match self.config.language {
Language::C => {
out.write("#include <stdarg.h>");
out.new_line();
out.write("#include <stdbool.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();
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("from libcpp cimport bool");
out.new_line();
}
}
Expand Down Expand Up @@ -316,10 +331,10 @@ impl Bindings {

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);
match self.config.language {
Language::C => write!(out, "#endif /* {} */", f),
Language::Cxx => write!(out, "#endif // {}", f),
Language::Cython => {}
}
out.new_line();
}
Expand Down Expand Up @@ -349,6 +364,13 @@ impl Bindings {
}

fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
if self.config.language == Language::Cython && op == NamespaceOperation::Open {
out.new_line();
out.write("cdef extern from *:");
out.new_line();
out.push_tab();
}

let mut namespaces = self.all_namespaces();
if namespaces.is_empty() {
return;
Expand Down
5 changes: 1 addition & 4 deletions src/bindgen/cdecl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ enum CDeclarator {

impl CDeclarator {
fn is_ptr(&self) -> bool {
match self {
CDeclarator::Ptr { .. } | CDeclarator::Func(..) => true,
_ => false,
}
matches!(self, CDeclarator::Ptr { .. } | CDeclarator::Func(..))
}
}

Expand Down
3 changes: 3 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
35 changes: 20 additions & 15 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,10 @@ impl Literal {
fields,
path,
} => {
if config.language == Language::C {
write!(out, "({})", export_name);
} else {
write!(out, "{}", export_name);
match config.language {
Language::C => write!(out, "({})", export_name),
Language::Cxx => write!(out, "{}", export_name),
Language::Cython => {}
}

write!(out, "{{ ");
Expand Down Expand Up @@ -551,21 +551,26 @@ impl Constant {
false
};

if (config.constant.allow_static_const || allow_constexpr)
&& config.language == Language::Cxx
if config.language == Language::Cython
|| config.language == Language::Cxx
&& (config.constant.allow_static_const || allow_constexpr)
{
if allow_constexpr {
out.write("constexpr ")
}
if config.language == Language::Cxx {
if allow_constexpr {
out.write("constexpr ")
}

if config.constant.allow_static_const {
out.write(if in_body { "inline " } else { "static " });
}
if config.constant.allow_static_const {
out.write(if in_body { "inline " } else { "static " });
}

if let Type::Ptr { is_const: true, .. } = self.ty {
// Nothing.
if let Type::Ptr { is_const: true, .. } = self.ty {
// Nothing.
} else {
out.write("const ");
}
} else {
out.write("const ");
out.write("cdef ");
}

self.ty.write(config, out);
Expand Down
82 changes: 48 additions & 34 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,9 @@ impl Item for Enum {
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);

if config.language == Language::C && self.tag.is_some() {
if self.tag.is_some()
&& (config.language == Language::C || config.language == Language::Cython)
{
// it makes sense to always prefix Tag with type name in C
let new_tag = format!("{}_Tag", self.export_name);
if self.repr.style == ReprStyle::Rust {
Expand Down Expand Up @@ -646,46 +648,53 @@ impl Source for Enum {
};

// Emit the actual enum
if config.language == Language::C {
if size.is_none() && config.style.generate_typedef() {
out.write("typedef ");
}
match config.language {
Language::C => {
if size.is_none() && config.style.generate_typedef() {
out.write("typedef ");
}

out.write("enum");
out.write("enum");

if size.is_some() || config.style.generate_tag() {
write!(out, " {}", enum_name);
}
if size.is_some() || config.style.generate_tag() {
write!(out, " {}", enum_name);
}

if config.cpp_compat {
if let Some(prim) = size {
out.new_line();
out.write("#ifdef __cplusplus");
out.new_line();
write!(out, " : {}", prim);
out.new_line();
out.write("#endif // __cplusplus");
out.new_line();
if config.cpp_compat {
if let Some(prim) = size {
out.new_line();
out.write("#ifdef __cplusplus");
out.new_line();
write!(out, " : {}", prim);
out.new_line();
out.write("#endif // __cplusplus");
out.new_line();
}
}
}
} else {
if config.enumeration.enum_class(&self.annotations) {
out.write("enum class");
} else {
out.write("enum");
}
Language::Cxx => {
if config.enumeration.enum_class(&self.annotations) {
out.write("enum class");
} else {
out.write("enum");
}

if self.annotations.must_use {
if let Some(ref anno) = config.enumeration.must_use {
write!(out, " {}", anno)
if self.annotations.must_use {
if let Some(ref anno) = config.enumeration.must_use {
write!(out, " {}", anno)
}
}
}

write!(out, " {}", enum_name);
if let Some(prim) = size {
write!(out, " : {}", prim);
write!(out, " {}", enum_name);
if let Some(prim) = size {
write!(out, " : {}", prim);
}
}
Language::Cython => {
write!(out, "cdef enum {}", enum_name);
}
}

out.open_brace();
for (i, variant) in self.variants.iter().enumerate() {
if i != 0 {
Expand Down Expand Up @@ -867,14 +876,16 @@ impl Source for Enum {
out.new_line();

// Emit the actual union
if config.language == Language::C {
if config.style.generate_typedef() {
if config.language == Language::C || config.language == Language::Cython {
if config.language == Language::Cython {
out.write("cdef ");
} else if config.style.generate_typedef() {
out.write("typedef ");
}

out.write(if separate_tag { "struct" } else { "union" });

if config.style.generate_tag() {
if config.language == Language::Cython || config.style.generate_tag() {
write!(out, " {}", self.export_name());
}

Expand Down Expand Up @@ -912,6 +923,9 @@ impl Source for Enum {
out.new_line();

if separate_tag {
if config.language == Language::Cython {
out.write("cdef ");
}
out.write("union");
out.open_brace();
}
Expand Down
27 changes: 18 additions & 9 deletions src/bindgen/ir/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,24 @@ impl Source for OpaqueItem {

self.generic_params.write_with_default(config, out);

if config.style.generate_typedef() && config.language == Language::C {
write!(
out,
"typedef struct {} {};",
self.export_name(),
self.export_name()
);
} else {
write!(out, "struct {};", self.export_name());
match config.language {
Language::C if config.style.generate_typedef() => {
write!(
out,
"typedef struct {} {};",
self.export_name(),
self.export_name()
);
}
Language::C | Language::Cxx => {
write!(out, "struct {};", self.export_name());
}
Language::Cython => {
write!(out, "cdef struct {}", self.export_name());
out.open_brace();
out.write("pass");
out.close_brace(false);
}
}

condition.write_after(config, out);
Expand Down
Loading

0 comments on commit ed50ebe

Please sign in to comment.