diff --git a/Makefile b/Makefile index fb978eb..34c47b9 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ LINUX_DIR=$(RESOURCES_DIR)/linux-x86-64 LINUX_SHARED_LIB=$(LINUX_DIR)/libenry.so DARWIN_DIR=$(RESOURCES_DIR)/darwin DARWIN_SHARED_LIB=$(DARWIN_DIR)/libenry.dylib +STATIC_LIB=$(RESOURCES_DIR)/libenry.a HEADER_FILE=libenry.h NATIVE_LIB=./shared/enry.go @@ -79,4 +80,10 @@ $(LINUX_SHARED_LIB): CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-shared -o $(LINUX_SHARED_LIB) $(NATIVE_LIB) && \ mv $(LINUX_DIR)/$(HEADER_FILE) $(RESOURCES_DIR)/$(HEADER_FILE) + +static: $(STATIC_LIB) + +$(STATIC_LIB): + CGO_ENABLED=1 go build -buildmode=c-archive -o $(STATIC_LIB) $(NATIVE_LIB) + .PHONY: benchmarks benchmarks-samples benchmarks-slow diff --git a/java/Makefile b/java/Makefile index 0fadca6..08ed5eb 100644 --- a/java/Makefile +++ b/java/Makefile @@ -59,4 +59,6 @@ package: clean: rm -rf $(JAR) rm -rf $(RESOURCES_DIR) + rm -rf $(JNAERATOR_JAR) +.PHONY: test package clean linux-shared darwin-shared os-shared-lib diff --git a/python/README.md b/python/README.md new file mode 100644 index 0000000..eab4f15 --- /dev/null +++ b/python/README.md @@ -0,0 +1,29 @@ +# Python bindings for enry + +Python bingings thoug cFFI (API, out-of-line) for calling enr Go functions though CGo wrapper. + +## Build + +``` +$ make static +$ python enry_build.py +``` + +Will build static library for Cgo wrapper `libenry`, then generate and build `enry.c` +- a CPython extension that + +## Run + +Example for single exposed API function is provided. + +``` +$ python enry.py +``` + +## TODOs + - [ ] try ABI mode, to aviod dependency on C compiler on install (+perf test?) + - [ ] ready `libenry.h` and generate `ffibuilder.cdef` content + - [ ] cover the rest of enry API + - [ ] add `setup.py` + - [ ] build/release automation on CI (publish on pypi) + - \ No newline at end of file diff --git a/python/enry.py b/python/enry.py new file mode 100644 index 0000000..0905e9e --- /dev/null +++ b/python/enry.py @@ -0,0 +1,40 @@ +""" +Python library calling enry Go implementation trough cFFI (API, out-of-line) and Cgo. +""" + +from _c_enry import ffi, lib + + +def go_str_to_py(go_str): + str_len = go_str.n + if str_len > 0: + return ffi.unpack(go_str.p, go_str.n).decode() + return "" + + +def py_str_to_go(py_str): + str_bytes = py_str.encode() + c_str = ffi.new("char[]", str_bytes) + go_str = ffi.new("_GoString_ *", [c_str, len(str_bytes)]) + return go_str[0] + + +def language_by_extension(filename: str) -> str: + fName = py_str_to_go(filename) + guess = lib.GetLanguageByExtension(fName) + lang = go_str_to_py(guess.r0) + return lang + + +## Test + + +def main(): + files = ["Parse.hs", "some.cpp", "and.go", "type.h"] + for filename in files: + lang = language_by_extension(filename) + print("file: {:10s} language: '{}'".format(filename, lang)) + + +if __name__ == "__main__": + main() diff --git a/python/enry_build.py b/python/enry_build.py new file mode 100644 index 0000000..637f8fe --- /dev/null +++ b/python/enry_build.py @@ -0,0 +1,33 @@ +from cffi import FFI +ffibuilder = FFI() + +# cdef() expects a single string declaring the C types, functions and +# globals needed to use the shared object. It must be in valid C syntax. +ffibuilder.cdef(""" +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +typedef _GoString_ GoString; +typedef unsigned char GoUint8; + +/* Return type for GetLanguageByExtension */ +struct GetLanguageByExtension_return { + GoString r0; /* language */ + GoUint8 r1; /* safe */ +}; + +extern struct GetLanguageByExtension_return GetLanguageByExtension(GoString p0); +""") + +# set_source() gives the name of the python extension module to +# produce, and some C source code as a string. This C code needs +# to make the declarated functions, types and globals available, +# so it is often just the "#include". +ffibuilder.set_source("_c_enry", + """ + #include "../.shared/libenry.h" // the C header of the library +""", + libraries=['enry'], + library_dirs=['../.shared' + ]) # library name, for the linker + +if __name__ == "__main__": + ffibuilder.compile(verbose=True)