Mixing Kotlin and Java in a JPMS Module Gradle Project

The goal is to write libraries that make use of the JPMS module system and implementation is with a mix of Kotlin and/or Java.

I am using Gradle 8.6 and JDK 21. The example project was created with “gradlew init” as a library.

The attached example project works (start “Runner.java”, which calls Kotlin code).
But JPMS seems to get in the way. As soon as module-info.java is added (rename module-info.XXXjava to module-info.java), the Java code cannot access the Kotlin code anymore.

> Task :lib:compileJava FAILED
C:\Users\taakabe4\IdeaProjects\kotlinjava\lib\src\main\java\org\example\Runner.java:6: error: cannot find symbol
        var lib = new Library();

Is there a (reasonable) way to solve this or is it simply not supported?
kotlinjava.zip (104.6 KB)

tasks.compileJava {
    options.compilerArgumentProviders.add(object : CommandLineArgumentProvider {
        @CompileClasspath
        val kotlinClasses = kotlin.sourceSets.main.flatMap { it.kotlin.classesDirectory }

        override fun asArguments() = listOf(
            "--patch-module",
            "kotlinjava.lib.main=${kotlinClasses.get().asFile.absolutePath}"
        )
    })
}
1 Like

Thanks a lot! Works perfectly for the provided example project.

Unfortunately with the “real world project” I get the error below.
While the real project is still Gradle 8.6, I am forced to use JDK 11 and old Kotlin 1.6.21.
The project structure is the same (generated with "gradlew init” as a library), but it has a buildSrc and uses various plugins, for example maven-publish and extra-java-module-info. The error might imply that it.kotlin.classesDirectory is empty, but I am not sure about this and cannot see a reason why. The o

Any suggestions in what direction to look for a solution?

* What went wrong:
A problem was found with the configuration of task ':lib:compileJava' (type 'JavaCompile').
  - Type 'org.gradle.api.tasks.compile.JavaCompile' property 'options.compilerArgumentProviders.$0.kotlinClasses' doesn't have a configured value.
    
    Reason: This property isn't marked as optional and no value has been configured.
    
    Possible solutions:
      1. The value of 'options.compilerArgumentProviders.$0.kotlinClasses' is calculated, make sure a valid value can be calculated.
      2. Mark property 'options.compilerArgumentProviders.$0.kotlinClasses' as optional.

Yeah, that’s a KGP bug:

tasks.compileJava {
    options.compilerArgumentProviders.add(object : CommandLineArgumentProvider {
        @CompileClasspath
        // work-around for Kotlin Gradle Plugin <1.8.20 not setting the classDirectory properly
        //val kotlinClasses = kotlin.sourceSets.main.flatMap { it.kotlin.classesDirectory }
        val kotlinClasses = tasks.compileKotlin.flatMap { it.destinationDirectory }

        override fun asArguments() = listOf(
            "--patch-module",
            "kotlinjava.lib.main=${kotlinClasses.get().asFile.absolutePath}"
        )
    })
}
1 Like

Thank you so much!!!

I was about to give up on my plans to establish a clean modular structure for our future library projects. A dull and mediocre life was starting to unfold in front of me. :weary:

But your second version also solved it for the legacy Kotlin version I am stuck with! :+1:

May God bless you and your knowledge that easily leaves GPT-4 in the dust. :smile:

1 Like