Introduction

So here a bit of details:

  • Gradle is an open source build automation system.
  • Kotlin is a general purpose, open source, statically typed programming language for both functional and object-oriented programming.
  • A DSL (domain-specific language) is a language specialized to a particular application domain. For example Groovy that was created for Gradle’s build scripts.

Kotlin dsl with gradle means to use the kotlin build.gradle.kts instead of the groovy build.gradle. Kotlin is so general that it reaches specific programming niche.

Everything is hosted on GitHub: sylhare/kotlin

Basic set up

Basic set up for your kotlin project with Gradle > 4.8 (2018), may not apply to latest gradle releases:

  • group: the top level package(s) under src.main
  • version: the version of your application
allprojects {
    group = "hello"
    version = "1.0"
    repositories {
        jcenter()
    }
}

You want to specify the kotlin version and plugin you wish to use:

plugins {
    kotlin("jvm") version "1.3.21"
    // instead of id("org.jetbrains.kotlin.jvm") version "1.3.21"
}

You don’t need to apply the plugin just define it, and you’re good to go. Add the basic repositories for your dependencies:

repositories {
    mavenCentral()
    maven(url = "https://plugins.gradle.org/m2/")
}    

Add dependencies like kotlin:

dependencies {
    compile(kotlin("stdlib-jdk8"))
    // Or compile("org.jetbrains.kotlin:kotlin-stdlib:1.3.21")
}

The kotlin key word replaces org.jetbrains.kotlin you can also use it for plugins this way.

For higher version of gradle, compile has been deprecated to implementation

You can also add this for source compatibility:

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

Code Coverage

JUnit

To look at coverage that means you have unit test. Don’t forget to add something like JUnit into your dependencies:

dependencies {
    testCompile("junit:junit:4.12")
}    

Also you want to use the junit platform to run your tests with google using:

tasks.test {
    useJUnitPlatform()
}

Which will use the junit-vintage-engine incompatible with the newer Junit5 version (you can recognize them by the jupiter in the package name), you may encounter the No tests found issue if you mix them up.

If you want to use org.junit.jupiter:junit-jupiter:5.7.2 with its junit-jupiter-api and junit-jupiter-engine, you might need to exclude junit4 modules like in springboot:

testImplementation("org.springframework.boot:spring-boot-starter-test") {
    exclude(module = "junit")
    exclude(module = "junit-vintage-engine")
}

With that the useJunitPlatform() will use the junit5 one.

Jacoco

Create code coverage task

Code coverage with jacoco plugin

plugins {
    jacoco
}

Then add the task

tasks.withType<JacocoReport> {
    reports {
        xml.isEnabled = true
        csv.isEnabled = false
        html.destination = file("${buildDir}/reports/jacoco")
    }
}

You can now roll the test code coverage with jacoco using gradle test jacocoTestReport.

Ignore a class from coverage

If you have a main class which can’t be tested and you’d rather remove it from coverage, you would do it like:

tasks.withType<JacocoReport> {
    doFirst {
        classDirectories = fileTree("build/classes/kotlin/main").apply {
            exclude("**/MainKt.class")
        }
    }
    
    // ... your other stuff
    
}

And MainKt is not considered for the coverage anymore!

Execute the project

With the Application gradle plugin

It is a plugin available with gradle, add it to your gradle script like:

plugins {
    application
}

Then set your application main file:

  • It has to be outside a class
  • The Kt at the end is normal, Kotlin automatically generates it for backward compatibility with Java classes
application {
    mainClassName = "hello.MainKt"
}

// Or you can use
application.mainClassName = "hello.MainKt"

Now you can run your program with:

gradle run

Make the fat Jar

So the basic Jar file generated doesnt include all you need to run. You need to include them manually by adding this (gradle 5+):

tasks.withType<Jar> {
    // Otherwise you'll get a "No main manifest attribute" error
    manifest {
        attributes["Main-Class"] = "com.example.MainKt"
    }

    // To add all of the dependencies otherwise a "NoClassDefFoundError" error
    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}

The jar will be created as {project.name}-{version}.jar like hello-kotlin-1.0.jar. You can then run it using:

java -jar hello-kotlin-1.0.jar

You can also create another task fatJar that would create the jar with all of your dependencies. Follow the documentation on gradle.

Make the wrapper

For your project to work almost anywhere, you can use the wrapper:

# To use the installed gradle version as wrapper
gradle wrapper 
# To specify the gradle version
gradle wrapper --gradle-version 4.8 --distribution-type all

Then you’ll be able to use ./gradlew instead of gradle, and your project should run fine 👍

Now if you have the opportunity to use a newer version of gradle, do it.