I am currently working on a Gradle project that has both Java and Groovy sources. I am interested in some of the language features of Kotlin and am evaluating adding it in our code base. I have a sample project created that will compile and run all tests in IntelliJ however I can’t figure out how to make everything play nice with Gradle.
I’ve also played around with trying to call compile for each language. I believe what I need is something similar to how groovy compiles stubs but I can’t figure it out…
The only way to get joint compilation across JVM languages would be if every language supported an explicit stubbing mechanism. This is not the case, so joint compilation of Groovy and Kotlin is not possible
you can jointly compile Groovy and Java
you can jointly compile Kotlin and Java
you can serially compile any combination (but that means dependencies can only go in one direction, e.g. Java < Groovy < Kotlin)
If you want an honest opinion: Don’t create a tower of babel. Stick to one alternative general-purpose language per project.
I’m having the same challenge here. It’s only for a hobby project, for a real project I’d not take this approach, but right now I’m actually interested in doing a similar thing, and Java < Groovy < Kotlin is exactly what I want.
You mention you can do this by serially compiling your code, but can you explain how I could accomplish such a thing?
I don’t know how joint compilation works with Kotlin but hopefully I can explain the general process based on how groovy achieves the same goal. When doing joint compilation of groovy and Java sources that have dependencies both ways you would use the groovy compiler to first compile stubs of the groovy sources, then compile the Java sources and finally finish compiling the the full groovy sources. Introducing Kotlin into the mix would need to take the same approach. I imagine you would need to do the following:
Compile Groovy Stubs
Compile Kotlin Stubs
Compile Java
Compile Groovy
Compile Kotlin
As I stated originally, IntelliJ does this properly. From what I’ve seen in the Gradle documentation is that control over joint compilation isn’t possible, or at least not without more intimate knowledge of the plumbing where the documentation does delve into.
And I do kind of agree with Stefan regarding limiting the number of languages in a single project. On some days writing Java I wish I could exclusively use two alternative languages and skip out on Java completely though
H Matt, thanks for the reply! I’m not looking for cross compilation or anything like that, I just want to ‘force’ Gradle into compiling the Groovy code first, and then the Kotlin code, just as like having a jar dependency, but I’d prefer to not to go into the trouble of creating an extra module per language, but I don’t know how to change the build order. Right now, it compiles like this:
The problem here is simple: The Kotlin plugin authors decided that Kotlin should be compiled before Java. The Groovy plugin on the other hand makes the opposite assumption.
You can try overriding the Kotlin plugin’s behavior by doing:
taskDependencies.values contains a String for the task name
You need an explicit dependency for the classes task, so it still runs before tests etc, because compileKotlin doesn’t output to the standard classes dir, it uses a post hook to copy from the kotlin-classes directory
Likewise, for Kotlin to compile against Groovy, you need to make the files output by the compileGroovy task visible, without using the sourceset output, as that’ll cause a circular dependency for classes
It means that interop can only go one way, Kotlin -> Groovy, so you need to use reflection at the seams, or move your key wiring classes to Kotlin.
Incidentally, this is a handy extension for Groovy Closure interop:
fun <T> T.groovyClosure(function: () -> Unit) = object : Closure<Unit>(this) {
@Suppress("unused")
fun doCall() {
function()
}
}
Maybe interesting to know is that the clearest fail safe way to combine, in my view, is to have independent modules in a multi module project. Just put interfaces in java, have the modules implement them. Wire things up using dependency injection and let implementions be whatever jvm language.
So groovy and kotlin just use java interfaces and implement java interfaces. Both modules will do joint compilation wit java, no problems.