swift-android-sdk icon indicating copy to clipboard operation
swift-android-sdk copied to clipboard

How to build Package.swift

Open kmitj opened this issue 3 years ago • 3 comments

hello. how to generate .so library for android from SPM project? In ios project we have core library written with swift pm. How i can build this lib for android jni usage?

kmitj avatar Sep 13 '22 07:09 kmitj

I am not sure, as I haven't tried using Swift through JNI yet. I assume SPM has some way to generate a shared library, and you can then use a Swift JNI wrapper like this to call it. I can't help you more than that because I haven't tried such JNI usage myself yet.

finagolfin avatar Sep 13 '22 15:09 finagolfin

https://github.com/KittyMac/SilkRoad

I need to go back and document it all but I recently got it working using this toolchain. In a nutshell:

  • Follow the direction on this project's readme Grab the SDK releases from this project Grab the correct NDK Link the correct clang

  • Modify the android-aarch64.json (and other appropriate build json) so that extra-swiftc-flags includes "-emit-library" Build your SPM projects as described in the readme. -emit-library will generate all of the .so files from your project in the project folder

  • SPM doesn't construct the .so correctly for loading using System.LoadLibrary. Specifically, they lack the soname and the needed fields so that each library knows which dependencies its needs. you need to use patchelf to fix them. You need to do this for your built .so as well as libFoundationNetworking.so and its dependencies if you want it. You will need to download the correct dependencies and fix them all.

  • Copy the .so from SDKs and for the architectures you want to support

    • Put them in jniLibs/arm64-v8a, jniLibs/armeabi-v7a, jniLibs/x86_64
    • Call System.loadLibrary("mySPMProjectName") in your app somewhere you use JNI to access it You will need to manually load the following two prior, for whatever reason they don't load correctly even if you fix the NEEDED correctly:
System.loadLibrary("icuuc")
System.loadLibrary("icui18n")
  • I found it best to link to the .so libraries in C code included in my project, so I have kotlin -> JNI C wrapper -> Swift global functions. This allows the Swift code to not care about JNIEnv and all that. So something like:
external fun add(x: Long, y: Long): Long
val x = add(40, 2)
        Log.d("TAG", "the value is ${x}")
extern "C" int silkroad_add(jlong x, jlong y);
extern "C" JNIEXPORT jlong JNICALL
Java_com_chimerasw_silkroadandroidtest_MainActivityKt_add(JNIEnv *env,
                                                          jclass clazz,
                                                          jlong x,
                                                          jlong y) {
    return silkroad_add(x, y);
}
@_cdecl("silkroad_add")
public func add(x: Int, y: Int) -> Int {
    return x + y
}

If that's all too much then you probably want to steer clear. If you want to proceed, you can check out the Dockerfiles on my project. https://hub.docker.com/repository/docker/kittymac/silkroad is already built for you, and you can use it to build just your project (by making your own Dockerfile like https://github.com/KittyMac/SilkRoad/blob/main/Dockerfile.silkroad ).

Note: you must do all of them above on a x86 machine docker platform set to linux/amd64. If you try on Apple silicon the build process fails with a QEMU instruction error. I just buildx over to a different machine, its only needed for the step to generate your .so files.

Many thanks to @buttaface for this project making all that possible.

KittyMac avatar Sep 14 '22 01:09 KittyMac

@KittyMac thanks for the detailed answer. I will try it

kmitj avatar Sep 14 '22 05:09 kmitj

Closing since I couldn't help, feel free to report back to us later on how it worked out.

finagolfin avatar Sep 20 '22 11:09 finagolfin