-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add explicit module-info.java for JPMS compatibility #3297
Conversation
Note that there is still a highlighting issue with IntelliJ IDEA when using Java modules in a multi-platform Kotlin project. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking care of it!
This is a change we'll be able to ship in the next major release as it carries compatibility risks and may require and RC first, so I will properly investigate the PR around kotlinx.coroutines
1.7.0
Unfortunately, IDEA issues won't be fixed in a foreseeable future, so could you please conditionally enable modules if and only if Idea.isActive
is false? In kotlinx.serialization
we had plenty of IDEA issues due to that in JVM-nly modules
build.gradle
Outdated
// kotlinx.coroutines.debug and kotlinx.coroutines.test | ||
// because they export packages that also exist in kotlinx.coroutines.core. | ||
// Note that only 1 module can export a package. | ||
def invalidModules = ["kotlinx-coroutines-debug", "kotlinx-coroutines-test"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kotlinx-coroutines-test
has been fixed in 1.6.2 and no longer contains split package
Unfortunately I cannot use For now I just moved the |
AFAIU, module declarations must also declare all the services with |
A short update: the corresponding support of JPMS should land in IDEA 2022.3, so we are ready to proceed with the change. |
I just rebased and can confirm that the highlighting issues described in https://youtrack.jetbrains.com/issue/KTIJ-19614 are resolved in IDEA 2022.3 (EAP) |
exports kotlinx.coroutines.debug; | ||
exports kotlinx.coroutines.debug.internal; | ||
exports kotlinx.coroutines.flow; | ||
exports kotlinx.coroutines.flow.internal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please elaborate on how exactly you got these packages?
It seems like some of them should not be exported in the first place -- all internal
(though I have to double check some of them), scheduling
and intrinsics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These packages are mostly used in reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt
. So I need to export them from core so I don't get a compile error in kotlinx-coroutines-reactive
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered exporting the internal packages just to the kotlinx.coroutines modules that depend on them? For example:
exports kotlinx.coroutines.flow.internal; | |
exports kotlinx.coroutines.flow.internal to kotlinx.coroutines.reactive; |
requires kotlin.stdlib; | ||
requires kotlinx.coroutines.core; | ||
requires net.bytebuddy; | ||
requires net.bytebuddy.agent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it going to work with our ByteBuddy shadowing?
https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-debug/build.gradle#L40
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, probably not. I will check this out further.
Also, it seems like some modules cannot be compiled: https://teamcity.jetbrains.com/viewLog.html?buildId=3984907&tab=buildResultsDiv&buildTypeId=KotlinTools_KotlinxCoroutines_Build |
I also added the necessary |
@lion7 Is it ready for review or are you still working on it? (I'm not sure if "re-request review" button is available for external contributors, thus the question) |
Hey @lion7 -- is there any holdup on this? Having proper JPMS support would really be fantastic. |
@qwwdfsad I think this is ready enough for review, I'm not working on it anymore and just rebased it on the latest develop. The only thing that still bothers me is that there are currently no tests executed with a module path, especially after re-reading the comment from @elizarov. |
Thanks! Will take care of it during this week |
I've started the internal testing (mostly our internal projects, Ktor and some Android apps) process and, as soon as all perturbations are resolved, will publish the dev build for everybody interested in trying it out. For now, it seems like JavaFx ahs some non-trivial setup issues: none of the tests is able to start and fails with |
|
||
@JvmStatic | ||
fun configure(project: Project) = with(project) { | ||
val javaToolchains = extensions.getByName("javaToolchains") as JavaToolchainService |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change it to
val javaToolchains = extensions.getByName("javaToolchains") as JavaToolchainService | |
val javaToolchains = extensions.findByType(JavaToolchainService::class.java) | |
?: error("Gradle JavaToolchainService is not available") |
val invalidModules = listOf("kotlinx-coroutines-play-services") | ||
|
||
configure(subprojects.filter { | ||
!unpublished.contains(it.name) && !invalidModules.contains(it.name) && it.extensions.findByName("kotlin") != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll be more correct to run the configuration once some Kotlin plugin is applied, like
plugins.withType(org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin::class) {
// perform some configuration
}
instead of checking for the extension presence right now. The order of plugin appliance should not matter.
|
||
// Patch the compileKotlinJvm output classes into the compilation so exporting packages works correctly. | ||
val kotlinCompileDestinationDir = compileKotlinTask.destinationDirectory.asFile.get() | ||
options.compilerArgs.addAll(listOf("--patch-module", "$moduleName=$kotlinCompileDestinationDir")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please try to preserve laziness here, e.g. like
val destinationDirProperty = compileKotlinTask.destinationDirectory.asFile
options.compilerArgumentProviders.add({
val kotlinCompileDestinationDir = destinationDirProperty.get()
listOf("--patch-module", "$moduleName=$kotlinCompileDestinationDir")
})
if (!sourceFile.exists()) { | ||
throw IllegalStateException("$sourceFile not found in $project") | ||
} | ||
val compileKotlinTask = compilation.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compilation.compileKotlinTask
is deprecated.
You could use
val compileKotlinTask = compilation.compileTaskProvider.get()
as? org.jetbrains.kotlin.gradle.tasks.KotlinCompile
?: error("Cannot access Kotlin compile task ${compilation.compileKotlinTaskName}")
// but it currently won't compile to a module-info.class file. | ||
// Note that module checking only works on JDK 9+, | ||
// because the JDK built-in base modules are not available in earlier versions. | ||
val javaVersion = compileKotlinTask.kotlinJavaToolchain.javaVersion.orNull |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to keep it lazy as well.
You could do it by defining such class
private class ModuleInfoFilter(
private val compileKotlinTaskPath: String,
private val javaVersionProvider: Provider<JavaVersion>,
private val moduleInfoFile: File,
private val logger: Logger
) : Spec<FileTreeElement> {
private val isJava9Compatible
get() = javaVersionProvider.orNull?.isJava9Compatible == true
private var logged = false
private fun logStatusOnce() {
if (logged) return
if (isJava9Compatible) {
logger.info("Module-info checking is enabled; $compileKotlinTaskPath is compiled using Java ${javaVersionProvider.get()}")
} else {
logger.info("Module-info checking is disabled")
}
logged = true
}
override fun isSatisfiedBy(element: FileTreeElement): Boolean {
logStatusOnce()
if (isJava9Compatible) return false
return element.file == moduleInfoFile
}
}
and then use it like
val javaVersionProvider = compileKotlinTask.kotlinJavaToolchain.javaVersion
compileKotlinTask.exclude(ModuleInfoFilter(compileKotlinTask.path, javaVersionProvider, sourceFile, logger))
Yes, it's more verbose, but performs the check lazily. Otherwise it depends on the order, so if the task configured before toolchains, then it may get an incorrect Java version.
modularity.inferModulePath.set(true) | ||
} | ||
|
||
tasks.getByName<Jar>(target.artifactsTaskName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've applied all the proposed suggestions in #3629, will keep you posted regarding further actions |
I've run a few out integration tests, so far so good. Our JPMS-related testing capabilities are limited (because we are not really using JPMS ourselves anywhere), so I've published |
Merged manually. Thanks! |
No description provided.