11

I'm using Gradle 5.5. I have a Groovy-based build script that I'm trying to migrate to the Kotlin DSL. The jar task contains the typical line for copying all dependencies to the JAR file:

from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }

I can't find a way to translate this line to the Kotlin DSL.

Let me give you some context. This is my original Groovy-based build script:

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.3.41"
}

group = "com.rhubarb_lip_sync"
version = "1.0.0"

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    compile "com.beust:klaxon:5.0.1"
    compile "org.apache.commons:commons-lang3:3.9"
    compile "no.tornado:tornadofx:1.7.19"
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

jar {
    manifest {
        attributes "Main-Class": "com.rhubarb_lip_sync.rhubarb_for_spine.MainKt"
    }

    // This line of code recursively collects and copies all of a project"s files
    // and adds them to the JAR itself. One can extend this task, to skip certain 
    // files or particular types at will
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}

And this is my Kotlin-based build script. It's working fine, except for that one line:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

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

group = "com.rhubarb_lip_sync"
version = "1.0.0"

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("com.beust:klaxon:5.0.1")
    implementation("org.apache.commons:commons-lang3:3.9")
    implementation("no.tornado:tornadofx:1.7.19")
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}

tasks.withType<Jar> {
    manifest {
        attributes("Main-Class" to "com.rhubarb_lip_sync.rhubarb_for_spine.MainKt")
    }

    // ?
}

Daniel Wolf
  • 12,855
  • 13
  • 54
  • 80
  • 1
    Does `from(configurations.compileClasspath.get().map { if (it.isDirectory) it else zipTree(it) })` work? – Slaw Jul 19 '19 at 20:28
  • @Slaw It does! Thank you very much! – Daniel Wolf Jul 19 '19 at 20:54
  • Does this answer your question? [How to create the fat jar with gradle kotlin script](https://stackoverflow.com/questions/41794914/how-to-create-the-fat-jar-with-gradle-kotlin-script) – Mahozad Feb 12 '22 at 13:02

2 Answers2

15

collect() in groovy is map() in Kotlin.

The ternary operator of groovy can be transformed into an if in Kotlin.

The main difference is that configurations.compile in Kotlin is not a Configuration but a Provider<Configuration>. So either you get the configuration out of the Provider, or you stay lazy by mapping the Provider to another Provider. So I think it should work

from(configurations.compileClasspath.get().map { if (it.isDirectory()) it else zipTree(it) })

or

from(configurations.compileClasspath.map { config -> config.map { if (it.isDirectory) it else zipTree(it) } })

Note that compile is deprecated for a long time now. Since use implementation now to declare your dependencies, there's nothing anymore in the compile configuration, and you must get the dependencies out of the compileClasspath one to build your uber jar.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    Thank you for your explanations! Both your snippets are syntactically correct, but neither seems to work. In both cases, I end up with a slim JAR without dependencies. – Daniel Wolf Jul 19 '19 at 20:49
  • I just realized that Slaw's comment above works, and the only difference to your code is that he used `configurations.compileClasspath` instead of `configurations.compile`. Apparently, the latter is not only deprecated, but also works differently. – Daniel Wolf Jul 19 '19 at 20:59
  • 1
    I didn't notice that you already switched to implementation to declare your dependencies. So they're not part of the deprecated compile configuration anymore, which is why you need to use compileClasspath. – JB Nizet Jul 19 '19 at 21:11
  • 1
    Now I get it. Thank you very much. Maybe you could move this information up to your answer in case other developers are in the same situation and don't realize that moving from `compile` to `implementation` breaks the JAR target. – Daniel Wolf Jul 20 '19 at 06:18
  • Thanks. Furthermore, if anyone gets a the error: _What went wrong: Execution failed for task ':jar'. > Entry META-INF/services/io.ktor.client.HttpClientEngineContainer is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.2/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details._ This can be resolved with: `duplicatesStrategy = DuplicatesStrategy.EXCLUDE` – razalghul Aug 21 '21 at 23:19
6

Here is a great sample by Maksim Kostromin

val mainClass = "com.myproject" // Replace this, your project main name

tasks {
  register("fatJar", Jar::class.java) {
    archiveClassifier.set("all")
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    manifest {
      attributes("Main-Class" to mainClass)
    }
    from(configurations.runtimeClasspath.get()
        .onEach { println("add from dependencies: ${it.name}") }
        .map { if (it.isDirectory) it else zipTree(it) })
    val sourcesMain = sourceSets.main.get()
    sourcesMain.allSource.forEach { println("add from sources: ${it.name}") }
    from(sourcesMain.output)
  }
}
Thiago
  • 12,778
  • 14
  • 93
  • 110