如何使用Gradle 4.4创建具有所有依赖项的jar?

时间:2018-06-26 21:02:42

标签: java gradle kotlin

此问题与this one有关。

我完成了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()

在Kotlin方法中返回Optional<File>Answer on related question

  

kotlin运行时都必须位于类路径中,并使用$ echo $ CLASSPATH进行验证。

     

或者您必须将kotlin-runtime添加到maven,然后使用mvn compile assembly:single

在jar本身中进行汇编

这意味着kotlin运行时未包含在类路径中。在继续回答“将kotlin运行时添加到依赖项”之前,它是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中使用
  • implementation任务中添加fatJar子句会使编译失败,并带有单独的异常

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


解决建议的重复项:仅适用于compile关键字,不适用于implementation。此外,不赞成使用compileimplementation来支持api,这就是为什么在声明依赖性不是选项时使用compile而不是implementation的原因。

1 个答案:

答案 0 :(得分:8)

您是否尝试过像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在评论中指出的那样,这也是可以接受的解决方案:

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 coderuntimeClasspathruntimeOnlyruntimeimplementation的组合,根据是否需要,可能取决于这种情况-例如,您可能不希望包含runtime依赖项,因为它们是由容器提供的。