代码之家  ›  专栏  ›  技术社区  ›  Zoe - Save the data dump

如何使用Gradle4.4创建一个包含所有依赖项的jar?

  •  7
  • Zoe - Save the data dump  · 技术社区  · 6 年前

    这个问题与 this one 是的。

    我完成了毕业任务:

    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运行很好。但是,当我部署到我的覆盆子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在jar内部进行组装:single,

    这意味着kotlin运行时不包含在类路径中。在回答“将Kotlin运行时添加到依赖项”之前,它是 stdlib 以下内容:

    Kotlin运行时(已弃用,请改用Kotlin stdlib项目)

    我用 kotlin-stdlib-jdk8 它在IDE中工作。只是为了测试,使用 kotlin-stdlib 相反,不会改变任何事情。

    另外,更换 implementation 具有 compile 修复它。

    在我链接到问题顶部的帖子中,有一个建议 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
    }
    

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

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

    我试过了。我的结局是:

    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
    }
    

    还有一个例外:

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

    因此,考虑到:

    • 编译 而不是 实施 作品
    • 运行时异常来自不在类路径中的kotlin
    • 它在IDE中工作
    • 添加 实施 条款 fatJar 任务导致编译崩溃,并出现单独的异常

    当使用 实施 关键词?


    解决建议的重复项:它只适用于compile关键字,而不是 实施 是的。也, 编译 已被弃用 实施 api ,这就是为什么使用 编译 而不是 实施 当声明依赖项不是一个选项时。

    1 回复  |  直到 5 年前
        1
  •  10
  •   Konrad Botor    6 年前

    你试过 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 code 那个 runtimeClasspath runtimeOnly 我是说, runtime implementation ,可能需要,也可能不需要,具体取决于具体情况-例如,您可能不希望包括 运行时 依赖项,因为它们是由容器提供的。