首页 文章

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

提问于
浏览
2

此问题与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-runtime必须在类路径中并使用$ echo $ CLASSPATH进行验证 . 或者你必须将kotlin-runtime添加到maven,然后使用mvn编译程序集在jar本身内组装:single,

这意味着kotlin-runtime不是stdlib的一部分:

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

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

另外,用 compile 替换 implementation 会修复它 .

在我在问题顶部链接的帖子中,有一个建议在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 . 此外, compile 已被弃用,支持 implementationapi ,这就是为什么在声明依赖项不是一个选项时使用 compile 而不是 implementation .

1 回答

  • 3

    你有没有试过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]
    }
    

    编辑:

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

    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 code发出通知 runtimeClasspathruntimeOnlyruntimeimplementation 的组合,根据具体情况可能会或可能不合适 - 例如,您可能不希望包含 runtime 依赖项,因为它们是由容器提供的 .

相关问题