本文测试环境为ubuntu,没有使用IDE;从基本层面了解kotlin native环境中,C和kotlin的编译,互相调用。
1. kotlin 动态库
1.1 动态库编译
源码文件libktest.kt:
Kotlin
//file name:libktest.kt
@OptIn(kotlin.experimental.ExperimentalNativeApi::class)
@CName("testk") // 指定 C 函数名
fun testk() {
println("Hello, Kotlin!") // 打印并换行
}
执行如下命令:
bash
kotlinc-native libktest.kt -produce dynamic -o libktest
bash
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ cat libktest.kt
@OptIn(kotlin.experimental.ExperimentalNativeApi::class)
@CName("testk") // 指定 C 函数名
fun testk() {
println("Hello, Kotlin!") // 打印并换行
}
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ cat build.sh
#!/bin/bash
kotlinc-native libktest.kt -produce dynamic -o libktest
#执行shell命令
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ./build.sh
#获取的结果
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 888
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$
执行shell脚步后获取libktest_api.h和libktest.so,so文件是linux平台可以直接link的调用的的动态库,h文件是生成的头文件
与c代码编译的动态库比较,kotlin的动态库文件比C代码编译的动态库文件大很多,so文件除了包含testk函数外,还额外包含了很多其他信息。看起来是kotlin相关的一些信息,暂时还未研究这些信息,应该是kotlinc-native编译native动态库时做的特别处理。因此看来在客户端瘦身时,这里为考虑点。
1.2 kotlin so文件的动态链接调用
1.2.1 kotlin 代码的动态链接
main.kt主程序代码
Kotlin
import ktest.* // 导入生成的绑定
import kotlinx.cinterop.*
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
fun main() {
testk()
//println("3 + 5 = ${add(3, 5)}") // 调用 C 函数
//println("sqrt(9.0) = ${sqrtf(9.0f)}") // 注意类型匹配(Float)
}
创建ktest.def文件,描述需要绑定(binding)的内容, kotlin代码调用native模块的前提条件
bash
headers = libktest_api.h
headerFilter = libktest_api.h
package = ktest
compilerOpts.linux = -I.
linkerOpts.linux = -L./ -lktest
关于def文件的配置可以参考:
执行如下命令生成绑定:
bash
cinterop -def ktest.def -o ktest.klib
结果如下:
bash
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 892
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ cinterop -def ktest.def -o ktest.klib
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 904
-rw-r--r-- 1 liucx liucx 7694 4月 2 15:35 ktest.klib
drwxr-xr-x 3 liucx liucx 4096 4月 2 15:35 ktest.klib-build
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$
生成ktest.klib 和ktest.klib-build.
编译main.kt,获取maink程序,终端输出信息如下:
bash
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 908
-rw-r--r-- 1 liucx liucx 305 4月 2 15:37 main.kt
-rw-r--r-- 1 liucx liucx 7694 4月 2 15:35 ktest.klib
drwxr-xr-x 3 liucx liucx 4096 4月 2 15:35 ktest.klib-build
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ kotlinc-native main.kt -library ktest.klib -o maink
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 1384
-rwxr-xr-x 1 liucx liucx 485224 4月 2 15:37 maink.kexe
-rw-r--r-- 1 liucx liucx 305 4月 2 15:37 main.kt
-rw-r--r-- 1 liucx liucx 7694 4月 2 15:35 ktest.klib
drwxr-xr-x 3 liucx liucx 4096 4月 2 15:35 ktest.klib-build
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
查看执行结果:
bash
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ./maink.kexe
Hello, Kotlin!
ps:动态库的路径需要手动临时配置下。
至此完成了kotlin调用native动态库的测试。
需要进一步研究的点:
- def文件的binding操作具体做了什么,生成文件的作用
- kotlin native编译的native可执行程序变大了,那么kotlin的编译过程中注入信息以及这写注入的作用
- OptIn的注解作用原理和过程
1.2.2 C 代码的动态链接
bash
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ cat main.c
#include "libktest_api.h"
void main() {
testk();
}
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 1388
-rw-r--r-- 1 liucx liucx 56 4月 2 15:48 main.c
-rwxr-xr-x 1 liucx liucx 485224 4月 2 15:37 maink.kexe
-rw-r--r-- 1 liucx liucx 305 4月 2 15:37 main.kt
-rw-r--r-- 1 liucx liucx 7694 4月 2 15:35 ktest.klib
drwxr-xr-x 3 liucx liucx 4096 4月 2 15:35 ktest.klib-build
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ gcc main.c -o cmain.exe -L. -lktest -Wl,-rpath=./
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ls -lt
总用量 1400
-rwxr-xr-x 1 liucx liucx 8288 4月 2 15:48 cmain.exe
-rw-r--r-- 1 liucx liucx 56 4月 2 15:48 main.c
-rwxr-xr-x 1 liucx liucx 485224 4月 2 15:37 maink.kexe
-rw-r--r-- 1 liucx liucx 305 4月 2 15:37 main.kt
-rw-r--r-- 1 liucx liucx 7694 4月 2 15:35 ktest.klib
drwxr-xr-x 3 liucx liucx 4096 4月 2 15:35 ktest.klib-build
-rw-r--r-- 1 liucx liucx 129 4月 2 15:35 ktest.def
-rwxr-xr-x 1 liucx liucx 892544 4月 2 15:14 libktest.so
-rw-r--r-- 1 liucx liucx 4154 4月 2 15:14 libktest_api.h
-rwxr-xr-x 1 liucx liucx 69 4月 2 15:13 build.sh
-rw-r--r-- 1 liucx liucx 163 4月 2 15:13 libktest.kt
liucx@liucx-QiTianM428-N000:~/Documents/kotlin_demo/lib_file/k_lib/debug$ ./cmain.exe
Hello, Kotlin!
main.c为C代码和cmain.exe为编译的可执行程序。
C代码调用kotlin native编译的动态库很方便,直接包含头文件,链接对应动态库即可。
由此看来,kotlin-native本身生成的native动态库,被native使用时很方便,基本和native使用方式一致。需要考虑的是kotlin编写的native动态库主要需要考虑接口设计时的参数传递。
kotlin调用native动态库时,需要额外的处理生成klib形式的中间文件,以供kotlin-native编译kt文件时使用。kotlin-native编译kt需要klib的中间文件, 仅仅用于编译,最终的可执行程序运行时只依赖于so动态库