Building a self-executable jar with Gradle and Kotlin

GradleKotlin

Gradle Problem Overview


I've written a simple kotlin source file in order to get started, and a gradle script file. But I can't figure out how to add the main func to the manifest, so that the jar could be self-executable.

Here my build.gradle script :

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.9.66'
    }
}
apply plugin: "kotlin"
repositories {
    mavenCentral()
}
dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib:0.9.66'
}

jar {
    manifest {
        attributes 'Main-Class': 'com.loloof64.kotlin.exps.ExpsPackage'
    }
}

Here is my com.loloof64.kotlin.exps.Multideclarations.kt

package com.loloof64.kotlin.exps

class Person(val firstName: String, val lastName: String) {
    fun component1(): String {
        return firstName
    }
    fun component2(): String {
        return lastName
    }
}

fun main(args: Array < String > ) {
    val(first, last) = Person("Laurent", "Bernabé")
    println("First name : $first - Last name : $last")
}

When I launch the jar from terminal (java -jar MYJar.jar) I get the following stacktrace, saying me that the kotlin reflection library classes are missing, and indeed they have not been added to the jar. It seems that I am missing the kotlin-compiler artifact classes from the final jar, and also the kotlin-stdlib sources, but I don't know how to adapt the gradle build.

$> java -jar build/libs/kotlin_exps.jar 

Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/reflect/jvm/internal/InternalPackage
    at com.loloof64.kotlin.exps.ExpsPackage.<clinit>(MultiDeclarations.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.reflect.jvm.internal.InternalPackage
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 1 more

I am using Kotlin 0.9.66 and gradle 2.1

Gradle Solutions


Solution 1 - Gradle

Add the plugin application, then set the mainClassName as

mainClassName = '[your_namespace].[your_arctifact]Kt'

For instance, suppose you have placed the following code in a file named main.kt:

package net.mydomain.kotlinlearn

import kotlin
import java.util.ArrayList

fun main(args: Array<String>) {

    println("Hello!")

}

your build.gradle should be:

apply plugin: 'kotlin'
apply plugin: 'application'

mainClassName = "net.mydomain.kotlinlearn.MainKt"

In fact Kotlin is building a class to encapsulate your main function named with the same name of your file - with Title Case.

Solution 2 - Gradle

I've found the workaround (thanks to MkYong website)

  1. The gradle script needed the kotlin-compiler artifact as a dependency
  2. The gradle script needed a way to collect all kotlin files and put them into the jar.

So i get with the following gradle script :

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
       classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.1-2'
    }
}

apply plugin: "kotlin"

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib:1.0.1-2'
}

jar {
    manifest {
        attributes 'Main-Class': 'com.loloof64.kotlin.exps.MultideclarationsKT'
    }

    // NEW LINE HERE !!!
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}

Solution 3 - Gradle

None of the above solution worked for me while building Artifact.

IDE version IntelliJ IDEA 2019.1.1.

To fix the issue, do the following

Steps

Step 1 - Create Artifact

  1. Go to File -> Project Structure -> Artifacts

  2. Click the + -> JAR -> From modules with dependencies

enter image description here

  1. Select your program's Main Class

enter image description here

Step 2 - Change MANIFEST Path

  1. Change value of Directory for META-INF/MANIFEST.MF to your project root.

    For example , from /your/project/directory/src/main/kotlin to /your/project/directory

enter image description here

  1. Press OK,then Press Apply and OK.

Step 3 - Build Artifact

  1. Finally, Go to Build -> Build Artifacts -> [your-artifact-name] -> Build.

The generated JAR file can be found in the out/artifact/[your-artifact-name] directory. (y)

Solution 4 - Gradle

Thanks, for me it worked adding the jar section

jar {
    manifest {
        attributes 'Main-Class': 'com.photofiles.Application'
    }

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

No need for the application plugin.

Solution 5 - Gradle

If using Gradle with the Kotlin DSL, then my duplicate question has an answer of:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins {
    kotlin("jvm") version "1.2.51"
    id("com.github.johnrengelman.shadow") version "2.0.4"
}

group = "xxx.yyy"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
}

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

tasks.withType<ShadowJar> {

    manifest.attributes.apply {
        put("Implementation-Title", "Gradle Jar File Example")
        //put("Implementation-Version" version)
        put("Main-Class", "HelloKotlinWorld.App")
    }

Which is, I think, the simplest solution. Oh, perhaps you're using just Kotlin itself and not the DSL.

Solution 6 - Gradle

In case you happen to be dumb, like me:

Don't create your IntelliJ run configuration as an Application. IntelliJ will assume it's Java & it will never work. Instead, use the "Kotlin" entry.

Solution 7 - Gradle

For anyone using the Kotlin MP plugin here is your code

jvm{
    jvmJar {
        manifest{
            attributes 'Main-Class':'Class path here'
        }
    }

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionloloof64View Question on Stackoverflow
Solution 1 - GradleGuildenstern70View Answer on Stackoverflow
Solution 2 - Gradleloloof64View Answer on Stackoverflow
Solution 3 - Gradletheapache64View Answer on Stackoverflow
Solution 4 - GradlepauclsView Answer on Stackoverflow
Solution 5 - GradleThufirView Answer on Stackoverflow
Solution 6 - GradleTim KeatingView Answer on Stackoverflow
Solution 7 - GradleAlaa SbateenView Answer on Stackoverflow