Puzzling behavior from the Kotlin Gradle plug-in


#1

I’ve been trying to understand how the Kotlin Gradle plug-in works and I’m a bit puzzled.

For example, I created a small project where a Kotlin file depends on a Java file:

// src/main/kotlin/example
package example

fun main(argv: Array<String>) {
	println("KotlinMain calling JavaClass().run()")
	JavaClass().run()
}
// src/main/java/example
package example;

public class JavaClass {
	public void run() {
	    System.out.println("JavaClass run()");
	}
}

Not surprisingly, if I try to compile the Kotlin file alone and manually, it fails:

$ kotlinc src/main/kotlin/example/KotlinMain.kt
src/main/kotlin/example/KotlinMain.kt:5:2: error: unresolved reference: JavaClass
	JavaClass().run()

However, if I launch the build with Gradle, something interesting happens:

$ ./gradlew clean
$ ./gradlew compileKotlin
$ find build/classes/main/example/KotlinMainKt.class
build/classes/main/META-INF/mixed-compileKotlin.kotlin_module
build/kotlin-classes/main/example/KotlinMainKt.class
build/kotlin-classes/main/META-INF/mixed-compileKotlin.kotlin_module

Somehow, Kotlin was able to compile KotlinMain without ever needing JavaClass!

I suspect there is some magic happening with the compilation that I don’t follow. Maybe the compiler can be told to compile despite missing symbols?

Can somebody explain how the Kotlin Gradle plug-in manages to do this? I’d like to replicate this with Kobalt…

Thanks!


#2

The Kotlin compiler is capable of parsing Java source code. In your first example, you specified only the path to the Kotlin file, so it wasn’t able to locate the dependency. In the second example, the Java source code is part of the project, so the Gradle plugin passes it to the compiler.


#3

That explains it.

How do I pass this source directory to the compiler?


#4

CompilerConfiguration.addJavaSourceRoot()


#5

Thanks.

I couldn’t find any args on the compiler to set this. I’m invoking K2VMCompiler directly, what’s the easiest way to add these directories to K2VMCompiler?


#6

Follow up question: apparently kotlinc merely parses the Java files (probably just to parse the symbols so it can then compile the Kotlin files) but it doesn’t actually compile them, so it needs to run javac later.

That seems inefficient, why not actually compile them once and for all instead of doing the work twice?


#7

Because those java files can contain references to kotlin classes, and javac can’t parse kotlin sources. Therefore kotlin sources have to be compiled before java ones.