Home > OS >  Compile libraries for Kotlin android,rust and go
Compile libraries for Kotlin android,rust and go

Time:06-04

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:

  1. 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.

  • Related