【问题标题】:Create fat jar from ktor Kotlin multiplatform project with Kotlin Gradle DSL使用 Kotlin Gradle DSL 从 ktor Kotlin 多平台项目创建 fat jar
【发布时间】:2020-07-29 10:23:48
【问题描述】:

尝试为包含 ktor 服务器组件的 kotlin 多平台项目创建可运行的 jar,使用 Kotlin Gradle DSL 构建。

我见过几个问题,包括Create fat jar from kotlin multiplatform project,它询问并回答了如何在 Groovy 中创建 gradle 构建文件,但是如何在 kotlin dsl 中创建呢?

报告工作的 groovy 代码是:

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

这将如何转化为 Kotlin DSL?我尝试了很多变体,其中一些可以编译并运行,但没有创建所需的输出......一个可运行的 jar。

【问题讨论】:

  • 值得注意的是恕我直言,多平台破坏了许多插件,包括谷歌应用引擎。在用它开发 4 个月后,不幸的是,主要是“打开”它而不是“用”它,我正在尝试别的东西。 gralde 子项目...我认为我可以以更传统的设计方式完成同样的事情(仅 8 小时,但到目前为止我可以灵活地使用它)。

标签: gradle-kotlin-dsl ktor kotlin-multiplatform


【解决方案1】:

我花了 3 天的时间试图让胖罐子工作,下面是解决方案,在解决方案澄清之前如下:

我早早犯的错误

  • 不应该冲进docker,应该先让fat jar在本地工作。
  • withJava() 被忽略了,这主要是浪费了 36 个工时... WTF 是这个函数的重点吗?
  • dependsOn(build):为什么jar任务类型不知道这个我已经不明白了。
  • main.compileDependencyFiles:我用这个代替了下面参数中的映射。
  • main.output.classesDirs:在其他解决方案中缺失,似乎包含您的实际代码。

注意:不需要影子插件,这太棒了(恕我直言,gradle 插件往往不能很好地一起玩)。

注意:版本控制很重要,因为在某些情况下,此堆栈的更改速度似乎比文档快约 50 倍,此解决方案使用了以下内容:

  • Kotlin 1.3.72
  • Gradle 6.5
  • Ktor 1.3.2

代码

//Import variables from gradle.properties
val environment: String by project
val kotlinVersion: String by project
val ktorVersion: 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 {
        withJava()
        compilations {
            val main = getByName("main")
            tasks {
                register<Jar>("buildFatJar2") {
                    group = "application"
                    dependsOn(build)
                    manifest {
                        attributes["Main-Class"] = "com.app.BackendAppKt"
                    }
                    from(configurations.getByName("runtimeClasspath").map { if (it.isDirectory) it else zipTree(it) }, main.output.classesDirs)
                    archiveBaseName.set("${project.name}-fat2")
                }
            }
        }
    }
    js {
        browser {

        }
    }
    sourceSets { SKIPPED FOR LENGTH }
}

我希望这可以节省某人 3 天的时间,如果您发现改进,请告诉我(我也在学习)。 Kotlin、gradle、multiplatform、docker……都很难处理,他们需要并行更新文档恕我直言,否则 jetbrains 注定要失败。

可能的改进

  • 生成的 jar 看起来比它应有的大得多,包含大量不必要的东西,怀疑更改为编译路径而不是运行时路径可以解决这个问题(已确认大小减少 30%)。
  • 也许还有更多的清单属性。
  • 另外值得注意的是,我读过一篇文章恰当地建议不应将 fatJars 部署到 docker,应将 java 依赖项构建为映像的一部分。

【讨论】:

  • 这对我来说非常适用于 mpp/kotlin/1.4 ktor 和 js(我将 js 输出添加到您的任务中)。你让我今天一整天都感觉很好!实际上,三天 :D 谢谢老兄!
【解决方案2】:

@andylamax 的答案非常接近,但会导致 @cfnz 看到的错误

要解决这个问题,您需要添加 doFirst,如本例所示:

val jvm = jvm() {
    withJava()
    val jvmJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
        doFirst {
            manifest {
                attributes["Main-Class"] = project.ext["mainClass"]
            }
            from(configurations.getByName("runtimeClasspath").map { if (it.isDirectory) it else zipTree(it) })
        }
    }
}

它在heregradle jvmJar &amp;&amp; java -jar build/libs/laguna-jvm.jar 中按预期工作

【讨论】:

  • 这实际上对我的项目不起作用,所以我开始了一个新的多平台项目,我可以确认构建的 jar 确实有效:-)。所以这个解决方案至少有效,我现在可以开始构建我的应用程序,看看是什么导致了我的失败。谢谢。
【解决方案3】:

你的 groovy dsl 可以用 kotlin 编写如下

kotlin {
  jvm {
    withJava()
    val jvmJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
        manifest {
            attributes["Main-Class"] = "sample.MainKt"
        }
        from(configurations.getByName("runtimeClasspath").map { if (it.isDirectory) it else zipTree(it) })
    }
  }
}

【讨论】:

  • 要编译,我必须将代码放在jvm {} 块中,即kotlin { jvm { ... }}。所以它可以编译,你可以构建它,但它仍然不能生成一个胖 jar。知道该怎么做吗?
  • @cfnz 我通常会创建另一个 gradle 项目。使用插件 kotlin("jvm") 并使用多平台库作为依赖项
  • 仍在尝试使其正常工作。收到错误 A problem occurred configuring project, Failed to notify project evaluation listener. &gt; Cannot change dependencies of dependency configuration ':my-multiplatform-project:jvmApi' after it has been included in dependency resolution. :jvmRuntime 的相同错误。如果我取出 withJava() 我没有收到错误,但 jar 仍然没有所有依赖项。
  • 还注意到没有withJava() 我需要application 插件。尝试使用和不使用application 插件的解决方案均无济于事。
  • 我建议您使用我的解决方法。创建另一个 kotlin-jvm 项目,使用您的 mpp 项目作为它的依赖项。应用application 插件,应该可以。它对我有用
【解决方案4】:

这个使用 gradle multiplatform (mpp) 项目对我有用

plugins {
    kotlin("multiplatform") version Deps.JetBrains.Kotlin.VERSION
    application
    id("com.github.johnrengelman.shadow") version Deps.Plugins.Shadow.VERSION
}
application {
    mainClass.set(theMainClass)
}

tasks {
    val shadowCreate by creating(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) {
        manifest { attributes["Main-Class"] = theMainClass }
        archiveClassifier.set("fat")
        mergeServiceFiles()
        from(kotlin.jvm().compilations.getByName("main").output)
        configurations = mutableListOf(kotlin.jvm().compilations.getByName("main").compileDependencyFiles as Configuration)
    }
    val build by existing {
        dependsOn(shadowCreate)
    }
}
tasks.named<JavaExec>("run") {
    classpath += objects.fileCollection().from(
        tasks.named("compileKotlinJvm"),
        configurations.named("jvmRuntimeClasspath")
    )
}

【讨论】:

    猜你喜欢
    • 2019-12-01
    • 2019-08-29
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-03-06
    • 2019-08-12
    相关资源
    最近更新 更多