Kotlin替代javah

jslywgbw  于 2023-11-21  发布在  Kotlin
关注(0)|答案(5)|浏览(344)

javah has been deprecated since JDK 8 and will be/has been removed in JDK 10,根据JEP 313和弃用文本,应该使用带有-h标志的javac
警告:javah工具计划在下一个主要JDK版本中删除。该工具已被JDK 8中添加到javac的“-h”选项所取代。建议用户使用javac“-h”选项迁移到;有关详细信息,请参阅javac手册页。
问题是,javah对编译后的.class文件进行操作,而javac对源文件(即.java文件)进行操作。
javah可以很好地与Kotlin和external函数一起工作,因为所有内容最终都被编译为Java字节码,但由于使用Kotlin时没有任何Java源文件,我看不出javac -h有任何工作方式。
Kotlin是否有javah替代品或解决方案?

xvw2m8pv

xvw2m8pv1#

我写了一个简单的gradle任务来生成JNI头文件,它的方法与@Oo.oO发布的类似,但与gradle有更好的集成,因此可以在Windows和基于Unix的操作系统上运行。

  1. val generateJniHeaders by tasks.creating {
  2. group = "build"
  3. dependsOn(tasks.getByName("compileKotlinJvm"))
  4. // For caching
  5. inputs.dir("src/jvmMain/kotlin")
  6. outputs.dir("src/jvmMain/generated/jni")
  7. doLast {
  8. val javaHome = Jvm.current().javaHome
  9. val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found")
  10. val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found")
  11. val buildDir = file("build/classes/kotlin/jvm/main")
  12. val tmpDir = file("build/tmp/jvmJni").apply { mkdirs() }
  13. val bodyExtractingRegex = """^.+\Rpublic \w* ?class ([^\s]+).*\{\R((?s:.+))\}\R$""".toRegex()
  14. val nativeMethodExtractingRegex = """.*\bnative\b.*""".toRegex()
  15. buildDir.walkTopDown()
  16. .filter { "META" !in it.absolutePath }
  17. .forEach { file ->
  18. if (!file.isFile) return@forEach
  19. val output = ByteArrayOutputStream().use {
  20. project.exec {
  21. commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
  22. standardOutput = it
  23. }.assertNormalExitValue()
  24. it.toString()
  25. }
  26. val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return@forEach
  27. val lastDot = qualifiedName.lastIndexOf('.')
  28. val packageName = qualifiedName.substring(0, lastDot)
  29. val className = qualifiedName.substring(lastDot+1, qualifiedName.length)
  30. val nativeMethods =
  31. nativeMethodExtractingRegex.findAll(methodInfo).mapNotNull { it.groups }.flatMap { it.asSequence().mapNotNull { group -> group?.value } }.toList()
  32. if (nativeMethods.isEmpty()) return@forEach
  33. val source = buildString {
  34. appendln("package $packageName;")
  35. appendln("public class $className {")
  36. for (method in nativeMethods) {
  37. if ("()" in method) appendln(method)
  38. else {
  39. val updatedMethod = StringBuilder(method).apply {
  40. var count = 0
  41. var i = 0
  42. while (i < length) {
  43. if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 })
  44. else i++
  45. }
  46. }
  47. appendln(updatedMethod)
  48. }
  49. }
  50. appendln("}")
  51. }
  52. val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
  53. outputFile.writeText(source)
  54. project.exec {
  55. commandLine(javac, "-h", jniHeaderDirectory.absolutePath, outputFile.absolutePath)
  56. }.assertNormalExitValue()
  57. }
  58. }
  59. }

字符串

展开查看全部
toe95027

toe950272#

我推荐gjavah
在未来,我还将实现一个易于使用的命令行工具,它提供与javah类似的功能。

cnjp1d6j

cnjp1d6j3#

目前还没有内置的方法来做到这一点。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中测试了这个方法,它目前可以作为一个解决方案。步骤是:
1.使用javap将字节码反编译回Java
1.编辑反编译后的Java,使其适合生成头文件(将文件缩减为仅包含native方法定义)。
1.通过javac -h运行反编译的Java代码以生成头文件
我正在考虑写一个gradle脚本来完成这个任务(或者希望有人能比我先完成!)。如果我能完成这个任务,我会更新这篇文章。

jckbn6z7

jckbn6z74#

在Kotlin开始生成特定于JDK 10的字节码之前,您可以在已编译的Kotlin类上使用JDK 9或更低版本的javah工具。
甚至在此之后,您可以使用jvmTarget=1.8编译external函数,并在生成的类上使用javah

c86crjj0

c86crjj05#

您也可以使用javap并基于class文件创建facade类。
看看这里的一个例子:http://www.owsiak.org/how-to-solve-missing-javah-ugly-way/

相关问题