kotlin 生成jni头文件

Posted Eritque arcus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kotlin 生成jni头文件相关的知识,希望对你有一定的参考价值。

kotlin 生成jni头文件

问题

最近在用kotlin写jni,但是生成头文件的时候遇到了些问题。
首先 javah 在java >= 1.9 就被取消用javac -h代替,但是javac对kotlin不适用,kotlinc也没有 -h 这个生成头文件的选项。

解决方法

在stackoverflow论坛找到了个解决方案,那个人提供了一个gradle task,大概原理是先用complieKotlin任务(或kotlinc)生成class字节码,再用javac编译回java文件,然后再调用javac -h 针对那个java文件生成jni头文件,我稍微修复了下,然后迁移到gradle kotlin dsl(就kts脚本),代码在下面。

使用方法

复制到build.gradle.kts的最外层就可以了,然后sync一下gradle,然后在gradle task里的build分类下就有generate jniheader这个任务了,StackOverflow原贴地址https://stackoverflow.com/a/65661275/14646226

需要改下代码里的inputs.dir("src/main/kotlin")到你的kt源码文件夹, outputs.dir("src/main/generated/jni")到你想的输出文件夹

代码

val generateJniHeaders: Task by tasks.creating 
    group = "build"
    dependsOn(tasks.getByName("compileKotlin"))

    // For caching
    inputs.dir("src/main/kotlin")
    outputs.dir("src/main/generated/jni")

    doLast 
        val javaHome = org.gradle.internal.jvm.Jvm.current().javaHome
        val javap = javaHome.resolve("bin").walk().firstOrNull  it.name.startsWith("javap") ?.absolutePath ?: error("javap not found")
        val javac = javaHome.resolve("bin").walk().firstOrNull  it.name.startsWith("javac") ?.absolutePath ?: error("javac not found")
        val buildDir = file("build/classes/kotlin/main")
        val tmpDir = file("build/tmp/jvmJni").apply  mkdirs() 

        val bodyExtractingRegex = """^.+\\Rpublic \\w* ?class ([^\\s]+).*\\\\R((?s:.+))\\\\R$""".toRegex()
        val nativeMethodExtractingRegex = """.*\\bnative\\b.*""".toRegex()

        buildDir.walkTopDown()
            .filter  "META" !in it.absolutePath 
            .forEach  file ->
                if (!file.isFile) return@forEach

                val output = com.gradle.publish.plugin.dep.org.apache.commons.io.output.ByteArrayOutputStream().use 
                    project.exec 
                        commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
                        standardOutput = it
                    .assertNormalExitValue()
                    it.toString()
                

                val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return@forEach

                val lastDot = qualifiedName.lastIndexOf('.')
                val packageName = qualifiedName.substring(0, lastDot)
                val className = qualifiedName.substring(lastDot+1, qualifiedName.length)

                val nativeMethods =
                    nativeMethodExtractingRegex.findAll(methodInfo).mapNotNull  it.groups .flatMap  it.asSequence().mapNotNull  group -> group?.value  .toList()
                if (nativeMethods.isEmpty()) return@forEach

                val source = buildString 
                    appendLine("package $packageName;")
                    appendLine("public class $className ")
                    for (method in nativeMethods) 
                        if ("()" in method) appendLine(method)
                        else 
                            val updatedMethod = StringBuilder(method).apply 
                                var count = 0
                                var i = 0
                                while (i < length) 
                                    if (this[i] == ',' || this[i] == ')') insert(i, " arg$count++".also  i += it.length + 1 )
                                    else i++
                                
                            
                            appendLine(updatedMethod)
                        
                    
                    appendLine("")
                
                val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply  mkdirs() .resolve("$className.java").apply  delete() .apply  createNewFile() 
                outputFile.writeText(source)

                project.exec 
                    commandLine(javac, "-h", "src/main/generated/jni", outputFile.absolutePath)
                .assertNormalExitValue()
            
    

gradle task位置截图

以上是关于kotlin 生成jni头文件的主要内容,如果未能解决你的问题,请参考以下文章

Javah生成JNI头文件方法

android studio下生成jni头文件

[JNI开发]使用javah命令生成.h的头文件

javah生成jni头文件时报错 Error: cannot access android.support...

JNI调用C语言

jni