// 配置 def outputDir = project.file("cpp/header") def configFile = project.file("config/jni-classes.txt") // 日志函数 def log(msg) { println "[JNI] $msg" } def logError(msg) { println "[JNI ERROR] $msg" } def logWarn(msg) { println "[JNI WARN] $msg" } // 创建配置任务 tasks.register('createJniConfig') { group = 'jni' description = '创建 JNI 配置文件模板' doLast { configFile.parentFile.mkdirs() if (!configFile.exists()) { configFile.text = """# JNI 头文件生成配置 # 每行一个类全限定名,例如: # com.example.MyNativeClass # com.example.NativeUtils # 或者使用正则表达式自动匹配: # #auto:.*Native.* """ log "配置文件已创建: ${configFile.absolutePath}" log "请编辑此文件并添加包含 native 方法的类" } } } // 生成头文件任务 tasks.register('generateJniHeaders') { group = 'jni' description = '生成 JNI 头文件' dependsOn 'compileJava' doLast { // 确保目录存在 outputDir.mkdirs() // 读取配置 def targetClasses = [] if (configFile.exists()) { configFile.eachLine { line -> def trimmed = line.trim() if (trimmed && !trimmed.startsWith('#')) { targetClasses.add(trimmed) } } } if (targetClasses.isEmpty()) { logError "没有配置任何 JNI 类" logError "请先运行: ./gradlew createJniConfig" logError "然后编辑 ${configFile.absolutePath} 添加类名" return } log "开始生成 JNI 头文件..." log "目标类 (${targetClasses.size()} 个):" targetClasses.eachWithIndex { className, i -> log " ${i + 1}. $className" } // 准备类路径 def classesDir = project.sourceSets.main.output.classesDirs.singleFile def classpath = project.configurations.runtimeClasspath.asPath + File.pathSeparator + classesDir.absolutePath // 查找对应的 Java 源文件 def sourceFiles = [] def sourceDirs = project.sourceSets.main.java.srcDirs targetClasses.each { className -> def found = false def relativePath = className.replace('.', '/') + '.java' sourceDirs.each { srcDir -> def sourceFile = new File(srcDir, relativePath) if (sourceFile.exists()) { sourceFiles.add(sourceFile) found = true log "找到源文件: ${sourceFile.absolutePath}" } } if (!found) { logWarn "警告: 未找到类 $className 的源文件" } } if (sourceFiles.isEmpty()) { logError "错误: 未找到任何源文件" logError "请确保源文件存在于 src/main/java/ 目录中" return } // 使用 javac -h 命令(正确的方式) def javaHome = System.getProperty('java.home') def javacPath = "${javaHome}/bin/javac" if (!new File(javacPath).exists()) { javacPath = "${javaHome}/bin/javac" } log "使用 javac -h 生成头文件..." // 构建临时目录用于编译输出 def tempOutputDir = new File(project.buildDir, "tmp/jni-headers") tempOutputDir.mkdirs() try { // 方式1:逐个类生成(更可靠) def successCount = 0 def failCount = 0 sourceFiles.each { sourceFile -> try { // 构建 javac 命令 def processArgs = [ javacPath, '-h', outputDir.absolutePath, // 头文件输出目录 '-cp', classpath, // 类路径 '-d', tempOutputDir.absolutePath, // 类文件输出目录 sourceFile.absolutePath // 源文件 ] log "处理: ${sourceFile.name}" def process = processArgs.execute() def stdout = new StringBuilder() def stderr = new StringBuilder() process.consumeProcessOutput(stdout, stderr) def exitCode = process.waitFor() if (exitCode == 0) { successCount++ if (stdout.length() > 0) { log " 输出: ${stdout.toString().trim()}" } } else { failCount++ logError " 处理失败: ${sourceFile.name}" if (stderr.length() > 0) { logError " 错误: ${stderr.toString().trim()}" } } } catch (Exception e) { failCount++ logError " 处理异常: ${e.message}" } } log "处理完成: 成功 ${successCount} 个, 失败 ${failCount} 个" if (successCount > 0) { // 检查生成了哪些头文件 def headerFiles = outputDir.listFiles({ dir, name -> name.endsWith('.h') } as FilenameFilter) if (headerFiles && headerFiles.size() > 0) { log "=" * 60 log "JNI 头文件生成成功!" log "=" * 60 log "输出目录: ${outputDir.absolutePath}" log "生成的头文件 (${headerFiles.size()} 个):" headerFiles.sort { it.name }.each { file -> def size = file.length() def sizeStr = size < 1024 ? "${size} B" : "${String.format("%.1f", size / 1024.0)} KB" log " ✓ ${file.name} ($sizeStr)" // 显示文件开头几行 try { def lines = file.readLines() if (lines.size() > 0) { def headerGuard = lines.find { it.contains('#ifndef') } if (headerGuard) { log " 头文件保护: ${headerGuard.trim()}" } } } catch (Exception e) { // 忽略读取错误 } } log "=" * 60 log "🎉 头文件已成功生成!" log "" log "使用建议:" log " 1. 将生成的头文件复制到你的 C/C++ 项目中" log " 2. 在 C/C++ 源文件中包含这些头文件" log " 3. 实现头文件中声明的 JNI 函数" log "" log "示例 C++ 代码:" log " #include \"${headerFiles[0].name}\"" log " JNIEXPORT void JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv* env, jobject obj) {" log " // 你的实现代码" log " }" } else { logWarn "警告: 未生成任何 .h 头文件" logWarn "可能的原因:" logWarn " 1. 源文件中没有 native 方法声明" logWarn " 2. javac 版本不支持 -h 选项" logWarn " 3. 类路径配置不正确" } } else { logError "所有处理都失败,未生成任何头文件" } } finally { // 清理临时目录 tempOutputDir.deleteDir() } } } // 备选方案:使用传统 javah(如果 javac -h 失败) tasks.register('generateJniHeadersLegacy') { group = 'jni' description = '使用传统 javah 生成 JNI 头文件' dependsOn 'compileJava' doLast { outputDir.mkdirs() def targetClasses = [] if (configFile.exists()) { configFile.eachLine { line -> def trimmed = line.trim() if (trimmed && !trimmed.startsWith('#')) { targetClasses.add(trimmed) } } } if (targetClasses.isEmpty()) { logError "没有配置任何 JNI 类" return } log "使用传统 javah 生成头文件..." def classesDir = project.sourceSets.main.output.classesDirs.singleFile def classpath = project.configurations.runtimeClasspath.asPath + File.pathSeparator + classesDir.absolutePath def javaHome = System.getProperty('java.home') def javahPath = "${javaHome}/bin/javah" if (!new File(javahPath).exists()) { javahPath = "${javaHome}/../bin/javah" } if (!new File(javahPath).exists()) { logError "找不到 javah 工具" logError "请使用 Java 8-9 或使用 generateJniHeaders 任务" return } def processArgs = [javahPath, '-classpath', classpath, '-d', outputDir.absolutePath] processArgs.addAll(targetClasses) log "执行命令: ${processArgs.join(' ')}" def process = processArgs.execute() def exitCode = process.waitFor() if (exitCode == 0) { def files = outputDir.listFiles({ dir, name -> name.endsWith('.h') } as FilenameFilter) log "生成成功!创建了 ${files?.size() ?: 0} 个头文件" if (files) { files.each { log " - ${it.name}" } } } else { logError "生成失败" } } } // 扫描 native 方法任务 tasks.register('scanForNativeMethods') { group = 'jni' description = '扫描项目中的 native 方法' doLast { log "扫描项目中可能包含 native 方法的类..." def sourceDirs = project.sourceSets.main.java.srcDirs def foundClasses = [] sourceDirs.each { srcDir -> if (srcDir.exists()) { srcDir.eachFileRecurse(groovy.io.FileType.FILES) { file -> if (file.name.endsWith('.java')) { def content = file.text if (content.contains('native ') || content.contains(' native')) { // 提取类名 def packageMatch = content =~ /package\s+([\w.]+)\s*;/ def classMatch = content =~ /class\s+(\w+)/ if (packageMatch.find() && classMatch.find()) { def packageName = packageMatch.group(1) def className = classMatch.group(1) def fullClassName = "${packageName}.${className}" if (!foundClasses.contains(fullClassName)) { foundClasses.add(fullClassName) log "发现: $fullClassName" } } } } } } } if (foundClasses.isEmpty()) { log "未发现包含 native 方法的类" } else { log "=" * 60 log "发现 ${foundClasses.size()} 个可能包含 native 方法的类:" foundClasses.sort().eachWithIndex { cls, i -> log " ${i + 1}. $cls" } log "" log "你可以将这些类添加到 ${configFile.name} 中" } } } // 清理任务 tasks.register('cleanJniHeaders', Delete) { group = 'jni' description = '清理 JNI 头文件' delete outputDir doLast { log "已清理 JNI 头文件目录: ${outputDir.absolutePath}" } } // 验证任务 tasks.register('verifyJniSetup') { group = 'jni' description = '验证 JNI 配置' doLast { log "验证 JNI 配置..." log "Java 版本: ${System.getProperty('java.version')}" log "Java Home: ${System.getProperty('java.home')}" // 检查 javac def javaHome = System.getProperty('java.home') def javacPath = "${javaHome}/bin/javac" def javacExists = new File(javacPath).exists() || new File("${javaHome}/../bin/javac").exists() log "javac 工具: ${javacExists ? '找到 ✓' : '未找到 ✗'}" // 检查 javah(传统方式) def javahPath = "${javaHome}/bin/javah" def javahExists = new File(javahPath).exists() || new File("${javaHome}/../bin/javah").exists() log "javah 工具: ${javahExists ? '找到 ✓' : '未找到 ✗'}" // 检查配置文件 if (configFile.exists()) { def classes = configFile.readLines() .findAll { it.trim() && !it.trim().startsWith('#') } log "配置文件: 已找到 (${classes.size()} 个类)" if (classes.size() > 0) { classes.each { log " - $it" } } } else { log "配置文件: 未找到 ✗" } // 检查输出目录 log "输出目录: ${outputDir.absolutePath}" } } // 帮助任务 tasks.register('jniHelp') { group = 'help' description = 'JNI 帮助' doLast { println """ ${'=' * 70} JNI 头文件生成系统 ${'=' * 70} 📋 概述: 为包含 native 方法的 Java 类生成 C/C++ 头文件。 🚀 快速开始: 1. ./gradlew createJniConfig # 创建配置文件 2. 编辑 config/jni-classes.txt # 添加你的 JNI 类 3. ./gradlew generateJniHeaders # 生成头文件(推荐) 4. 头文件输出到: cpp/header/ 🔧 可用任务: jniHelp - 显示此帮助 createJniConfig - 创建配置文件 generateJniHeaders - 生成头文件(现代方式) generateJniHeadersLegacy - 传统方式(Java 8-9) scanForNativeMethods - 扫描 native 方法 verifyJniSetup - 验证配置 cleanJniHeaders - 清理生成的文件 📝 配置文件格式 (config/jni-classes.txt): # 注释 com.example.MyNativeClass # 直接指定类名 #auto:.*Native.* # 自动匹配(以 #auto: 开头) ⚠️ 注意事项: • 确保类中包含 native 方法声明 • 先编译项目再生成头文件 • 对于 Java 10+ 使用 generateJniHeaders • 对于 Java 8-9 使用 generateJniHeadersLegacy 🔍 调试: • 运行 verifyJniSetup 检查环境 • 运行 scanForNativeMethods 发现 native 类 • 确保源文件中有 native 关键字 ${'=' * 70} """ } } // 可选:自动集成到构建 // tasks.named('build') { // dependsOn tasks.named('generateJniHeaders') // } // 清理时包含 JNI 头文件 tasks.named('clean') { dependsOn tasks.named('cleanJniHeaders') } log "JNI 模块已加载" log "输出目录: ${outputDir.absolutePath}" log "配置文件: ${configFile.absolutePath}" log "使用 ./gradlew jniHelp 查看详细帮助"