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头文件的主要内容,如果未能解决你的问题,请参考以下文章