How to Make Gradle Kotlin Project Works Properly With Annotation Processor in IntelliJ

A week ago I was thinking of writing a small Kotlin command line tool to read some local json files. I opened IntelliJ and started a kotlin project with gradle. Everything worked just fine. I could compile and run the output jar file until I imported autovalue. Autovalue uses annotation processor to generate model objects, but IntelliJ just would not invoke annotation processing properly so the compilation failed. I did some research and finally made it to work, and I think these tricks are worth to share.

A side note:

Not long ago I wrote a post about why I am still using autovalue in Kotlin projects Why converting Autovalue class to Kotlin data class may not be “pragmatic” for JSON models at this moment.

Jetbrains is really good at listening to the community. They released two exeprimental features to address the two issues I mentioned in the post:

Start a new Kotlin project

Let’s start a new project with Kotlin and gradle supports:

Import the library that uses annotation processing, and enable Kotlin kapt plugin. I use auto value as the example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"

    compileOnly "com.google.auto.value:auto-value:1.5"
    kapt "com.google.auto.value:auto-value:1.5"
}

// make sure you add these configuration for jar task
jar {
    manifest {
        attributes 'Main-Class': 'AnnotationProcessorKt'
    }

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

jar task reference: Kotlin Basics: Create Executable Kotlin JARs, using Gradle

Lastly we need a simple main method and a autovalue class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import com.google.auto.value.AutoValue

@AutoValue
abstract class Person {

    abstract fun firstName(): String

    abstract fun lastName(): String
}

fun main(args: Array<String>) {

    // This won't work in IntelliJ out of box
    val p: Person = AutoValue_Person("Eric", "Lin")
    println("Hi, I am $p")
}

Of course like my comment above, the line val p: Person = AutoValue_Person("Eric", "Lin") just won’t work! But if you run the jar task via gradle in command line directly, you can get a working jar file:

jar in gradle

Delegate IDE build/run actions to gradle

The problem here is when you “build” the project, IntelliJ does NOT use the same settings you configure in build.gradle. You must to configure the annotation processor manually, which can be awkward to setup and I could not get it to work properly.

Instead there’s a much simpler trick you can do: Just tell IntellJ to use gradle for your build process!

In Build Tools -> Gradle -> Runner, you can tick the setting Delegate IDE build/run actions to gradle so that IntelliJ will use gradle for your project.

runner

Expose generated files to IntelliJ

Now you can verify the build/generated directory contains the generated files:

generated

However the file we need AutoValue_Person.java is still not recognized as the source file by the IDE.

There are two ways to mark the kapt directory as source directory:

1. Manually mark the directory

Right click on the kapt directory -> Mark Directory as -> Sources Root

mark as source manually

2. Use IDEA gradle plugin to mark sources

Add the following snippet to your build.gradle

1
2
3
4
5
6
idea {
    module {
        sourceDirs += files('build/generated/source/kapt/main', 'build/generated/source/kaptKotlin/main')
        generatedSourceDirs += files('build/generated/source/kapt/main', 'build/generated/source/kaptKotlin/main')
    }
}

By doing so the IDEA plugin will configure IntelliJ to treat those two directories as normal source directories.

reference: JetBrains/kotlin-examples

Run and Debug the jar file inside IntelliJ

Now you should be able to compile the project in IntelliJ without any compilation errors. However wouldn’t it be nice if we can run and debug the output jar file in the IDE as well?

Here’s the trick I use:

  1. go to run configuration
  2. create a Jar Application
  3. type in the path to the expected output jar file
  4. in Before Launch settings, add a gradle task jar

run configuration

Once the run configuration is set, you should be able to click on run or debug for your compiled jar file inside the IDE!

run the jar file

debug the jar file

Whoa! Long live Kotlin!

comments powered by Disqus
Content written by Eric Lin with passions in programming
Built with Hugo
Theme Stack designed by Jimmy