GraalVM Native Image无法找到FatJar中依赖库问题求助
问题:FatJar构建成功但GraalVM Native Image生成时找不到Clikt依赖类的解决方法
环境信息
macOS Sonoma 14.5上的GraalVM版本:
java -version java version "21" 2023-09-19 Java(TM) SE Runtime Environment Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15) Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15, mixed mode, sharing)
Gradle版本:
gradle --v ------------------------------------------------------------ Gradle 8.7 ------------------------------------------------------------ Build time: 2024-03-22 15:52:46 UTC Revision: 650af14d7653aa949fce5e886e685efc9cf97c10 Kotlin: 1.9.22 Groovy: 3.0.17 Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023 JVM: 21 (Oracle Corporation 21+35-jvmci-23.1-b15) OS: Mac OS X 14.5 aarch64
build.gradle.kts配置:
plugins { kotlin("jvm") version "1.9.23" } group = "com.example" version = "1.0-SNAPSHOT" tasks.withType<Jar> { manifest { attributes["Main-Class"] = "com.example.SampleCli" } } repositories { mavenCentral() } dependencies { implementation("com.github.ajalt.clikt:clikt:4.3.0") testImplementation(kotlin("test")) } tasks.test { useJUnitPlatform() } kotlin { jvmToolchain(21) } fun getClasspath() = "libs/" + file("$buildDir/libs").list()?.joinToString(":libs/") task<Exec>("buildNativeImage") { description = "Build native image using GraalVM" dependsOn(tasks.getByName("build")) workingDir(buildDir) commandLine( "native-image", "-cp", getClasspath(), "-H:+ReportExceptionStackTraces", "-H:+AddAllCharsets", // "--report-unsupported-elements-at-runtime", // "--allow-incomplete-classpath", "--no-server", "--no-fallback", "--enable-http", "--enable-https", "com.example.SampleCli", "com.example" ) }
主类代码:
package com.example import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.help import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.prompt import com.github.ajalt.clikt.parameters.types.int class SampleCli() : CliktCommand(name="cli") { val count: Int by option().int().default(1).help("Number of greetings") val name: String by option().prompt("Your name").help("The person to greet") override fun run() { repeat(count) { echo("Hello $name!") } } } fun main(args: Array<String>) = SampleCli().main(args)
问题现象
执行./gradlew build构建项目时,输出:
BUILD SUCCESSFUL in 647ms 3 actionable tasks: 3 executed
但执行GraalVM Native Image生成命令(或./gradlew buildNativeImage)时失败,执行的命令:
native-image -jar build/libs/SampleCli-1.0-SNAPSHOT.jar cli
报错信息:
======================================================================================================================== GraalVM Native Image: Generating 'cli' (executable)... ======================================================================================================================== [1/8] Initializing... (0.0s @ 0.15GB) The build process encountered an unexpected error: > java.lang.NoClassDefFoundError: com/github/ajalt/clikt/core/CliktCommand Please inspect the generated error report at: /Users/user1/SampleProject/svm_err_b_20240603T04124.720_pid14052.md If you are unable to resolve this problem, please file an issue with the error report at: https://graalvm.org/support
原因分析
- 现有
Jar任务生成的是普通Jar包,而非包含所有依赖的FatJar(uber-Jar),因此Jar包中只包含项目自身代码,没有Clikt等第三方依赖类。 buildNativeImage任务中的getClasspath()函数逻辑错误:它试图从build/libs目录下找依赖Jar,但Gradle默认不会把依赖包复制到该目录,导致类路径不完整。
解决步骤
步骤1:修改Gradle配置,生成FatJar
替换原有的tasks.withType<Jar>配置,使用shadowJar插件生成包含所有依赖的FatJar:
plugins { kotlin("jvm") version "1.9.23" id("com.github.johnrengelman.shadow") version "8.1.1" // 添加shadow插件 } // 替换原Jar任务配置,用shadowJar配置主类 tasks.shadowJar { manifest { attributes["Main-Class"] = "com.example.SampleCli" } archiveBaseName.set("SampleCli") // 自定义Jar包名称 archiveVersion.set("1.0-SNAPSHOT") } // 让build任务依赖shadowJar,确保构建时生成FatJar tasks.build { dependsOn(tasks.shadowJar) }
步骤2:修复buildNativeImage任务的类路径逻辑
修改getClasspath()函数,或者直接使用FatJar路径作为类路径:
task<Exec>("buildNativeImage") { description = "Build native image using GraalVM" dependsOn(tasks.shadowJar) // 依赖shadowJar而非build val shadowJar = tasks.shadowJar.get() commandLine( "native-image", "-jar", shadowJar.archiveFile.get().asFile.absolutePath, // 直接使用FatJar "-H:+ReportExceptionStackTraces", "-H:+AddAllCharsets", "--no-server", "--no-fallback", "--enable-http", "--enable-https", "cli" // 生成的可执行文件名称 ) }
步骤3:重新构建并生成Native Image
执行以下命令:
./gradlew buildNativeImage
额外说明
- 使用
shadowJar插件是Gradle生成FatJar的标准方式,能自动打包所有依赖到单个Jar包中。 - 直接用
-jar参数指定FatJar给native-image命令,无需手动拼接类路径,避免路径错误。
内容的提问来源于stack exchange,提问作者PacificNW_Lover
相关产品推荐
相关产品推荐

