52
votes

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

7
Note that the classname generated by Kotlin for a top-level function such as main is the package defined in the same file, plus the filename with KT appended. In your case this would be com.loloof64.kotlin.exps.MultideclarationsKT. You can change this behavior by adding to the top of the file @file:JvmName("OtherName") to define a new classname for all top-level functions in a file. - Jayson Minard
You append the two characters Kt not KT... that is upper case K and lower case t. It is an easy error to make, but will cause the jar execution to fail. I had this problem myself on occasion due to imperfect typing. - J.E.Tkaczyk

7 Answers

37
votes

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.

27
votes

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

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)

1
votes

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.

1
votes

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.

1
votes

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.

0
votes

For anyone using the Kotlin MP plugin here is your code

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

}