javah
has been deprecated since JDK 8 and will be/has been removed in JDK 10,根据JEP 313和弃用文本,应使用javac
标记为-h
:
警告:计划在下一个主要JDK版本中删除
javah
工具。该工具已被' -h'取代。选项已添加到JDK 8中的javac
。建议用户迁移到使用javac
' -h'选项;有关更多信息,请参阅javac手册页。
问题是,javah
对已编译的.class
文件进行操作,而javac
则对源文件(即.java
文件进行操作。)
javah
可以与Kotlin和external
函数一起使用,因为所有内容最终都编译为Java字节码,但由于在使用Kotlin时没有任何Java源文件,所以我没有'无论如何javac -h
可以发挥作用。
Kotlin是否有javah
替代品或解决方法?
答案 0 :(得分:2)
我建议gjavap 在未来,我还将实现一个更易于使用的命令行工具,该工具提供与javap类似的功能。
答案 1 :(得分:2)
当前没有内置方法可以执行此操作。在2019年11月提出了an open issue for this on the Kotlin issue tracker,但是到目前为止,它尚未被确定优先级,也没有目标版本。
使用JDK 10+生成标头的唯一方法是使用javac -h
,它仅适用于Java源代码,不适用于Kotlin。我在Oo.oO链接的How to solve missing javah in Java 10 – ugly way中测试了该方法,目前它只是一种解决方法。这些步骤是:
javap
将字节码反编译回Java native
方法定义)。javac -h
运行反编译的Java代码以产生标题我正在考虑编写gradle脚本来做到这一点(或者希望其他人能击败我!)。如果我设法完成它,我会更新这篇文章。
答案 2 :(得分:2)
我编写了一个简单的 gradle 任务来生成 JNI 头文件,它的方法与@Oo.oO 发布的方法类似,但与 gradle 有更好的集成,因此可以在 Windows 和基于 Unix 的操作系统上运行。< /p>
val generateJniHeaders by tasks.creating {
group = "build"
dependsOn(tasks.getByName("compileKotlinJvm"))
// For caching
inputs.dir("src/jvmMain/kotlin")
outputs.dir("src/jvmMain/generated/jni")
doLast {
val javaHome = 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/jvm/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 = 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 {
appendln("package $packageName;")
appendln("public class $className {")
for (method in nativeMethods) {
if ("()" in method) appendln(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++
}
}
appendln(updatedMethod)
}
}
appendln("}")
}
val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
outputFile.writeText(source)
project.exec {
commandLine(javac, "-h", jniHeaderDirectory.absolutePath, outputFile.absolutePath)
}.assertNormalExitValue()
}
}
}
答案 3 :(得分:0)
在Kotlin开始生成特定于JDK10的字节码之前,您可以在已编译的kotlin类上使用JDK 9或更低版本的javah
工具。
在此之后,您可以使用external
编译jvmTarget=1.8
个函数,并在结果类上使用javah
。
答案 4 :(得分:0)
您还可以使用javap
并基于class
文件创建立面类。
在这里查看示例:http://www.owsiak.org/how-to-solve-missing-javah-ugly-way/