Skip to content

Commit 4393520

Browse files
committed
Add explicit module-info.java for JPMS compatibility
1 parent bfc6c0b commit 4393520

File tree

20 files changed

+280
-20
lines changed

20 files changed

+280
-20
lines changed

build.gradle

+16
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,22 @@ allprojects {
290290
CacheRedirector.configure(project)
291291
}
292292

293+
// --------------- Java 9 modularity ---------------
294+
295+
// Currently the compilation of the module-info fails for
296+
// kotlinx-coroutines-play-services because it depends on Android JAR's
297+
// which do not have an explicit module-info descriptor.
298+
// Because the JAR's are all named `classes.jar`,
299+
// the automatic module name also becomes `classes`.
300+
// This conflicts since there are multiple JAR's with identical names.
301+
def invalidModules = ["kotlinx-coroutines-play-services"]
302+
303+
configure(subprojects.findAll {
304+
!unpublished.contains(it.name) && !invalidModules.contains(it.name) && it.extensions.findByName("kotlin") != null
305+
}) {
306+
Java9Modularity.configure(project)
307+
}
308+
293309
// --------------- Configure sub-projects that are published ---------------
294310

295311
def publishTasks = getTasksByName("publish", true) + getTasksByName("publishNpm", true)
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
import org.gradle.api.*
6+
import org.gradle.api.attributes.*
7+
import org.gradle.api.tasks.bundling.*
8+
import org.gradle.api.tasks.compile.*
9+
import org.gradle.jvm.toolchain.*
10+
import org.gradle.kotlin.dsl.*
11+
import org.jetbrains.kotlin.gradle.dsl.*
12+
13+
object Java9Modularity {
14+
15+
@JvmStatic
16+
fun Project.configure() {
17+
val javaToolchains = extensions.getByName("javaToolchains") as JavaToolchainService
18+
val target = when (val kotlin = extensions.getByName("kotlin")) {
19+
is KotlinJvmProjectExtension -> kotlin.target
20+
is KotlinMultiplatformExtension -> kotlin.targets.getByName("jvm")
21+
else -> throw IllegalStateException("Unknown Kotlin project extension in $project")
22+
}
23+
val compilation = target.compilations.getByName("main")
24+
25+
// Force the use of JARs for compile dependencies, so any JPMS descriptors are picked up.
26+
// For more details, see https://github.com/gradle/gradle/issues/890#issuecomment-623392772
27+
configurations.getByName(compilation.compileDependencyConfigurationName).attributes {
28+
attribute(
29+
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
30+
objects.named(LibraryElements::class, LibraryElements.JAR)
31+
)
32+
}
33+
34+
val compileJavaModuleInfo = tasks.register("compileJavaModuleInfo", JavaCompile::class.java) {
35+
val moduleName = project.name.replace('-', '.') // this module's name
36+
val sourceFile = file("${target.name.ifEmpty { "." }}/src/module-info.java")
37+
if (!sourceFile.exists()) {
38+
throw IllegalStateException("$sourceFile not found in $project")
39+
}
40+
val compileKotlinTask = compilation.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
41+
val targetDir = compileKotlinTask.destinationDirectory.dir("../java9")
42+
43+
// Use a Java 11 compiler for the module-info.
44+
javaCompiler.set(javaToolchains.compilerFor {
45+
languageVersion.set(JavaLanguageVersion.of(11))
46+
})
47+
48+
// Always compile kotlin classes before the module descriptor.
49+
dependsOn(compileKotlinTask)
50+
51+
// Add the module-info source file.
52+
// Note that we use the parent dir and an include filter,
53+
// this is needed for Gradle's module detection to work in
54+
// org.gradle.api.tasks.compile.JavaCompile.createSpec
55+
source(sourceFile.parentFile)
56+
include { it.file == sourceFile }
57+
58+
// The Kotlin compiler will parse and check module dependencies,
59+
// but it currently won't compile to a module-info.class file.
60+
// Note that module checking only works on JDK 9+,
61+
// because the JDK built-in base modules are not available in earlier versions.
62+
val javaVersion = compileKotlinTask.kotlinJavaToolchain.javaVersion.getOrNull()
63+
if (javaVersion?.isJava9Compatible == true) {
64+
logger.info("Module-info checking is enabled; $compileKotlinTask is compiled using Java $javaVersion")
65+
} else {
66+
logger.info("Module-info checking is disabled")
67+
// Exclude the module-info.java source file from the Kotlin compile task,
68+
// to prevent compile errors when resolving the module graph.
69+
compileKotlinTask.exclude { it.file == sourceFile }
70+
}
71+
72+
// Set the task outputs and destination directory
73+
outputs.dir(targetDir)
74+
destinationDirectory.set(targetDir)
75+
76+
// Configure JVM compatibility
77+
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
78+
targetCompatibility = JavaVersion.VERSION_1_9.toString()
79+
80+
// Set the Java release version.
81+
options.release.set(9)
82+
83+
// Ignore warnings about using 'requires transitive' on automatic modules.
84+
// not needed when compiling with recent JDKs, e.g. 17
85+
options.compilerArgs.add("-Xlint:-requires-transitive-automatic")
86+
87+
// Patch the compileKotlinJvm output classes into the compilation so exporting packages works correctly.
88+
val kotlinCompileDestinationDir = compileKotlinTask.destinationDirectory.asFile.get()
89+
options.compilerArgs.addAll(listOf("--patch-module", "$moduleName=$kotlinCompileDestinationDir"))
90+
91+
// Use the classpath of the compileKotlinJvm task.
92+
// Also ensure that the module path is used instead of classpath.
93+
classpath = compileKotlinTask.libraries
94+
modularity.inferModulePath.set(true)
95+
}
96+
97+
tasks.getByName<Jar>(target.artifactsTaskName) {
98+
manifest {
99+
attributes("Multi-Release" to true)
100+
}
101+
from(compileJavaModuleInfo) {
102+
into("META-INF/versions/9/")
103+
}
104+
}
105+
}
106+
}

gradle/compile-jvm-multiplatform.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ kotlin {
1010
sourceSets {
1111
jvmMain.dependencies {
1212
compileOnly "org.codehaus.mojo:animal-sniffer-annotations:1.20"
13+
// Workaround until https://github.com/JetBrains/kotlin/pull/4999 is picked up
14+
api "org.jetbrains:annotations:23.0.0"
1315
}
1416

1517
jvmTest.dependencies {

integration-testing/src/debugAgentTest/kotlin/DebugProbes.kt

-14
This file was deleted.

integration-testing/src/debugAgentTest/kotlin/PrecompiledDebugProbesTest.kt

+16-5
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ class PrecompiledDebugProbesTest {
1616
@Test
1717
fun testClassFileContent() {
1818
val clz = Class.forName("kotlin.coroutines.jvm.internal.DebugProbesKt")
19-
val className: String = clz.getName()
20-
val classFileResourcePath = className.replace(".", "/") + ".class"
21-
val stream = clz.classLoader.getResourceAsStream(classFileResourcePath)!!
22-
val array = stream.readBytes()
19+
val classFileResourcePath = clz.name.replace(".", "/") + ".class"
20+
val array = clz.classLoader.getResourceAsStream(classFileResourcePath).use { it.readBytes() }
21+
assertJava6Compliance(array)
2322
// we expect the integration testing project to be in a subdirectory of the main kotlinx.coroutines project
2423
val base = File("").absoluteFile.parentFile
2524
val probes = File(base, "kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin")
@@ -31,8 +30,20 @@ class PrecompiledDebugProbesTest {
3130
assertTrue(
3231
array.contentEquals(binContent),
3332
"Compiled DebugProbesKt.class does not match the file shipped as a resource in kotlinx-coroutines-core. " +
34-
"Typically it happens because of the Kotlin version update (-> binary metadata). In that case, run the same test with -Poverwrite.probes=true."
33+
"Typically it happens because of the Kotlin version update (-> binary metadata). " +
34+
"In that case, run the same test with -Poverwrite.probes=true."
3535
)
3636
}
3737
}
38+
39+
private fun assertJava6Compliance(classBytes: ByteArray) {
40+
DataInputStream(classBytes.inputStream()).use {
41+
val magic: Int = it.readInt()
42+
if (magic != -0x35014542) throw IllegalArgumentException("Not a valid class!")
43+
val minor: Int = it.readUnsignedShort()
44+
val major: Int = it.readUnsignedShort()
45+
assertEquals(50, major)
46+
assertEquals(0, minor)
47+
}
48+
}
3849
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module kotlinx.coroutines.guava {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires com.google.common;
5+
requires failureaccess;
6+
7+
exports kotlinx.coroutines.guava;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@SuppressWarnings("JavaModuleNaming")
2+
module kotlinx.coroutines.jdk8 {
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module kotlinx.coroutines.slf4j {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires org.slf4j;
5+
6+
exports kotlinx.coroutines.slf4j;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module kotlinx.coroutines.core {
2+
requires transitive kotlin.stdlib;
3+
requires kotlinx.atomicfu;
4+
5+
// these are used by kotlinx.coroutines.debug.AgentPremain
6+
requires static java.instrument; // contains java.lang.instrument.*
7+
requires static jdk.unsupported; // contains sun.misc.Signal
8+
9+
exports kotlinx.coroutines;
10+
exports kotlinx.coroutines.channels;
11+
exports kotlinx.coroutines.debug;
12+
exports kotlinx.coroutines.debug.internal;
13+
exports kotlinx.coroutines.flow;
14+
exports kotlinx.coroutines.flow.internal;
15+
exports kotlinx.coroutines.future;
16+
exports kotlinx.coroutines.internal;
17+
exports kotlinx.coroutines.intrinsics;
18+
exports kotlinx.coroutines.scheduling;
19+
exports kotlinx.coroutines.selects;
20+
exports kotlinx.coroutines.stream;
21+
exports kotlinx.coroutines.sync;
22+
exports kotlinx.coroutines.time;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module kotlinx.coroutines.debug {
2+
requires java.management;
3+
requires java.instrument;
4+
requires kotlin.stdlib;
5+
requires kotlinx.coroutines.core;
6+
requires net.bytebuddy;
7+
requires net.bytebuddy.agent;
8+
requires org.junit.jupiter.api;
9+
requires org.junit.platform.commons;
10+
requires junit;
11+
requires reactor.blockhound;
12+
13+
// exports kotlinx.coroutines.debug; // already exported by kotlinx.coroutines.core
14+
exports kotlinx.coroutines.debug.junit4;
15+
exports kotlinx.coroutines.debug.junit5;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module kotlinx.coroutines.test {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
5+
exports kotlinx.coroutines.test;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@SuppressWarnings("JavaModuleNaming")
2+
module kotlinx.coroutines.jdk9 {
3+
requires kotlin.stdlib;
4+
requires kotlinx.coroutines.core;
5+
requires kotlinx.coroutines.reactive;
6+
requires org.reactivestreams;
7+
8+
exports kotlinx.coroutines.jdk9;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module kotlinx.coroutines.reactive {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires kotlinx.atomicfu;
5+
requires org.reactivestreams;
6+
7+
exports kotlinx.coroutines.reactive;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module kotlinx.coroutines.reactor {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires kotlinx.coroutines.reactive;
5+
requires org.reactivestreams;
6+
requires reactor.core;
7+
8+
exports kotlinx.coroutines.reactor;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@SuppressWarnings("JavaModuleNaming")
2+
module kotlinx.coroutines.rx2 {
3+
requires kotlin.stdlib;
4+
requires kotlinx.coroutines.core;
5+
requires kotlinx.coroutines.reactive;
6+
requires kotlinx.atomicfu;
7+
requires io.reactivex.rxjava2;
8+
9+
exports kotlinx.coroutines.rx2;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@SuppressWarnings("JavaModuleNaming")
2+
module kotlinx.coroutines.rx3 {
3+
requires kotlin.stdlib;
4+
requires kotlinx.coroutines.core;
5+
requires kotlinx.coroutines.reactive;
6+
requires kotlinx.atomicfu;
7+
requires io.reactivex.rxjava3;
8+
9+
exports kotlinx.coroutines.rx3;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module kotlinx.coroutines.android {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires android;
5+
requires annotation;
6+
7+
exports kotlinx.coroutines.android;
8+
}

ui/kotlinx-coroutines-javafx/build.gradle.kts

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5+
buildscript {
6+
dependencies {
7+
// this line can be removed when https://github.com/openjfx/javafx-gradle-plugin/pull/135 is released
8+
classpath("org.javamodularity:moduleplugin:1.8.12")
9+
}
10+
}
11+
512
plugins {
6-
id("org.openjfx.javafxplugin") version "0.0.9"
13+
id("org.openjfx.javafxplugin") version "0.0.13"
714
}
815

916
configurations {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module kotlinx.coroutines.javafx {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires javafx.base;
5+
requires javafx.graphics;
6+
7+
exports kotlinx.coroutines.javafx;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module kotlinx.coroutines.swing {
2+
requires kotlin.stdlib;
3+
requires kotlinx.coroutines.core;
4+
requires java.desktop;
5+
6+
exports kotlinx.coroutines.swing;
7+
}

0 commit comments

Comments
 (0)