Как создать толстую банку с помощью скрипта gradle kotlin

Как и хотелось бы узнать, как изменить 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") {

}

5 ответов

Вот версия, которая не использует плагин, больше похожая на версию 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)
    }
}

Также объяснил здесь

Обратите внимание, что первые 3 метода изменяют существующую задачу Gradle.

Способ 1: размещение файлов библиотеки рядом с 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 требует, чтобы мы использовали относительные URL-адреса для Class-Pathатрибут. Таким образом, мы не можем использовать абсолютный путь зависимостей Gradle (который также подвержен изменениям и недоступен в других системах). Если вы хотите использовать абсолютные пути, возможно, этот обходной путь сработает.

Создайте JAR с помощью следующей команды:

      ./gradlew jar

Результирующий JAR будет создан в каталоге build/libs/ по умолчанию.

После создания JAR-файла скопируйте JAR-файлы своей библиотеки в подкаталог libs/ того места, куда вы поместили полученный JAR-файл. Убедитесь, что JAR-файлы вашей библиотеки не содержат пробелов в имени файла (их имя файла должно совпадать с именем, указанным ${file.name}переменная выше в задаче).

Способ 2. Встраивание библиотек в итоговый JAR-файл (толстый или убер-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 (для создания толстого или убер 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"] = "org.example.MainKt"
}

Создайте JAR с помощью этой команды:

      ./gradlew shadowJar

См. документацию Shadow для получения дополнительной информации о настройке плагина.

Способ 4: Создание новой задачи (вместо изменения 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

Вышеупомянутые решения были протестированы с:

  • Ява 17
  • Gradle 7.1 (который использует Kotlin 1.4.31 для сценариев сборки .kts )

См. официальную документацию Gradle для создания uber (толстых) 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()
    
  • Ява
            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"));
    

Вот как это сделать в Gradle 6.5.1, Kotlin/Kotlin-Multiplatform 1.3.72, используя файл build.gradle.kts и без использования дополнительного плагина, который кажется ненужным и проблематичным для мультиплатформенности;

Примечание: на самом деле, насколько я могу судить, с многоплатформенным плагином хорошо работают несколько плагинов, поэтому я подозреваю, что его философия дизайна сама по себе очень многословна. На самом деле это довольно элегантно, IMHO, но недостаточно гибкое или документированное, поэтому для настройки даже БЕЗ дополнительных плагинов требуется масса проб и ошибок.

Надеюсь, это поможет другим.

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)
                }
            }
        }
    }
}

Вы можете использовать плагин ShadowJar для создания толстой банки:

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 (последний по состоянию на 13.02.2017).

Теперь есть документация о том, как это сделать в последних версиях gradle, см. Https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_packaging

Другие вопросы по тегам