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

Zoe*_*Zoe 7 java gradle kotlin

这个问题关系到这一个.

我得到了这个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
}
Run Code Online (Sandbox Code Playgroud)

并且只有一个依赖项,将测试依赖项放在一边:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    testImplementation group: 'junit', name: 'junit', version: '4.12'
}
Run Code Online (Sandbox Code Playgroud)

从IDE运行正常.但是,当我部署到我的Raspberry Pi(或gradlew fatJar在本地使用jar 结果)时,我得到以下异常:

$ 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
Run Code Online (Sandbox Code Playgroud)

这是由这一行触发的:

return Optional.empty()
Run Code Online (Sandbox Code Playgroud)

在Kotlin方法中返回Optional<File>.回答相关问题:

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

或者你必须将kotlin-runtime添加到maven,然后使用mvn编译程序集在jar本身内组装:single,

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

Kotlin Runtime(不推荐使用,改为使用kotlin-stdlib工件)

我使用kotlin-stdlib-jdk8它,它在IDE中工作.仅用于测试目的,使用kotlin-stdlib而不是改变任何东西.

另外,用implementationcompile修复它.

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

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
}
Run Code Online (Sandbox Code Playgroud)

仍然不包括依赖项,程序崩溃.

那么为什么不将实现添加为配置来复制呢?

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

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
}
Run Code Online (Sandbox Code Playgroud)

还有一个例外:

不允许直接解析配置"实现"

因此,考虑:

  • compile而不是implementation工作
  • 运行时异常来自Kotlin不在类路径中
  • 它适用于IDE
  • 向任务添加implementation子句fatJar会使编译崩溃,并出现单独的异常

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


解决建议的副本:它只适用于compile关键字,而不是implementation.此外,compile已被弃用赞成implementationapi,这就是为什么使用compile而不是implementation声明依赖不是一个选项.

Kon*_*tor 11

您是否尝试过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]
}
Run Code Online (Sandbox Code Playgroud)

编辑:

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

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
}
Run Code Online (Sandbox Code Playgroud)

但是你必须重复所有的实现依赖关系作为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
}
Run Code Online (Sandbox Code Playgroud)

但是,应通知按照源代码,runtimeClasspath是一个组合runtimeOnly,runtime并且implementation,可能会或可能不希望根据情况-例如,你可能不希望包括runtime依赖性,因为它们是由容器提供.