Building a Kotlin + Java 9 project with Gradle

2019-03-12 20:22发布

问题:

I'm fairly new to Gradle (and Java 9, to be honest), and I'm trying to use Gradle to build a simple library project that is a mix of Java 9 and Kotlin. More in detail, there is an interface in Java and an implementation in Kotlin; I'd do everything in Kotlin, but the modules-info.java is java anyway, so I decided to do things this way.

I'm building on IntelliJ Idea, with the 1.2.0 kotlin plugin and gradle 4.3.1 defined externally.

Filesystem schema is:

+ src
  + main
    + java
      + some.package
        - Roundabout.java [an interface]
      - module-info.java
    + kotlin
      + some.package.impl
        - RoundaboutImpl.kt [implementing the interface]

module-info.java is:

module some.package {
  requires kotlin.stdlib;
  exports some.package;
}

and build.gradle is:

buildscript {
    ext.kotlin_version = '1.2.0'

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

group 'some.package'
version '1.0-PRE_ALPHA'

apply plugin: 'java-library'
apply plugin: 'kotlin'

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

sourceCompatibility = 9

compileJava {
    dependsOn(':compileKotlin')
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
        ]
        classpath = files()
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: "$kotlin_version"
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

Notice that I had to specify a module path on the java compile task, or the compilation fails with:

error: module not found: kotlin.stdlib requires kotlin.stdlib;

Anyway, now this build fails with this error, and I can't figure out how to solve it:

error: package some.package.impl does not exist

import some.package.impl.RoundaboutImpl;

error: cannot find symbol

return new RoundaboutImpl<>(queueSize, parallelism, worker, threadPool);

I think that the Kotlin part of the compilation is going ok, then the java part fails because it doesn't "see" the kotlin side, so to speak.

I think I should tell it somehow to to load the already compiled kotlin classes in the classpath; but (first) how do I do this in gradle? and (second) is it even possible? I think you can't mix module path and class path in Java 9.

How can I solve this? I think it is a pretty common situation, as every java9-style module will be a mixed-language module (because of module-info.java), so I think I'm missing something really basic here.

Thanks in advance!

回答1:

Solved! It was sufficient to set the kotlin compilation dir to the same dir as Java:

compileKotlin.destinationDir = compileJava.destinationDir

It works now, both with the sources in the same tree or in different trees; but with a quirk: the jar task produces a jar with all the entries duplicated. I'll work on fix this, next.

Thanks to everyone!



回答2:

I am using the following gradle script where I put the module-info.java under src/module. It gets automatically included in the jar (without duplicates):

if (JavaVersion.current() >= JavaVersion.VERSION_1_9) {
    subprojects {
        def srcModule = "src/module"
        def moduleInfo = file("${project.projectDir}/$srcModule/module-info.java")
        if (moduleInfo.exists()) {

            sourceSets {
                module {
                    java {
                        srcDirs = [srcModule]
                        compileClasspath = main.compileClasspath
                        sourceCompatibility = '9'
                        targetCompatibility = '9'
                    }
                }
                main {
                    kotlin { srcDirs += [srcModule] }
                }
            }

            compileModuleJava.configure {
                dependsOn compileKotlin
                destinationDir = compileKotlin.destinationDir
                doFirst {
                    options.compilerArgs = ['--module-path', classpath.asPath,]
                    classpath = files()
                }
            }
            jar.dependsOn compileModuleJava
        }
    }
}

I won't update it any longer, have a look at https://github.com/robstoll/atrium/blob/master/build.gradle to see the current version in use.