如何使用 Gradle Kotlin 脚本创建 fat JAR?

问题描述 投票:0回答:5

如标题所示,我想知道如何修改

gradle.build.kts
,以便有一个任务来创建一个独特的
jar
,其中包含所有依赖项(包括 kotlin lib)。

我在 Groovy 中找到了这个示例:

//create a single Jar with all dependencies
task fatJar(type: Jar) {
    manifest {
        attributes 'Implementation-Title': 'Gradle Jar File Example',
            'Implementation-Version': version,
            'Main-Class': 'com.mkyong.DateUtils'
    }
    baseName = project.name + '-all'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

但我不知道如何用 kotlin 编写它,除了:

task("fatJar") {

}
kotlin gradle build.gradle executable-jar gradle-kotlin-dsl
5个回答
62
投票

这是一个不使用插件的版本,更像是Groovy版本。

import org.gradle.jvm.tasks.Jar

val fatJar = task("fatJar", type = Jar::class) {
    baseName = "${project.name}-fat"
    manifest {
        attributes["Implementation-Title"] = "Gradle Jar File Example"
        attributes["Implementation-Version"] = version
        attributes["Main-Class"] = "com.mkyong.DateUtils"
    }
    from(configurations.runtime.map({ if (it.isDirectory) it else zipTree(it) }))
    with(tasks["jar"] as CopySpec)
}

tasks {
    "build" {
        dependsOn(fatJar)
    }
}

这里也有解释


一些评论者指出,这对于较新的 Gradle 版本不再适用。 使用 Gradle 5.4.1 测试更新:

import org.gradle.jvm.tasks.Jar

val fatJar = task("fatJar", type = Jar::class) {
    baseName = "${project.name}-fat"
    manifest {
        attributes["Implementation-Title"] = "Gradle Jar File Example"
        attributes["Implementation-Version"] = version
        attributes["Main-Class"] = "com.mkyong.DateUtils"
    }
    from(configurations.runtimeClasspath.get().map({ if (it.isDirectory) it else zipTree(it) }))
    with(tasks.jar.get() as CopySpec)
}

tasks {
    "build" {
        dependsOn(fatJar)
    }
}

注意

configurations.runtimeClasspath.get()
with(tasks.jar.get() as CopySpec)
的区别。


30
投票

这里有 4 种方法可以做到这一点。请注意,前 3 个方法修改了 Gradle 现有的

Jar
任务。

方法一:将库文件放在结果JAR旁边

此方法不需要

application
或任何其他插件。

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    manifest.attributes["Class-Path"] = configurations
        .runtimeClasspath
        .get()
        .joinToString(separator = " ") { file ->
            "libs/${file.name}"
        }
}

请注意,Java 要求我们对

Class-Path
属性使用相对 URL。所以,我们不能使用Gradle依赖的绝对路径(这也容易被改变并且在其他系统上不可用)。如果您想使用绝对路径,也许这个解决方法会起作用。

使用以下命令创建 JAR:

./gradlew jar

默认情况下,结果 JAR 将在 build/libs/ 目录中创建。

创建 JAR 后,将库 JAR 复制到放置结果 JAR 的 libs/ 子目录中。确保您的库 JAR 文件的文件名中不包含空格(其文件名应与任务中上面的

${file.name}
变量指定的文件名匹配)。

方法 2:将库嵌入到结果 JAR 文件中(fat 或 uber JAR)

这个方法也不需要任何 Gradle 插件。

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree) // OR .map { zipTree(it) }
    from(dependencies)
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

创建JAR与之前的方法完全相同。

方法 3:使用 Shadow 插件(创建 fat 或 uber JAR)

plugins {
    id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these configs are reflected for Shadow as well
tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
}

使用以下命令创建 JAR:

./gradlew shadowJar

有关配置插件的更多信息,请参阅Shadow 文档

方法四:创建新任务(而不是修改
Jar
任务)

tasks.create("MyFatJar", Jar::class) {
    group = "my tasks" // OR, for example, "build"
    description = "Creates a self-contained fat JAR of the application that can be run."
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree)
    from(dependencies)
    with(tasks.jar.get())
}

运行创建的JAR

java -jar my-artifact.jar

上述解决方案经过测试:

  • Java 17
  • Gradle 7.1(使用 Kotlin 1.4.31 进行 .kts 构建脚本)

请参阅官方 Gradle 文档以创建 uber(fat)JAR
有关清单的更多信息,请参阅 Oracle Java 文档:使用清单文件
有关

tasks.create()
tasks.register()
之间的区别,请参阅 这篇文章

请注意,您的资源文件将自动包含在 JAR 文件中(假设它们放置在 /src/main/resources/ 目录或在构建文件中设置为资源根目录的任何自定义目录中)。要访问应用程序中的资源文件,请使用以下代码(请注意名称开头的

/
):

  • 科特林
    val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
    // Alternative ways:
    // val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
    // val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
    // val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
    
  • Java
    var stream = MyClass.class.getResource("/vegetables.txt").openStream();
    // OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
    
    var reader = new BufferedReader(new InputStreamReader(stream));
    var vegetables = reader.lines().collect(Collectors.joining("\n"));
    

9
投票

以下是如何从 Gradle 6.5.1、Kotlin/Kotlin-Multiplatform 1.3.72 开始,利用 build.gradle.kts 文件,而不使用额外的插件,这对于多平台来说似乎是不必要的和有问题的;

注意:实际上,据我所知,很少有插件能够与多平台插件很好地配合,这就是为什么我怀疑它的设计理念本身是如此冗长。恕我直言,它实际上相当优雅,但不够灵活或记录不够,因此即使没有额外的插件,也需要大量的试验和错误来设置。

希望这对其他人有帮助。

kotlin {
    jvm {
        compilations {
            val main = getByName("main")
            tasks {
                register<Jar>("fatJar") {
                    group = "application"
                    manifest {
                        attributes["Implementation-Title"] = "Gradle Jar File Example"
                        attributes["Implementation-Version"] = archiveVersion
                        attributes["Main-Class"] = "[[mainClassPath]]"
                    }
                    archiveBaseName.set("${project.name}-fat")
                    from(main.output.classesDirs, main.compileDependencyFiles)
                    with(jar.get() as CopySpec)
                }
            }
        }
    }
}

8
投票

您可以使用 ShadowJar 插件 构建一个 fat jar:

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

buildscript {
    repositories {
        mavenCentral()
        gradleScriptKotlin()
    }
    dependencies {
        classpath(kotlinModule("gradle-plugin"))
        classpath("com.github.jengelman.gradle.plugins:shadow:1.2.3")
    }
}

apply {
    plugin("kotlin")
    plugin("com.github.johnrengelman.shadow")
}

repositories {
    mavenCentral()
}

val shadowJar: ShadowJar by tasks
shadowJar.apply {
    manifest.attributes.apply {
        put("Implementation-Title", "Gradle Jar File Example")
        put("Implementation-Version" version)
        put("Main-Class", "com.mkyong.DateUtils")
    }

    baseName = project.name + "-all"
}

只需使用“shadowJar”运行任务即可。

注意:假设您使用的是 GSK 0.7.0(最新截至 2017 年 2 月 13 日)。


0
投票

哇,上面对这个已有 7 年历史的问题的回答和评论拯救了我的理智……我对以上所有内容都投了赞成票,他们帮助我实现了下面的 build.gradle.kts 的基本框架 - 感谢大家的贡献!

这是使用他们的 Kotlin SDK 构建的 AWS Lambda,由他们的 Java 21 运行时在 arm64 架构上运行。要上传到所述 AWS Lambda 的 .jar 是使用 Gradle 8.5 从命令行构建的:

>gradle awsJar

plugins {
    kotlin("jvm") version "1.9.20"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("aws.sdk.kotlin:aws-core-jvm:0.35.0-beta")
    implementation("aws.sdk.kotlin:s3:0.35.0-beta")

    implementation("com.amazonaws:aws-lambda-java-core:1.2.3")
    implementation("com.amazonaws:aws-lambda-java-events:3.11.3")

    runtimeOnly("com.amazonaws:aws-lambda-java-log4j2:1.5.1")
}

kotlin {
    jvmToolchain(21)
}

task("awsJar", type = Jar::class) {
    manifest.attributes["Main-Class"] = "com.example.Handler.handleRequest"
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    from(configurations.runtimeClasspath.get().map { if (it.isDirectory()) it else zipTree(it) })
    with(tasks.jar.get() as CopySpec)
}

© www.soinside.com 2019 - 2024. All rights reserved.