基于kotlin native的C与kotlin互相调用

本文测试环境为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文件的配置可以参考:

定义文件 | Kotlin 语言参考文档 中文版

执行如下命令生成绑定:

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动态库的测试。

需要进一步研究的点:

  1. def文件的binding操作具体做了什么,生成文件的作用
  2. kotlin native编译的native可执行程序变大了,那么kotlin的编译过程中注入信息以及这写注入的作用
  3. 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动态库

相关推荐
alexhilton2 天前
端侧RAG实战指南
android·kotlin·android jetpack
Kapaseker2 天前
2026年,我们还该不该学编程?
android·kotlin
Kapaseker3 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
Kapaseker4 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish5 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker5 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker6 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z8 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton9 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream9 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin