Do Kotlin 1.2.10 and Java 9 have opposite rules regarding automatic modules?
Asked Answered
S

2

11

I have a Gradle project using the Kotlin Gradle plugin. I want to build a Java 9 module, so my directory structure looks like this:

src/main/java/
    - module-info.java
src/main/kotlin/
    - Foo.kt
    - Bar.kt
build.gradle
...

My build.gradle declares the following dependencies:

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.10"
    compile "org.jetbrains.kotlin:kotlin-reflect:1.2.10"
    compile "org.junit.jupiter:junit-jupiter-api:5.0.2"
}

and I use all of these dependencies in my Kotlin source (Foo.kt, Bar.kt, ...).

Everything works hunky-dory if I write my module-info.java like so:

module my.module {
    requires kotlin.stdlib;
    exports my.module.pkg;
}

and if I supply all my compile-time dependencies to javac during the compileJava task using this technique.

However if I turn on -Xlint:all for the Java compiler during the compileJava task (to compile module-info.java), I get the following warnings:

/path/to/my.module/src/main/java/module-info.java:26: warning: requires directive for an automatic module
    requires kotlin.stdlib;
                   ^

So here we have the Java compiler, javac complaining that kotlin.stdlib is an automatic module so I shouldn't have a requires clause for it.

But if I delete the requires clause to make javac happy, it makes kotlinc even angrier than javac was (I get an error not a warning):

e: /path/to/my.module/src/main/java/module-info.java: The Kotlin standard library is not found in the module graph. Please ensure you have the 'requires kotlin.stdlib' clause in your module definition

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':my.module:compileKotlin'.

Now I can fix that, too, by editing my compileKotlin task thus:

compileKotlin {
    doFirst {
        kotlinOptions.freeCompilerArgs = ['-Xallow-kotlin-package']
    }
}

But this only leads to MORE errors during the compileKotlin task, all looking like this one:

e: /path/to/my.module/src/main/kotlin/Foo.kt: (27, 30): Symbol is declared in module 'org.junit.jupiter.api' which current module does not depend on

And then if I try to force compileKotlin to take a module path rather than a classpath by adding "-Xmodule-path=${classpath.asPath}" to freeCompilerArgs and setting classpath to be empty, the Kotlin compiler can't find anything at all, and I end up with zillions of unresolved reference errors!

Why is the Kotlin compiler telling me I have to have requires kotlin.stdlib; when the Java compiler says the opposite? How can I get Kotlin and Java to work together to produce a Java 9 module?

Sarracenia answered 24/12, 2017 at 6:49 Comment(5)
did you try adding a requires directive for org.junit.jupiter.api? maybe this is what causes the last error from the kotlin compilerLots
@msrd0, I just tried it (hadn't done so before in isolation). Adding org.junit.jupiter.api into the module-info.java as a requires does get rid of the unresolved symbol reference errors. HOWEVER, again, it generates an -Xlint:all warning from javac which doesn't want requires on automatic modules. So again it seems like Kotlin and Java are enforcing different rules here.Sarracenia
did you try it together with the -Xallow-kotlin-package flag?Lots
@Lots - yes: With and without. Neither works although both generate different errors. Essentially all -Xallow-kotlin-package allows me to do is omit requires kotlin.stdlib; from my module-info.java thus saving one javac warning.Sarracenia
Well, I added a comment to your previous question. Ideally, this could be solved in two ways, (1) if you could live with the warnings you could wait for the dependency you rely on, to move to JPMS(module system itself first) (2) you can go ahead and modify the jar being used to get rid of the warning using the MANIFEST entry.Encephalograph
E
3

If you're authoring Java 9 module in Kotlin, you have to declare requires kotlin.stdlib in your module-info.java in order to satisfy runtime dependencies of the compiled Kotlin code in addition to the explicit dependencies on the standard library API.

javac warns you about requiring an automatic module when lint is enabled, because automatic modules have some potential drawbacks compared to normal modules. Until the standard library is compiled as a normal module, you have to deal with this warning.

-Xallow-kotlin-package compiler flag allows you to omit require kotlin.stdlib, because it is intended to be used only when the standard library itself is compiled. Obviously, if you specify this flag and omit that requirement, you won't be able to use any API from the standard library, so this is not really an option for you.

Embolus answered 4/2, 2018 at 15:48 Comment(0)
S
2

I find it easiest to simply suppress the warnings for use of an automatic module. (In my case I have to use some automatic modules whether I want to or not, so these warnings are just distracting noise.) I have the following in my build.gradle.kts:

val compilerArgs = listOf(
    "-Xlint:all",                           // Enable all warnings except...
    "-Xlint:-requires-automatic",           // Suppress "requires directive for an automatic module" warnings from module-info.java
    "-Xlint:-requires-transitive-automatic" // Suppress "requires transitive directive for an automatic module" warnings from module-info.java
)

// This task will compile all Java code in the target module except for test code.
tasks.compileJava {
    doFirst {
        options.compilerArgs.addAll(compilerArgs)
    }
}

// This task will compile all Java test code in the target module.
tasks.compileTestJava {
    doFirst {
        options.compilerArgs.addAll(compilerArgs)
    }
}
Swearingen answered 31/12, 2018 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.