如何使用 Gradle 创建具有实现依赖项的可执行胖 jar

Posted

技术标签:

【中文标题】如何使用 Gradle 创建具有实现依赖项的可执行胖 jar【英文标题】:How do I create an executable fat JAR with Gradle with implementation dependencies? 【发布时间】:2018-08-23 00:02:09 【问题描述】:

我在 Gradle 4.6 中有一个简单的项目,并且想制作一个可执行的 jar。我试过shadowgradle-fatjar-plugingradle-one-jarspring-boot-gradle-plugin 插件,但它们都没有添加我声明为implementation 的依赖项(我没有任何compile 的)。它适用于compile,例如对于gradle-one-jar 插件,但我想拥有implementation 依赖项。

【问题讨论】:

【参考方案1】:

根据接受的答案,我需要再添加一行代码:

task fatJar(type: Jar) 
  manifest 
    attributes 'Main-Class': 'com.yourpackage.Main'
  
  archiveClassifier = "all"
  from 
    configurations.compile.collect  it.isDirectory() ? it : zipTree(it) 
    configurations.runtimeClasspath.collect  it.isDirectory() ? it : zipTree(it) 
    
  with jar

没有这一行,它省略了我的源文件,只添加了依赖项:

configurations.compile.collect  it.isDirectory() ? it : zipTree(it) 

对于较新的 gradle (7+),您可能会看到此错误:

Execution failed for task ':fatJar'.
> Entry [some entry here] is a duplicate but no duplicate handling strategy has been set. Please 
refer to https://docs.gradle.org/7.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy
 for details.

如果发生这种情况,请将 duplicatesStrategy 添加到 fatJar 任务中,例如 duplicatesStrategy "exclude"

同样,对于 Gradle 7+,您只需删除 configuration.compile.collect 行,因为它在此版本的 gradle 中不再是有效配置。

【讨论】:

这在 Gradle 6.0.1 中所有其他人都没有的地方有效。奇怪的是,Gradle 4.x 中似乎不需要第二行,但是一旦升级到 Gradle 6 或 5,我的旧构建配置突然停止创建胖 JAR。 Gradle 6.5 报告以下警告:编译配置已被弃用以解决问题。这将在 Gradle 7.0 中失败并出现错误。请改为解析 compileClasspath 配置。用“compileClasspath”替换“compile”修复了它。 工作得很好,不知道你是怎么找到这个的,但你在做上帝的工作!! @jbel 谢谢!我通过结合另一个帖子的信息偶然发现它 对我来说有点奇怪,尝试运行一个 github 项目(已使用 gradle 包装器设置)并不断收到依赖项的“classdefnotfoundexception”错误。我检查了子模块中的 gradle.build 文件,发现依赖项是使用“实现”关键字(而不是“编译”)导入的。一旦我为 jar 任务添加了“configuration.runtimeClasspath”行,问题就解决了。【参考方案2】:

同样的任务可以使用Gradle Kotlin DSL以类似的方式完成:

val jar by tasks.getting(Jar::class) 
    manifest 
        attributes["Main-Class"] = "com.package.YourClass"
    

    from(configurations
        .runtime
        // .get() // uncomment this on Gradle 6+
        // .files
        .map  if (it.isDirectory) it else zipTree(it) )

【讨论】:

【参考方案3】:

Kotlin 1.3.72 & JVM 插件,Gradle 6.5.1

所有这些平台的语法都在迅速变化

tasks 
    compileKotlin 
        kotlinOptions.jvmTarget = "1.8"
    
    compileTestKotlin 
        kotlinOptions.jvmTarget = "1.8"
    
    val main = sourceSets.main.get()
    //TODO
    register<Jar>("buildFatJar") 
        group = "app-backend"
        dependsOn(build)
        // shouldRunAfter(parent!!.tasks["prepCopyJsBundleToKtor"]) -> This is for incorporating KotlinJS gradle subproject resulting js file.
        manifest 
            attributes["Main-Class"] = "com.app.app.BackendAppKt"
        

        from(configurations.compileClasspath.get().files.map  if (it.isDirectory) it else zipTree(it) )
        with(jar.get() as CopySpec)
        archiveBaseName.set("$project.name-fat")
    

【讨论】:

【参考方案4】:

from configurations.runtimeClasspath.collect it.isDirectory() ? it : zipTree(it)

这条线对我来说很重要。

【讨论】:

【参考方案5】:

您可以使用以下代码。

jar 
    manifest 
        attributes(
                'Main-Class': 'com.package.YourClass'
        )
    
    from 
        configurations.runtimeClasspath.collect  it.isDirectory() ? it : zipTree(it) 
    
 

确保将com.package.YourClass 替换为包含static void main( String args[] ) 的完全限定类名。

这将打包运行时依赖项。如果您需要更多信息,请查看docs。

【讨论】:

fat jar 的名称是什么,我在哪个目录中找到它? @VijayKumar 矿位于build/libs 之下。 您可能想使用configurations.compileClasspath.filter it.exists() .collect it.isDirectory() ? it : zipTree(it) 。这样,如果您有不存在的类路径条目,它就不会挂断。我正在使用 Kotlin,所以 java 源类路径条目没有任何内容,而且它变得非常暴躁。 当我使用这段代码时,所有的库类都包含在uberjar中,但源目录中的类没有。 伙计,你为我节省了很多时间。网上有一个howto,但是有问题:)

以上是关于如何使用 Gradle 创建具有实现依赖项的可执行胖 jar的主要内容,如果未能解决你的问题,请参考以下文章

Gradle 无法构建具有 Docker 依赖项的 JAR

在 Gradle 中,如何生成具有解析为实际使用版本的动态依赖项的 POM 文件?

创建具有 3rd 方依赖项的可安装 Django 应用程序

如何使用 spring boot 插件 2.0.x 从一个具有不同依赖项的 gradle 项目生成 2 个 jars

如何找到依赖项的 gradle 实现路径?

如何使用 Maven 创建具有依赖关系的可执行 JAR?