I see if I have a KMP library that is building for...
# kotlin-native
a
I see if I have a KMP library that is building for Kotlin Native, I end up with a
shared/build/outputs/aar/shared-debug.aar
for Android and
shared/build/bin/iosArm64/debugFramework/shared.framework
for iOS Upon extracting the
shared
file that got generated on iOS, it appears to be holding a binary generated file targeting
Mach-O 64-bit object arm64
which is what's expected. Now, what's confusing, I extracted the
shared-debug.aar
file generated for Android, and couldn't find any
.so
file, however I am pretty sure I use at some point in life some Kotlin code that got converted into a binary
object/ or ELF
that gets executed on
Linux
. 🤔 How to use Kotlin to have the
AAR
have a binary shared object from my Kotlin code? 🤔
l
Are you using the androidNative targets? Normal android runs on the JVM, so you won't have any shared object. If you target androidNative, it will create a folder in build for each one with a shared object. It's up to you to move the so to the aar.
a
Let me share my build.gradle.kt, but it doesn't include androidNative in it
l
In that case, when building for android, Kotlin uses the JVM compiler. This is typically what you'll want. You should only need androidNative in very specific circumstances.
a
l
The JVM compiler creates classfiles inside the AAR instead of a shared object file like the Native compiler does, since that's what the JVM supports.
I do see that you are using androidNativeArm64 and androidNativeArm32, however. These will build shared object files as outputs. You should check in the build folder for each. These will not be placed in the AAR automatically. I typically create a gradle task that moves them to the spot where the android build expects them to be (which I don't remember offhand).
a
No shared object being generated whatsoever
is it in a different format than ".so" ?
l
I would also rename the 'androidMain' sourceset to 'androidNativeMain' or 'ndkMain', since it looks like you also have an androidTarget, which will try to create a sourceset also named 'androidMain'. That may confuse it.
You may have to add 'binaries.sharedLib' to the androidNative* setup. Take a look at this build gradle.
a
checking now
oh!!
I just added
sharedLib()
and now there's an .so and a .h file
and upon checking your project I can see you're manually creating your JNI bridge
any way to use the library without having to write a glue code?
l
Not right now. The library in that project makes it easier to wire up, but it still takes some work.
a
so, to utilize any so, or KN in Android, there should be a glue code and a System.loadLibrary ?
that's a fact?
Cause I see doing things in a non straightforward way
<https://github.com/Kotlin/kotlinconf-spinner>
oh... I guess I understand why might that be... probably cause its all in native, and the Android Activity is a NativeActivity, which might mean that the app is already/still living in the native realm
or there's something I'm mistaking 🙄
l
The JNI is just the bridge between JVM/Native. If you only have one, you don't need the bridging.
a
yeah agreed
I don't understand why is the generated .klib still doesn't include the .so/.h files
tho the .so/.h is placed after running the task and they're inside the shared/build/ dir
l
The klib is meant for kotlin code to consume. The .so is what you produce for C or the JVM's native loader to use.
a
okay, so when creating the final product library, should it be an .aar? or some other format
l
The final product will be a AAR with a .so in a spot Android knows where to find it. The build gradle I shared defines a task that builds the .so files and moves them to the proper place. After this, the AAR will have the .so files inside when you build it.
a
ah, so the task apparently isn't working for some reason
I am checking for a jniLibs dir afterwards and nothing is there
l
Is there an error it prints? You may need to create the jniLibs folder.
a
no errors
will try creating the jniLibs dir
l
You have to run the task manually
./gradlew prepareAndroidNdkSo
a
I'm running ./gradlew sharedandroid64Binaries ./gradlew sharedassembleDebug
and end up with a few bytes .aar and the jniLibs dir is empty
I need to make sure the variable is being set to the correct dir
l
I'd check that the paths match. You may have a different structure.
a
any way to print/log it ?
l
It's kotlin code if you're using build.gradle.kts. You can println.
I think any printlns typically run when you sync project.
a
yeah its showing
dirs incorrect
fixing that now and checking
copy() isn't happening
l
Are the from/to correct?
a
yeah
they both exists
and the from includes the .so/.h
l
Print out the absolute path?
a
oh, its the doLast
its not executing
I added a println inside and its not showing
l
I'd try just the .so first to eliminate the possibility that including multiple types isn't the issue
a
Copy code
copy {
            from(debugArm64SoFolder)
            into(jniArm64Folder)
            include("*.so")
        }
that's what I have now
okay
its able to copy the .h file when I changed the include()
but not the .so !
l
That's odd. Is the .so in the same folder as the h?
a
yeah
I'm changing a syntax a bit to define it as an isolated task for now
and yea it works and the .so is included inside the .aar
Copy code
├── AndroidManifest.xml
├── META-INF
│   └── com
│       └── android
│           └── build
│               └── gradle
│                   └── aar-metadata.properties
├── R.txt
├── classes.jar
├── jni
│   └── arm64-v8a
│       └── libutils.so
└── libutils-debug.aar
and the classes.jar file is only 22 bytes and is empty upon extraction
I have an issue with which tasks should be executed and in which order
right now, each time I have to do
Copy code
./gradlew :shared:android64Binaries
./gradlew :shared:assembleDebug
and I'm pretty sure there's definitely a single task that could do both
l
You could add a dependency between the tasks.
a
I created a separate shell script that does that to avoid complexity and this stage
and now testing the final .aar crossing my fingers
thanks for all your help
its now loaded in the main project
its accessible in the androidMain of my KMM project, not the commonMain, which I will keep debugging
@Landry Norris sorry to interrupt, but wanted to ask if you might know why the generated aar’s exposed functions/namespaces is only available through androidMain in a KMM project and not from the shared commonMain module
l
An AAR is android-specific. You'll need to expect/actual to get code in common.
a
so I have to do that in the library structure right?
so lets say I am building a libcurl Kotlin library, so I'll be having the glue code + the expect/actual code?
am I understanding this right? any existing template project on that?