This question is exploratory in nature, not sure if this fits stack overlflow Q&A.
Context:
I have a library written in golang that I need to compile for multiple services to use.
These services are in Kotlin android,Rust,Golang.
The only option I am aware of is using something like SWIG to compile the go library for different languages.
Problem:
- I don't think SWIG works for Kotlin.
I am trying to fish for the best methods to do this and different approaches this can be done.
CodePudding user response:
For any language that can generate a C shared library and header file you can use SWIG to wrap it. Equally for any language that runs within a JVM and can call Java classes you can make use of SWIG's auto generated Java bindings.
With that we can therefore do a sequence of things that looks like this:
Go -> C -> JNI -> Java -> Kotlin
It's actually fairly sane. I've put together an example below for this to show how it works since I was curious having never written Go nor Kotlin before. (Take this with a pinch of salt therefore, I've probably not hit "best practice" for either!)
This example assumes you have a working JDK/JRE, C compiler, Go installation and kotlinc.
My demo.go looks like this:
package main
import ("C"
"fmt")
//export TestGoFunc
func TestGoFunc(str *C.char) *C.char {
fmt.Printf("Got string: %s\n", C.GoString(str))
return nil
}
func main() {}
And hello.kt looks like this:
fun main() {
println("Hello, World!")
test.TestGoFunc("Another string")
}
To wrap this I wrote the following SWIG interface:
%module test
%{
#include "golib.h"
%}
%include <typemaps.i>
%pragma(java) jniclasscode=%{
static {
System.loadLibrary("test");
}
%}
// Assuming you don't care about these in your library silence/neaten stuff
#define _Complex
%ignore _GoString_;
%ignore GoComplex64;
%ignore GoComplex128;
%ignore GoSlice;
%ignore GoInterface;
%include "golib.h"
This is a fairly standard SWIG interface for targeting Java - it hides some of the stuff in the generated header file we don't care about and autoloads the .so file inside Java using a pragma for us.
Then I put together a small Makefile to build everything since there's a bunch of steps to this build:
all: libtest.so hello.jar
golib.so: demo.go
go build -o golib.so -buildmode=c-shared demo.go
test_wrap.c: golib.so test.i
swig3.0 -java -Wall test.i
libtest.so: test_wrap.c
gcc -shared -Wall -Wextra test_wrap.c -o libtest.so ./golib.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux
hello.jar: hello.kt
javac *.java
kotlinc hello.kt -include-runtime -d hello.jar -cp .
jar uvf hello.jar *.class
If we build and run this then it all works nicely:
$ make
go build -o golib.so -buildmode=c-shared demo.go
swig3.0 -java -Wall test.i
gcc -shared -Wall -Wextra test_wrap.c -o libtest.so ./golib.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux
javac *.java
kotlinc hello.kt -include-runtime -d hello.jar -cp .
jar uvf hello.jar *.class
adding: test.class(in = 302) (out= 216)(deflated 28%)
adding: testJNI.class(in = 389) (out= 268)(deflated 31%)
$ LD_LIBRARY_PATH=. java -jar hello.jar
Hello, World!
Got string: Another string
I'd be tempted to use -buildmode=c-archive
for go to build a static library and then link that into the SWIG shared object instead just to keep things simpler in that regards though.