问题

最近在用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")到你想的输出文件夹

代码

  1. val generateJniHeaders: Task by tasks.creating {
  2. group = "build"
  3. dependsOn(tasks.getByName("compileKotlin"))
  4. // For caching
  5. inputs.dir("src/main/kotlin")
  6. outputs.dir("src/main/generated/jni")
  7. doLast {
  8. val javaHome = org.gradle.internal.jvm.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/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 = com.gradle.publish.plugin.dep.org.apache.commons.io.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. appendLine("package $packageName;")
  35. appendLine("public class $className {")
  36. for (method in nativeMethods) {
  37. if ("()" in method) appendLine(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. appendLine(updatedMethod)
  48. }
  49. }
  50. appendLine("}")
  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", "src/main/generated/jni", outputFile.absolutePath)
  56. }.assertNormalExitValue()
  57. }
  58. }
  59. }

gradle task位置截图

kotlin gradle 生成jni头文件的更多相关文章

  1. ubuntu下整合eclipse和javah生成jni头文件开发android的native程序(转)

    本文介绍两种利用javah命令生成jni头文件的方法,第一种为大众所知的javah命令,第二种为整合javah到eclipse里面.推荐第二种方式,方便快捷,随时修改随时生成 0:前提和条件: 1:u ...

  2. ubuntu下整合eclipse和javah生成jni头文件开发android的native程序

    0:前言: 这两天一直在研究用android的jni调用第三方库,上网搜方法,但是都是泛泛而谈,没有demo,经过我几番折磨,写了n多的helloword工程,总是不成功,工程名字也就由helloow ...

  3. Android NDK入门实例 计算斐波那契数列一生成jni头文件

    最近要用到Android NDK,调用本地代码.就学了下Android NDK,顺便与大家分享.下面以一个具体的实例计算斐波那契数列,说明如何利用Android NDK,调用本地代码.以及比较本地代码 ...

  4. Java 生成 JNI 头文件

    [参考文章]:javah javac 命令行生成native的方法教程 [参考文章]:用javah 导出类的头文件, 常见的错误及正确的使用方法 项目需要调用 C++动态库,由于项目包结构发生变化,需 ...

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

    javah生成jni头文件时报错: Error: cannot access android.support.v7.app.AppCompatActivity class file for andro ...

  6. [转]ubuntu下整合eclipse和javah生成jni头文件开发android的native程序

    转载自:http://blog.csdn.net/jiuyueguang/article/details/9404237 本文介绍两种利用javah命令生成jni头文件的方法,第一种为大众所知的jav ...

  7. 一键生成JNI头文件方法二

    经常使用java的同学一定都接触过JNI(Java Native Interface)吧.JNI为我们提供了java<---->C/C++之间的接口,使得我们可以在java中调用C程序,以 ...

  8. 使用javah生成jni 头文件和使用ndk编译so库

    1.jni 首先clean Project,在makeProject生成对应的class文件 然后点出命名框,输入命令: cd app/build/intermediates/classes/debu ...

  9. Android NDK学习(3)使用Javah命令生成JNI头文件 .

    转:http://www.cnblogs.com/fww330666557/archive/2012/12/14/2817387.html 第一步: 在Eclipse中创建android项目,并声明N ...

随机推荐

  1. Shell脚本 /dev/null 2>&1详解

    Shell脚本---- /dev/null 2>&1详解   1.可以将/dev/null看作"黑洞". 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失. ...

  2. shell基础之后台运行脚本

    使shell脚本后台执行,基本的方法有两种,第一种为在脚本后面追加&符号,第二种为在脚本前面使用nohup命令,结尾再追加&符号 一.后台运行脚本1 1.执行脚本test.sh:./t ...

  3. python类属性和实例属性的访问

  4. 在浏览器上运行 VS Code——GitHub 热点速览 v.21.22

    作者:HelloGitHub-小鱼干 和小程序类似,如果平时开发所用到的软件也能运行在浏览器中,"用完即走"岂不妙哉?code-server 便是一个让人在浏览器运行 VS Cod ...

  5. Java枚举类与注解详解——一篇文章读懂枚举类与注解详

    目录 一.枚举类 ① 自定义枚举类 ② enum关键字定义枚举类 ③ enum 枚举类的方法 ④ enum 枚举类实现接口 二.注解 ① 生成文档相关注解 ②注解在编译时进行格式检查 ③注解跟踪代码的 ...

  6. MongoDB(12)- 查询嵌入文档的数组

    插入测试数据 db.inventory.insertMany( [ { item: "journal", instock: [ { warehouse: "A" ...

  7. Spring Cloud Alibaba(13)---Sleuth概述

    Sleuth概述 前言 在微服务架构中,众多的微服务之间互相调用,如何清晰地记录服务的调用链路是一个需要解决的问题.同时,由于各种原因,跨进程的服务调用失败时,运维人员希望能够通过 查看日志和查看服务 ...

  8. GO学习-(27) Go语言操作NSQ

    Go语言操作NSQ NSQ是目前比较流行的一个分布式的消息队列,本文主要介绍了NSQ及Go语言如何操作NSQ. NSQ NSQ介绍 NSQ是Go语言编写的一个开源的实时分布式内存消息队列,其性能十分优 ...

  9. RMAN CROSSCHECK命令 说明

    CROSSCHECK命令:     用于核对磁盘和磁带上的备份文件,以确保RMAN资料库与备份文件保持同步.注意:该命令只会检查RMAN资料库所记载的备份文件.当执行crosscheck命令时,如果资 ...

  10. HashMap源码:聊聊Map的遍历性能问题(一)

    目录 引言 迭代器测试 迭代器源码探究 其他遍历方法 增强型for循环 Map.forEach Stream.forEach 总结 附:四种遍历源码 附:完整测试类与测试结果+一个奇怪的问题 引言 今 ...