从 kotlin 多平台项目创建 fat jar

Posted

技术标签:

【中文标题】从 kotlin 多平台项目创建 fat jar【英文标题】:Create fat jar from kotlin multiplatform project 【发布时间】:2019-12-01 18:39:18 【问题描述】:

我最近从旧的 1.2 多平台切换到 1.3。不同之处在于,每个多平台模块只有一个 build.gradle 文件(我有 5 个),因此配置要少得多。 但是,我似乎无法配置创建具有来自 jvm 平台的所有依赖项的可运行胖 jar。 我曾经在我的 jvm 项目和 jar 任务中使用标准的“应用程序”插件,但这不再起作用了。我发现有“jvmJar”任务并对其进行了修改(设置 Main-class),但创建的 jar 不包含依赖项并在 ClassNotFoundException 上崩溃。我该怎么做?

这就是我现在拥有的:

    jvm() 
        jvmJar 
            manifest 
                attributes 'Main-Class': 'eu.xx.Runner'
            
            from  configurations.compile.collect  it.isDirectory() ? it : zipTree(it)  
        

    

【问题讨论】:

也许ShadowJar 会帮助你? 是的,可能。仍然不确定如何处理它。 "每个多平台模块只有一个 build.gradle 文件" 你有这个来源吗?文档很糟糕,因为我怀疑您会同意,而且我从未听说过这样的事情是可能的。 【参考方案1】:

我确实遇到了这个问题并使用了这个方法。

1。重组你的项目

让我们将您的项目称为Project

创建另一个子模块,比如 subA,它将具有 gradle 符号 Project:subA

现在,subA 在其 build.gradle 中有您的多平台代码(它是带有 apply :kotlin-multiplafrom 的 gradle 项目)

2。添加另一个子模块

创建另一个仅针对 jvm 的子模块,例如 subB,它将具有 gradle 符号 Project:subB

所以,subB 将有插件:'application''org.jetbrains.kotlin.jvm'

3。将您的模块添加为 gradle 依赖项(请参阅我的 build.gradle)

plugins 
    id 'org.jetbrains.kotlin.jvm' version '1.3.31'
    id "application"


apply plugin: "kotlinx-serialization"

group 'tz.or.self'
version '0.0.0'

mainClassName = "com.example.MainKt"

sourceCompatibility = 1.8

compileKotlin 
    kotlinOptions.jvmTarget = "1.8"


dependencies 
    implementation project(':subA')

您可以像常规 java 项目一样继续并构建 subB,甚至可以使用现有的插件,它会工作

【讨论】:

为什么有2个项目?为什么它们必须是子项目?不过,我喜欢一般的想法。我尝试使用 kotlin/jvm/jar 设置 + 对 project 的依赖创建与 project 相同级别的另一个模块,并且效果很好。 如果它运行良好,请慢慢接受答案 目前,kotlin-multiplatform 插件可以很好地与应用程序插件配合使用,但是如果您开始以 android 为目标,您将会遇到插件冲突(如果您使用 com.android.library 或 com.android.在您的 mpp 中的应用程序)。我只需要使用它作为解决方法。此外,我尝试使用 kotlin-multiplatform 目标配置应用程序插件但没有成功 不想接受你的回答,因为这不是我最终的结果。正如我在评论中提到的那样,我的方法确实很有效,基于你有单独项目的想法,但不完全。【参考方案2】:

让它与 kotlin 1.3.61 中的多平台插件一起使用:

以下内容适用于src/jvmMain/kotlin/com/example/Hello.kt中的主文件

Hello.kt 还必须将其包指定为package com.example

我以这种方式配置了我的 jvm 目标:

kotlin 
    targets 
        jvm()

        configure([jvm])  
            withJava()
            jvmJar 
                manifest 
                    attributes 'Main-Class': 'com.example.HelloKt'
                
                from  configurations.runtimeClasspath.collect  it.isDirectory() ? it : zipTree(it)  
            
        
    

【讨论】:

runtimeClasspath 不适用于我的多平台(应用程序)项目,因为它是空的。所以我找到并使用了 jvmRuntimeClasspath,它运行良好。【参考方案3】:

让它与 luca992 所做的稍作修改的版本一起工作:

kotlin 
jvm() 
    withJava()
    jvmJar 
        manifest 
            attributes 'Main-Class': 'sample.MainKt'
        
        from  configurations.runtimeClasspath.collect  it.isDirectory() ? it : zipTree(it)  
    

...

【讨论】:

【参考方案4】:

让 gradle/multiplatform 工作的唯一方法似乎是无休止的反复试验;这是一场噩梦,它不是作为“构建”系统构建的,而是作为“构建系统”构建的;换句话说,这两个工具(一起或单独)是一种仅实现插件制造商预期的单个软件开发生命周期的方法,但是,如果您设计了所需的软件生命周期和 CI/CD 系统,并且现在您尝试实现该工程,使用这些工具执行此操作将比使用脚本、代码或 maven 执行此操作要困难得多。造成这种情况的原因有很多:

由于插件制造商只公开了最低可配置性,编码约定发生了巨大变化,可能只允许访问他们自己的个人项目所需的东西。 非常糟糕的文档更新; Kotlin、gradle 和插件的变化如此之快,我开始严重质疑这些工具的实用性。

因此,在撰写本文时,这似乎是使用 kotlin 1.3.72、多平台 1.3.72、ktor 1.3.2 和 gradle 6.2.2(使用 kts 格式)时使用的正确语法。

注意 fatJar 似乎组装正确但无法运行,它找不到类,所以我包含了我同时使用的第二个 runLocally 任务。

这不是一个完整的解决方案,所以我讨厌在这里发布它,但据我所知......这是我在任何地方都能找到的最完整和最新的解决方案。

//Import variables from gradle.properties
val environment: String by project
val kotlinVersion: String by project
val ktorVersion: String by project
val kotlinExposedVersion: String by project
val mysqlConnectorVersion: String by project
val logbackVersion: String by project
val romeToolsVersion: String by project
val klaxonVersion: String by project
val kotlinLoggingVersion: String by project
val skrapeItVersion: String by project
val jsoupVersion: String by project
val devWebApiServer: String by project
val devWebApiServerVersion: String by project

//Build File Configuration
plugins 
    java
    kotlin("multiplatform") version "1.3.72"


group = "com.app"
version = "1.0-SNAPSHOT"

repositories 
    mavenCentral()
    jcenter()
    jcenter 
        url = uri("https://kotlin.bintray.com/kotlin-js-wrappers")
    
    maven 
        url = uri("https://jitpack.io")
    


//Multiplatform Configuration
kotlin 
    jvm 
        compilations 
            val main = getByName("main")
            tasks 
                register<Jar>("buildFatJar") 
                    group = "application"
                    manifest 
                        attributes["Implementation-Title"] = "Gradle Jar File Example"
                        attributes["Implementation-Version"] = archiveVersion
                        attributes["Main-Class"] = "com.app.BackendAppKt"
                    
                    archiveBaseName.set("$project.name-fat")
                    from(main.output.classesDirs, main.compileDependencyFiles)
                    with(jar.get() as CopySpec)
                
                register<JavaExec>("runLocally") 
                    group = "application"
                    setMain("com.app.BackendAppKt")
                    classpath = main.output.classesDirs
                    classpath += main.compileDependencyFiles
                
            
        
    
    js 
        browser  EXCLUDED FOR LENGTH 
    
    sourceSets  EXCLUDED FOR LENGTH 

【讨论】:

哇,真不敢相信我的运气你昨天才回答这个问题。 The only way to get gradle/multiplatform working appears to be endless trial and error 不能再同意了! Note the fatJar seems to assemble correctly but won't run, it can't find the class 我使用 jar tvf &lt;project.name&gt;-fat-&lt;version&gt;.jar 检查了 jar 的内容,我注意到存档中有一堆 .jar 文件。我不是专家,我已经有一段时间没有使用胖罐子了,但我猜胖罐子应该只有 .class 文件我会深入挖掘,但也许你已经知道了? 所以docs 建议允许罐子内的罐子。我尝试通过添加attributes["Class-Path"] = main.compileDependencyFiles.map it.name .joinToString(separator = " ") 来添加类路径,但这也不起作用。在检查 MANIFEST.MF 时,我发现唯一奇怪的是在第 72 列添加了换行符(奇怪!)。基本上我正在努力让java -jar &lt;fat-jar&gt; 工作 我目前使用的解决方法,FWIW,是获取由您的代码创建的 jar,使用 jar xvf &lt;fat-jar.jar&gt; 解压它,然后使用 java -cp "./*:." com.app.BackendAppKt 运行它。所以这可能取决于 manifest.mf 是如何生成的。不用说,我离拔头发还有一步之遥 实际上完全解决了另一个类似的问题。我想我在这里缺少 withJava,这就是为什么它没有运行构建,几乎,构建正确。这里有更多信息:***.com/a/62770101/2550940

以上是关于从 kotlin 多平台项目创建 fat jar的主要内容,如果未能解决你的问题,请参考以下文章

如何在多平台多项目 Kotlin 构建中向另一个项目的测试添加依赖项

如何使用单个build.gradle配置向kotlin多平台项目添加依赖项

无法在 commonMain 中为 kotlin 多平台使用依赖项

Scala:运行fat jar时找不到主类

如何使用 IntelliJ 制作具有所有依赖项的 jar 文件,也就是 Fat jar

Gradle:使用 Spring Boot 依赖项构建“fat jar”