首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >多平台: ShadowJar gradle插件创建空jar

多平台: ShadowJar gradle插件创建空jar
EN

Stack Overflow用户
提问于 2020-08-15 12:47:31
回答 3查看 1.1K关注 0票数 2

我尝试使用ShadowJar gradle插件将我的ktor应用程序打包到胖罐子中。但是作为shadowJar任务的结果,我每次都得到几乎为空的jar。它只包含清单(已正确设置主类)。

Gradle配置(groovy):

代码语言:javascript
复制
import org.gradle.jvm.tasks.Jar

buildscript {
    ext.kotlin_version = '1.3.72'
    ext.ktor_version = '1.3.2'
    ext.serialization_version = '0.20.0'
    ext.sl4j_version = '1.6.1'
    repositories { jcenter() }

    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    }
}

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
}

apply plugin: 'kotlinx-serialization'
apply plugin: 'application'
apply plugin: 'java'

mainClassName = 'com.example.JvmMainKt'

apply plugin: 'com.github.johnrengelman.shadow'

repositories {
    mavenCentral()
    jcenter()
}
group 'com.example'
version '0.0.1'

apply plugin: 'maven-publish'

kotlin {

    jvm {
    }
    js {
        browser {
        }
        nodejs {
        }
    }
    // For ARM, should be changed to iosArm32 or iosArm64
    // For Linux, should be changed to e.g. linuxX64
    // For MacOS, should be changed to e.g. macosX64
    // For Windows, should be changed to e.g. mingwX64
    mingwX64("mingw") {
        binaries {
            executable {
                // Change to specify fully qualified name of your application's entry point:
                entryPoint = 'main'
                // Specify command-line arguments, if necessary:
                runTask?.args('')
            }
        }
    }
    sourceSets {
        commonMain {
            dependencies {
                implementation kotlin('stdlib-common')
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
                implementation "io.ktor:ktor-client-core:$ktor_version"
            }
        }
        commonTest {
            dependencies {
                implementation kotlin('test-common')
                implementation kotlin('test-annotations-common')
            }
        }
        jvmMain {
            dependencies {
                implementation kotlin('stdlib-jdk8')
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
                implementation "io.ktor:ktor-serialization:$ktor_version"
                implementation "io.ktor:ktor-server-netty:$ktor_version"
                implementation "org.slf4j:slf4j-simple:$sl4j_version"

            }
        }
        jvmTest {
            dependencies {
                implementation kotlin('test')
                implementation kotlin('test-junit')
            }
        }
        jsMain {
            dependencies {
                implementation kotlin('stdlib-js')
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
            }
        }
        jsTest {
            dependencies {
                implementation kotlin('test-js')
            }
        }
        mingwMain {
            dependencies {
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
            }
        }
        mingwTest {
        }
    }
}

shadowJar {
    mainClassName = 'com.example.JvmMainKt'

    mergeServiceFiles()
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-08-16 21:01:22

我终于找到了解决问题的办法。

代码语言:javascript
复制
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

//...

task shadowJar2(type: ShadowJar) {
    def target = kotlin.targets.jvm
    from target.compilations.main.output
    def runtimeClasspath = target.compilations.main.runtimeDependencyFiles
    manifest {
        attributes 'Main-Class': mainClassName
    }
    configurations = [runtimeClasspath]
}
票数 0
EN

Stack Overflow用户

发布于 2022-06-06 14:18:38

marcu的回答是正确的,但他没有解释为什么这个片段解决了他的问题,而且他的代码片段不能直接转换为build.gradle.kts,因为需要一个强制转换才能从Kotlin中看到runtimeDependencyFiles。在groovy中,他的代码片段可以工作,因为groovy支持鸭型,而Kotlin不支持。

我在Gradle Kotlin需要这个解决方案,所以我就这样分享它。

com.github.johnrengelman.shadow gradle插件旨在与常规的java gradle插件一起工作,该插件默认构建单个jar,因此它会根据该jar类路径自动生成单个fat-jar

Kotlin-Multiplatform gradle插件的工作方式不同,它根据许多使用Kotlin-Multiplatform特有的方法设置的自定义设置为每个jvm目标创建jar文件,这就是为什么默认的shadowJar任务在Kotlin-Multiplatform上不能正常工作的原因。

为了解决这个问题,我们必须为每个需要手动创建一个ShadowJarjvm目标创建一个新的fat-jar任务,我们可以在声明目标之后(正如marcu的例子所示)或者在创建目标时这样做。我将展示这两种方式,但我建议在创建过程中进行,以避免不得不强制转换对象。

另一件事是我创建了函数来应用所需的配置,因为我有一个8个JVM目标,所以这样我就不需要复制粘贴剪贴8次,我只需要调用这个函数。

在目标创建过程中

这些解释对代码进行了注释:

代码语言:javascript
复制
// Add this imports on top of your build.gradle.kts file
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget

// Since you need a main class, I added this default constant
// which you can change as you need:
val defaultMainClassName: String? = null

// Here I declare the function that will register the new ShadowJar task for the target
// If you need another mainClassName, you can pass it as parameter
fun KotlinJvmTarget.registerShadowJar(mainClassName: String? = defaultMainClassName) {
    // We get the name from the target here to avoid conflicting 
    // with the name of the compilation unit
    val targetName = name
    // Access the main compilation
    // We only want to create ShadowJar 
    // for the main compilation of the target, not the test
    compilations.named("main") {
        // Access the tasks
        tasks {
            // Here we register our custom ShadowJar task, 
            // it's being prefixed by the target name
            val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
                // Allows our task to be grouped alongside with other build task
                // this is only for organization
                group = "build"
                // This is important, it adds all output of the build to the fat-jar
                from(output)
                // This tells ShadowJar to merge all jars in runtime environment
                // into the fat-jar for this target
                configurations = listOf(runtimeDependencyFiles)
                // Here we configure the name of the resulting fat-jar file
                // appendix makes sure we append the target name before the version
                archiveAppendix.set(targetName)
                // classifier is appended after the version, 
                // it's a common practice to set this classifier to fat-jars
                archiveClassifier.set("all")

                // Apply the main class name attribute
                if (mainClassName != null) {
                    manifest {
                        attributes("Main-Class" to mainClassName)
                    }
                }

                // This instruction tells the ShadowJar plugin to combine
                // ServiceLoader files together, this is needed because
                // a lot of Kotlin libs uses service files and
                // they would break without this instruction 
                mergeServiceFiles()
            }

            // Finally, we get the normal jar task for this target
            // and tells kotlin to execute our recently created ShadowJar task
            // after the normal jar task completes
            getByName("${targetName}Jar") {
                finalizedBy(shadowJar)
            }
        }
    }
}
kotlin {
    // Here we create a JVM target
    jvm("jvm8") {
        // We can configure anything we want
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
        // If we need a ShadowJar for it, 
        // all we have to do now is call
        // our custom function
        // Here's an example of what I'm saying:
        // https://stackoverflow.com/a/57951962/804976
        registerShadowJar()
    }
    // Another one just to illustrate the example
    jvm("jvm16") {
        compilations.all {
            kotlinOptions.jvmTarget = "16"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
        registerShadowJar()
    }
}

删除注释

代码语言:javascript
复制
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget

val defaultMainClassName: String? = null

fun KotlinJvmTarget.registerShadowJar(mainClassName: String? = defaultMainClassName) {
    val targetName = name
    compilations.named("main") {
        tasks {
            val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
                group = "build"
                from(output)
                configurations = listOf(runtimeDependencyFiles)
                archiveAppendix.set(targetName)
                archiveClassifier.set("all")
                if (mainClassName != null) {
                    manifest {
                        attributes("Main-Class" to mainClassName)
                    }
                }
                mergeServiceFiles()
            }
            getByName("${targetName}Jar") {
                finalizedBy(shadowJar)
            }
        }
    }
}
kotlin {
    jvm("jvm8") {
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
        registerShadowJar()
    }
    jvm("jvm16") {
        compilations.all {
            kotlinOptions.jvmTarget = "16"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
        registerShadowJar()
    }
}

在目标创建之后

现在,我将只评论这些更改:

代码语言:javascript
复制
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget

kotlin {
    jvm("jvm8") {
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
        // Not registering on creation this time,
        // we are going to register the task later
    }
    jvm("jvm16") {
        compilations.all {
            kotlinOptions.jvmTarget = "16"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
    }
}


val defaultMainClassName: String? = null

// Instead of having KotlinJvmTarget as receiver,
// we are going to cast the target to it.
// We are also getting the target name from
// the function parameter
fun registerShadowJar(targetName: String, mainClassName: String? = defaultMainClassName) {
    // Get the target casting to KotlinJvmTarget in the process
    kotlin.targets.named<KotlinJvmTarget>(targetName) {
        // Access the main compilation
        compilations.named("main") {
            tasks {
                val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
                    group = "build"
                    from(output)
                    configurations = listOf(runtimeDependencyFiles)
                    archiveAppendix.set(targetName)
                    archiveClassifier.set("all")
                    if (mainClassName != null) {
                        manifest {
                        attributes("Main-Class" to mainClassName)
                        }
                    }
                    mergeServiceFiles()
                }
                getByName("${targetName}Jar") {
                    finalizedBy(shadowJar)
                }
            }
        }
    }
}

// Now we call the method for each JVM target that we need to create a ShadowJar
registerShadowJar("jvm8")
registerShadowJar("jvm16")

删除注释

代码语言:javascript
复制
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget

kotlin {
    jvm("jvm8") {
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
    }
    jvm("jvm16") {
        compilations.all {
            kotlinOptions.jvmTarget = "16"
        }
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
    }
}

val defaultMainClassName: String? = null

fun registerShadowJar(targetName: String, mainClassName: String? = defaultMainClassName) {
    kotlin.targets.named<KotlinJvmTarget>(targetName) {
        compilations.named("main") {
            tasks {
                val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
                    group = "build"
                    from(output)
                    configurations = listOf(runtimeDependencyFiles)
                    archiveAppendix.set(targetName)
                    archiveClassifier.set("all")
                    if (mainClassName != null) {
                        manifest {
                        attributes("Main-Class" to mainClassName)
                        }
                    }
                    mergeServiceFiles()
                }
                getByName("${targetName}Jar") {
                    finalizedBy(shadowJar)
                }
            }
        }
    }
}
registerShadowJar("jvm8")
registerShadowJar("jvm16")
票数 2
EN

Stack Overflow用户

发布于 2020-08-15 15:21:28

在过去,我也遇到过同样的问题,对我来说起作用的语法( groovy 的)是:

代码语言:javascript
复制
shadowJar {
    mergeServiceFiles()
    manifest {
        attributes 'Main-Class': 'com.example.JvmMainKt'
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63426211

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档