Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions _examples/vars/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

import vars

print("doc(vars):\n%s" % repr(vars.__doc__))
print("doc(vars.GetV1()):\n%s" % repr(vars.GetV1.__doc__))
print("doc(vars.SetV1()):\n%s" % repr(vars.SetV1.__doc__))

print("Initial values")
print("v1 = %s" % vars.GetV1())
print("v2 = %s" % vars.GetV2())
print("v3 = %s" % vars.GetV3())
Expand All @@ -21,3 +26,24 @@
#print("k3 = %s" % vars.GetKind3())
#print("k4 = %s" % vars.GetKind4())

vars.SetV1("test1")
vars.SetV2(90)
vars.SetV3(1111.1111)
vars.SetV4("test2")
vars.SetV5(50)
vars.SetV6(50)
vars.SetV7(1111.1111)

vars.SetKind1(123)
vars.SetKind2(456)
print("New values")
print("v1 = %s" % vars.GetV1())
print("v2 = %s" % vars.GetV2())
print("v3 = %s" % vars.GetV3())
print("v4 = %s" % vars.GetV4())
print("v5 = %s" % vars.GetV5())
print("v6 = %s" % vars.GetV6())
print("v7 = %s" % vars.GetV7())

print("k1 = %s" % vars.GetKind1())
print("k2 = %s" % vars.GetKind2())
16 changes: 4 additions & 12 deletions bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,13 @@ func GenCPython(w io.Writer, fset *token.FileSet, pkg *Package, lang int) error

// GenCFFI generates a CFFI package from a Go package
// Use 4spaces indentation for Python codes, aka PEP8.
// b is an io.Writer for a builder python script.
// w is an io.Writer for a wrapper python script which will be executed by a user.
//
// GenCFFI generates a builder python script and a wrapper python script by 3 steps.
// First, GenCFFI analyze which interfaces should be exposed from the Go package.
// Second, Then GenCFFI writes a CFFI builder package with writing exposed interfaces
// Third, The GenCFFI writes a wrapper python script.
func GenCFFI(b io.Writer, w io.Writer, fset *token.FileSet, pkg *Package, lang int) error {
// GenCFFI generates a wrapper python script by 2 steps.
// First, GenCFFI analyzes which interfaces should be exposed from the Go package.
// Then, GenCFFI writes a wrapper python script.
func GenCFFI(w io.Writer, fset *token.FileSet, pkg *Package, lang int) error {
gen := &cffiGen{
builder: &printer{buf: new(bytes.Buffer), indentEach: []byte(" ")},
wrapper: &printer{buf: new(bytes.Buffer), indentEach: []byte(" ")},
fset: fset,
pkg: pkg,
Expand All @@ -76,11 +73,6 @@ func GenCFFI(b io.Writer, w io.Writer, fset *token.FileSet, pkg *Package, lang i
return err
}

_, err = io.Copy(b, gen.builder)
if err != nil {
return err
}

_, err = io.Copy(w, gen.wrapper)
if err != nil {
return err
Expand Down
212 changes: 151 additions & 61 deletions bind/gencffi.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,18 @@ import (
)

const (
/*
FIXME(corona10): ffibuilder.cdef should be written this way.
ffibuilder.cdef("""
//header exported from 'go tool cgo'
#include "%[3]s.h"
""")

* discuss: https://github.com/go-python/gopy/pull/93#discussion_r119652220
*/
builderPreamble = `import os

from sys import argv
from cffi import FFI

ffibuilder = FFI()
ffibuilder.set_source(
'%[1]s',
None,
extra_objects=["_%[1]s.so"],
)
// FIXME(corona10): ffibuilder.cdef should be written this way.
// ffi.cdef("""
// //header exported from 'go tool cgo'
// #include "%[3]s.h"
// """)
// discuss: https://github.com/go-python/gopy/pull/93#discussion_r119652220
cffiPreamble = `"""%[1]s"""
import os
import cffi as _cffi_backend

ffibuilder.cdef("""
ffi = _cffi_backend.FFI()
ffi.cdef("""
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
Expand All @@ -57,69 +47,63 @@ extern char* _cgopy_ErrorString(GoInterface p0);
extern void cgopy_incref(void* p0);
extern void cgopy_decref(void* p0);

extern void cgo_pkg_%[1]s_init();

extern void cgo_pkg_%[2]s_init();
`
builderPreambleEnd = `""")

if __name__ == "__main__":
ffibuilder.compile()
# Remove itself after compile.
os.remove(argv[0])
`

cffiPreamble = `
__doc__="""%[1]s"""

import os
cffiHelperPreamble = `""")

# python <--> cffi helper.
class _cffi_helper(object):

here = os.path.dirname(os.path.abspath(__file__))
lib = ffi.dlopen(os.path.join(here, "_%[2]s.so"))
lib = ffi.dlopen(os.path.join(here, "_%[1]s.so"))

@staticmethod
def cffi_cnv_py2c_string(o):
def cffi_cgopy_cnv_py2c_string(o):
s = ffi.new("char[]", o)
return _cffi_helper.lib._cgopy_GoString(s)

@staticmethod
def cffi_cnv_py2c_int(o):
def cffi_cgopy_cnv_py2c_int(o):
return ffi.cast('int', o)

@staticmethod
def cffi_cnv_py2c_float32(o):
def cffi_cgopy_cnv_py2c_float32(o):
return ffi.cast('float', o)

@staticmethod
def cffi_cnv_py2c_float64(o):
def cffi_cgopy_cnv_py2c_float64(o):
return ffi.cast('double', o)

@staticmethod
def cffi_cnv_c2py_string(c):
def cffi_cgopy_cnv_py2c_uint(o):
return ffi.cast('uint', o)

@staticmethod
def cffi_cgopy_cnv_c2py_string(c):
s = _cffi_helper.lib._cgopy_CString(c)
return ffi.string(s)

@staticmethod
def cffi_cnv_c2py_int(c):
def cffi_cgopy_cnv_c2py_int(c):
return int(c)

@staticmethod
def cffi_cnv_c2py_float32(c):
def cffi_cgopy_cnv_c2py_float32(c):
return float(c)

@staticmethod
def cffi_cnv_c2py_float64(c):
def cffi_cgopy_cnv_c2py_float64(c):
return float(c)

@staticmethod
def cffi_cgopy_cnv_c2py_uint(c):
return int(c)
# make sure Cgo is loaded and initialized
_cffi_helper.lib.cgo_pkg_%[2]s_init()
_cffi_helper.lib.cgo_pkg_%[1]s_init()
`
)

type cffiGen struct {
builder *printer
wrapper *printer

fset *token.FileSet
Expand All @@ -130,31 +114,137 @@ type cffiGen struct {
}

func (g *cffiGen) gen() error {
// Write preamble for CFFI builder.py
g.genBuilderPreamble()
// Write preamble for CFFI library wrapper.
g.genCffiPreamble()
g.genCffiCdef()
g.genWrappedPy()
return nil
}

func (g *cffiGen) genCffiPreamble() {
n := g.pkg.pkg.Name()
pkgDoc := g.pkg.doc.Doc
g.wrapper.Printf(cffiPreamble, pkgDoc, n)
}

func (g *cffiGen) genCffiCdef() {

// first, process slices, arrays
{
names := g.pkg.syms.names()
for _, n := range names {
sym := g.pkg.syms.sym(n)
if !sym.isType() {
continue
}
g.genCdefType(sym)
}
}

for _, f := range g.pkg.funcs {
g.genFunc(f)
g.genCdef(f)
g.genCdefFunc(f)
}

// Finalizing preamble for CFFI builder.py
g.genBuilderPreambleEnd()
return nil
for _, c := range g.pkg.consts {
g.genCdefConst(c)
}

for _, v := range g.pkg.vars {
g.genCdefVar(v)
}
}

func (g *cffiGen) genBuilderPreamble() {
func (g *cffiGen) genWrappedPy() {
n := g.pkg.pkg.Name()
g.builder.Printf(builderPreamble, n)
g.wrapper.Printf(cffiHelperPreamble, n)

for _, f := range g.pkg.funcs {
g.genFunc(f)
}

for _, c := range g.pkg.consts {
g.genConst(c)
}

for _, v := range g.pkg.vars {
g.genVar(v)
}
}

func (g *cffiGen) genBuilderPreambleEnd() {
g.builder.Printf(builderPreambleEnd)
func (g *cffiGen) genConst(c Const) {
g.genGetFunc(c.f)
}

func (g *cffiGen) genCffiPreamble() {
n := g.pkg.pkg.Name()
pkgDoc := g.pkg.doc.Doc
g.wrapper.Printf(cffiPreamble, pkgDoc, n)
func (g *cffiGen) genVar(v Var) {
id := g.pkg.Name() + "_" + v.Name()
doc := v.doc
{
res := []*Var{newVar(g.pkg, v.GoType(), "ret", v.Name(), doc)}
sig := newSignature(g.pkg, nil, nil, res)
fget := Func{
pkg: g.pkg,
sig: sig,
typ: nil,
name: v.Name(),
id: id + "_get",
doc: "returns " + g.pkg.Name() + "." + v.Name(),
ret: v.GoType(),
err: false,
}
g.genGetFunc(fget)
}
{
params := []*Var{newVar(g.pkg, v.GoType(), "arg", v.Name(), doc)}
sig := newSignature(g.pkg, nil, params, nil)
fset := Func{
pkg: g.pkg,
sig: sig,
typ: nil,
name: v.Name(),
id: id + "_set",
doc: "sets " + g.pkg.Name() + "." + v.Name(),
ret: nil,
err: false,
}
g.genSetFunc(fset)
}
}

func (g *cffiGen) genCdefConst(c Const) {
g.genCdefFunc(c.f)
}

func (g *cffiGen) genCdefVar(v Var) {
id := g.pkg.Name() + "_" + v.Name()
doc := v.doc
{
res := []*Var{newVar(g.pkg, v.GoType(), "ret", v.Name(), doc)}
sig := newSignature(g.pkg, nil, nil, res)
fget := Func{
pkg: g.pkg,
sig: sig,
typ: nil,
name: v.Name(),
id: id + "_get",
doc: "returns " + g.pkg.Name() + "." + v.Name(),
ret: v.GoType(),
err: false,
}
g.genCdefFunc(fget)
}
{
params := []*Var{newVar(g.pkg, v.GoType(), "arg", v.Name(), doc)}
sig := newSignature(g.pkg, nil, params, nil)
fset := Func{
pkg: g.pkg,
sig: sig,
typ: nil,
name: v.Name(),
id: id + "_set",
doc: "sets " + g.pkg.Name() + "." + v.Name(),
ret: nil,
err: false,
}
g.genCdefFunc(fset)
}
}
25 changes: 23 additions & 2 deletions bind/gencffi_cdef.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,28 @@ import (
"strings"
)

func (g *cffiGen) genCdef(f Func) {
func (g *cffiGen) genCdefType(sym *symbol) {
if !sym.isType() {
return
}

if sym.isStruct() {
return
}

if sym.isBasic() && !sym.isNamed() {
return
}

if sym.isBasic() {
btyp := g.pkg.syms.symtype(sym.GoType().Underlying())
g.wrapper.Printf("typedef %s %s;\n\n", btyp.cgoname, sym.cgoname)
} else {
g.wrapper.Printf("typedef void* %s;\n\n", sym.cgoname)
}
}

func (g *cffiGen) genCdefFunc(f Func) {
var params []string
var cdef_ret string
sig := f.sig
Expand All @@ -31,5 +52,5 @@ func (g *cffiGen) genCdef(f Func) {
}

paramString := strings.Join(params, ", ")
g.builder.Printf("extern %[1]s cgo_func_%[2]s_%[3]s(%[4]s);\n", cdef_ret, g.pkg.Name(), f.name, paramString)
g.wrapper.Printf("extern %[1]s cgo_func_%[2]s(%[3]s);\n", cdef_ret, f.id, paramString)
}
Loading