【问题标题】:How do I create a jar with all dependencies using Gradle 4.4?如何使用 Gradle 4.4 创建一个包含所有依赖项的 jar?
【发布时间】:2018-06-26 21:02:41
【问题描述】:

这个问题与this one 有关——但是,由于compileimplementation 弃用,它不起作用。它确实拾取使用compile 声明的依赖项。然而,随着它被弃用,使用它不是一种选择(无论如何,当它被删除时我们会马上回到这里)


我得到了这个 Gradle 任务:

task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'rpi-sense-hat-lib',
                'Implementation-Version': version,
                'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
    }
    baseName = project.name
    from { 
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    with jar
}

而且只有一个依赖,忽略测试依赖:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    testImplementation group: 'junit', name: 'junit', version: '4.12'
}

从 IDE 运行可以正常工作。但是,当我部署到我的 Raspberry Pi(或在本地使用 jar gradlew fatJar 结果)时,我得到了这个异常:

$ java -jar java-sense-hat-1.0a.jar
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
    at io.github.lunarwatcher.pi.sensehat.UtilsKt.getSenseHat(Utils.kt:18)
    at io.github.lunarwatcher.pi.sensehat.SenseHat.<init>(SenseHat.java:12)
    at io.github.lunarwatcher.pi.sensehat.Tests.main(Tests.java:9)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
    ... 3 more

由这行触发:

return Optional.empty()

在返回 Optional&lt;File&gt; 的 Kotlin 方法中。 Answer on related question:

kotlin-runtime 必须在类路径中并使用 $ echo $CLASSPATH 进行验证。

或者您必须将 kotlin-runtime 添加到 maven,然后使用 mvn compile assembly:single 在 jar 内部组装,

这意味着 kotlin-runtime 不包含在类路径中。在您继续回答“将 kotlin-runtime 添加到您的依赖项”之前,它是 stdlib 的一部分:

Kotlin 运行时(已弃用,改用 kotlin-stdlib 工件)

我使用kotlin-stdlib-jdk8,它可以在 IDE 中运行。仅出于测试目的,使用kotlin-stdlib 不会改变任何内容。

此外,将implementation 替换为compile 可以解决此问题。

在我在问题顶部链接的帖子中,建议在 fatJar 任务中使用 runtime。所以:

task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'rpi-sense-hat-lib',
                'Implementation-Version': version,
                'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
    }
    baseName = project.name
    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
        configurations.runtime.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    with jar
}

依赖仍然没有包含,程序崩溃了。

那么为什么不将实现添加为要从中复制的配置呢?

我试过了。我最终得到了这个:

task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'rpi-sense-hat-lib',
                'Implementation-Version': version,
                'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
    }
    baseName = project.name
    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
        configurations.runtime.collect {
            it.isDirectory() ? it : zipTree(it)
        }
        configurations.implementation.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    with jar
}

还有一个例外:

不允许直接解析配置“实现”

所以,考虑到:

  • compile 代替 implementation 有效
  • 运行时异常来自 Kotlin 不在类路径中
  • 它在 IDE 中工作
  • fatJar 任务添加implementation 子句会导致编译崩溃并出现单独的异常

使用implementation 关键字时,如何在 Gradle 4.4 中生成包含所有依赖项的 jar?

【问题讨论】:

  • @ShafinMahmud 阅读了这个问题。它不适用于 implementation 关键字
  • 如果你改写你的问题标题来更具体地描述问题会很好
  • @ShafinMahmud 您是否因为标题而标记为重复?帖子的内容涵盖了整个问题,包括为什么链接的问题不起作用
  • 但是您的问题标题应该与现有的标题区分开来。无意冒犯,应该有一个清晰的标题来总结您的问题,如果已经有相关内容,则不是通用的。这将导致人们为您提供更多帮助

标签: java gradle kotlin


【解决方案1】:

你试过Shadow Plugin喜欢吗:

shadowJar {
  manifest {
     attributes 'Implementation-Title': 'rpi-sense-hat-lib',
            'Implementation-Version': version,
            'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
  }
  configurations = [project.configurations.compile, project.configurations.runtime]
}

编辑:

您也可以这样做(如question 的答案中所述):

configurations {
    fatJar
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    testImplementation group: 'junit', name: 'junit', version: '4.12'

    fatJar "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'rpi-sense-hat-lib',
                'Implementation-Version': version,
                'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
    }
    baseName = project.name
    from {
        configurations.fatJar.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    with jar
}

但是您必须将所有实现依赖项重复为 fatJar 依赖项。对于您当前的项目,这很好,因为您只有一个依赖项,但对于任何更大的项目,它都会变得一团糟......

编辑 2:

正如@Zoe 在 cmets 中指出的,这也是一个可以接受的解决方案:

task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'rpi-sense-hat-lib',
                   'Implementation-Version': version,
                   'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
    }
    baseName = project.name
    from {
        configurations.runtimeClasspath.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    with jar
}

但是,请注意 source code runtimeClasspathruntimeOnlyruntimeimplementation 的组合,这可能会也可能不会根据情况而定 - 例如您可能不想包括 runtime 依赖,因为它们是由容器提供的。

【讨论】:

  • 当我删除 configurations 并添加 with jar 时它可以工作。否则它仍然会因缺少的库而崩溃。没有插件就没有办法吗?
  • 找到了一些东西(因为您已经有了答案,所以将其作为评论发布)。我找到了this。您的编辑提醒我配置是在某处定义的,我还看到了 java-library 插件的图表(虽然我不使用它)提醒我可能存在继承。
  • 该链接包含实现的定义和一些其他配置,但该行本身是运行时类路径。在 fatJar 中使用 runtimeClasspath 修复了它。使用插件当然也是一种选择,但我不想这样做
猜你喜欢
  • 2014-01-08
  • 2014-07-21
  • 2018-06-30
  • 2012-04-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 2015-11-15
  • 1970-01-01
相关资源
最近更新 更多