英文:
Compile libraries for Kotlin android,rust and go
问题
这个问题是探索性质的,不确定是否适合在Stack Overflow上提问和回答。
背景:
我有一个用Go语言编写的库,我需要编译成多个服务可以使用的形式。
这些服务分别是Kotlin Android、Rust和Go。
我所知道的唯一选项是使用类似SWIG的工具将Go库编译成不同的语言。
问题:
- 我不认为SWIG适用于Kotlin。
我正在寻找最佳方法来解决这个问题,以及可以采用的不同方法。
英文:
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.
答案1
得分: 2
对于任何能生成C共享库和头文件的语言,您可以使用SWIG进行封装。同样,对于任何在JVM中运行并能调用Java类的语言,您可以使用SWIG的自动生成的Java绑定。
因此,我们可以按照以下顺序进行一系列操作:
Go -> C -> JNI -> Java -> Kotlin
实际上相当简单。我下面提供了一个示例来展示它的工作原理,因为我之前从未编写过Go或Kotlin。(因此,请对此持保留意见,我可能没有遵循最佳实践!)
此示例假设您已经安装了JDK/JRE、C编译器、Go和kotlinc。
我的demo.go文件如下所示:
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() {}
而hello.kt文件如下所示:
fun main() {
println("Hello, World!")
test.TestGoFunc("Another string")
}
为了封装它,我编写了以下SWIG接口:
%module test
%{
#include "golib.h"
%}
%include <typemaps.i>
%pragma(java) jniclasscode=%{
static {
System.loadLibrary("test");
}
%}
// 假设您不关心库中的这些内容,静音/整理一些东西
#define _Complex
%ignore _GoString_;
%ignore GoComplex64;
%ignore GoComplex128;
%ignore GoSlice;
%ignore GoInterface;
%include "golib.h"
这是一个针对Java的相当标准的SWIG接口,它隐藏了我们不关心的一些生成的头文件中的内容,并使用一个pragma在Java中自动加载.so文件。
然后,我编写了一个小型的Makefile来构建所有内容,因为构建过程中有很多步骤:
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
如果我们构建并运行这个项目,一切都会很好:
$ 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
我建议在Go中使用-buildmode=c-archive
来构建静态库,然后将其链接到SWIG共享对象中,以保持事情更简单。
英文:
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:
<!-- language: kotlin -->
fun main() {
println("Hello, World!")
test.TestGoFunc("Another string")
}
To wrap this I wrote the following SWIG interface:
<!-- language: c -->
%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:
<!-- language: none -->
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:
<!-- language: none -->
$ 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论