Skip to content

Commit 1b414a9

Browse files
qwwdfsadALikhachev
andauthored
Properly support atomicfu in JPMS (#3656)
* Add module-info.java processing task to remove the `kotlinx-atomicfu` dependency * Remove module-info.java filter as it's actually no-op * Cleanup imports in Java9Modularity * Fix configuration cache issue in compileModuleInfoJava Co-authored-by: Alexander.Likhachev <[email protected]>
1 parent 46765ed commit 1b414a9

File tree

2 files changed

+44
-40
lines changed

2 files changed

+44
-40
lines changed

buildSrc/src/main/kotlin/Java9Modularity.kt

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
import org.gradle.api.*
66
import org.gradle.api.attributes.*
77
import org.gradle.api.file.*
8-
import org.gradle.api.provider.*
9-
import org.gradle.api.specs.*
8+
import org.gradle.api.tasks.*
109
import org.gradle.api.tasks.bundling.*
1110
import org.gradle.api.tasks.compile.*
1211
import org.gradle.jvm.toolchain.*
1312
import org.gradle.kotlin.dsl.*
14-
import org.gradle.api.logging.Logger
13+
import org.gradle.work.*
1514
import org.jetbrains.kotlin.gradle.dsl.*
16-
import java.io.*
1715

1816
/**
1917
* This object configures the Java compilation of a JPMS (aka Jigsaw) module descriptor.
@@ -27,30 +25,40 @@ import java.io.*
2725
*/
2826
object Java9Modularity {
2927

30-
private class ModuleInfoFilter(
31-
private val compileKotlinTaskPath: String,
32-
private val javaVersionProvider: Provider<JavaVersion>,
33-
private val moduleInfoFile: File,
34-
private val logger: Logger
35-
) : Spec<FileTreeElement> {
36-
private val isJava9Compatible
37-
get() = javaVersionProvider.orNull?.isJava9Compatible == true
38-
private var logged = false
39-
40-
private fun logStatusOnce() {
41-
if (logged) return
42-
if (isJava9Compatible) {
43-
logger.info("Module-info checking is enabled; $compileKotlinTaskPath is compiled using Java ${javaVersionProvider.get()}")
44-
} else {
45-
logger.info("Module-info checking is disabled")
28+
/**
29+
* Task that patches `module-info.java` and removes `requires kotlinx.atomicfu` directive.
30+
*
31+
* To have JPMS properly supported, Kotlin compiler **must** be supplied with the correct `module-info.java`.
32+
* The correct module info has to contain `atomicfu` requirement because atomicfu plugin kicks-in **after**
33+
* the compilation process. But `atomicfu` is compile-only dependency that shouldn't be present in the final
34+
* `module-info.java` and that's exactly what this task ensures.
35+
*/
36+
abstract class ProcessModuleInfoFile : DefaultTask() {
37+
@get:InputFile
38+
@get:NormalizeLineEndings
39+
abstract val moduleInfoFile: RegularFileProperty
40+
41+
@get:OutputFile
42+
abstract val processedModuleInfoFile: RegularFileProperty
43+
44+
private val projectPath = project.path
45+
46+
@TaskAction
47+
fun process() {
48+
val sourceFile = moduleInfoFile.get().asFile
49+
if (!sourceFile.exists()) {
50+
throw IllegalStateException("$sourceFile not found in $projectPath")
51+
}
52+
val outputFile = processedModuleInfoFile.get().asFile
53+
sourceFile.useLines { lines ->
54+
outputFile.outputStream().bufferedWriter().use { writer ->
55+
for (line in lines) {
56+
if ("kotlinx.atomicfu" in line) continue
57+
writer.write(line)
58+
writer.newLine()
59+
}
60+
}
4661
}
47-
logged = true
48-
}
49-
50-
override fun isSatisfiedBy(element: FileTreeElement): Boolean {
51-
logStatusOnce()
52-
if (isJava9Compatible) return false
53-
return element.file == moduleInfoFile
5462
}
5563
}
5664

@@ -74,12 +82,13 @@ object Java9Modularity {
7482
)
7583
}
7684

85+
val processModuleInfoFile by tasks.registering(ProcessModuleInfoFile::class) {
86+
moduleInfoFile.set(file("${target.name.ifEmpty { "." }}/src/module-info.java"))
87+
processedModuleInfoFile.set(project.layout.buildDirectory.file("generated-sources/module-info-processor/module-info.java"))
88+
}
89+
7790
val compileJavaModuleInfo = tasks.register("compileModuleInfoJava", JavaCompile::class.java) {
7891
val moduleName = project.name.replace('-', '.') // this module's name
79-
val sourceFile = file("${target.name.ifEmpty { "." }}/src/module-info.java")
80-
if (!sourceFile.exists()) {
81-
throw IllegalStateException("$sourceFile not found in $project")
82-
}
8392
val compileKotlinTask =
8493
compilation.compileTaskProvider.get() as? org.jetbrains.kotlin.gradle.tasks.KotlinCompile
8594
?: error("Cannot access Kotlin compile task ${compilation.compileKotlinTaskName}")
@@ -97,15 +106,9 @@ object Java9Modularity {
97106
// Note that we use the parent dir and an include filter,
98107
// this is needed for Gradle's module detection to work in
99108
// org.gradle.api.tasks.compile.JavaCompile.createSpec
100-
source(sourceFile.parentFile)
101-
include { it.file == sourceFile }
102-
103-
// The Kotlin compiler will parse and check module dependencies,
104-
// but it currently won't compile to a module-info.class file.
105-
// Note that module checking only works on JDK 9+,
106-
// because the JDK built-in base modules are not available in earlier versions.
107-
val javaVersionProvider = compileKotlinTask.kotlinJavaToolchain.javaVersion
108-
compileKotlinTask.exclude(ModuleInfoFilter(compileKotlinTask.path, javaVersionProvider, sourceFile, logger))
109+
source(processModuleInfoFile.map { it.processedModuleInfoFile.asFile.get().parentFile })
110+
val generatedModuleInfoFile = processModuleInfoFile.flatMap { it.processedModuleInfoFile.asFile }
111+
include { it.file == generatedModuleInfoFile.get() }
109112

110113
// Set the task outputs and destination directory
111114
outputs.dir(targetDir)

kotlinx-coroutines-test/jvm/src/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
module kotlinx.coroutines.test {
77
requires kotlin.stdlib;
88
requires kotlinx.coroutines.core;
9+
requires kotlinx.atomicfu;
910

1011
exports kotlinx.coroutines.test;
1112

0 commit comments

Comments
 (0)