Skip to content

Commit cf6e41a

Browse files
authored
Add explicit module-info's for JPMS compatability (#1624)
1 parent c0c60a6 commit cf6e41a

File tree

16 files changed

+173
-2
lines changed

16 files changed

+173
-2
lines changed

buildSrc/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ repositories {
99
mavenCentral()
1010
}
1111

12+
dependencies {
13+
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30-RC")
14+
}
15+
1216
kotlinDslPluginOptions {
1317
experimentalWarning.set(false)
1418
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import org.gradle.api.*
2+
import org.gradle.api.file.*
3+
import org.gradle.api.tasks.bundling.*
4+
import org.gradle.api.tasks.compile.*
5+
import org.gradle.kotlin.dsl.*
6+
import org.gradle.util.GUtil.*
7+
import org.jetbrains.kotlin.gradle.dsl.*
8+
import org.jetbrains.kotlin.gradle.plugin.*
9+
import org.jetbrains.kotlin.gradle.plugin.mpp.*
10+
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
11+
import org.jetbrains.kotlin.gradle.targets.jvm.*
12+
import java.io.*
13+
import java.lang.module.*
14+
import java.util.spi.*
15+
16+
object Java9Modularity {
17+
18+
@JvmStatic
19+
@JvmOverloads
20+
fun Project.configureJava9ModuleInfo(multiRelease: Boolean = true) {
21+
val kotlin = extensions.findByType<KotlinProjectExtension>() ?: return
22+
val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*> }
23+
if (jvmTargets.isEmpty()) {
24+
logger.warn("No Kotlin JVM targets found, can't configure compilation of module-info!")
25+
}
26+
jvmTargets.forEach { target ->
27+
target.compilations.forEach { compilation ->
28+
val defaultSourceSet = compilation.defaultSourceSet.kotlin
29+
val moduleInfoSourceFile = defaultSourceSet.find { it.name == "module-info.java" }
30+
31+
if (moduleInfoSourceFile == null) {
32+
logger.info("No module-info.java file found in ${defaultSourceSet.srcDirs}, can't configure compilation of module-info!")
33+
} else {
34+
val targetName = toCamelCase(target.targetName)
35+
val compilationName = if (compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME) toCamelCase(compilation.name) else ""
36+
val compileModuleInfoTaskName = "compile${compilationName}ModuleInfo$targetName"
37+
val checkModuleInfoTaskName = "check${compilationName}ModuleInfo$targetName"
38+
39+
val compileKotlinTask = compilation.compileKotlinTask as AbstractCompile
40+
val modulePath = compileKotlinTask.classpath
41+
val moduleInfoClassFile = compileKotlinTask.destinationDirectory.file("module-info.class").get().asFile
42+
43+
val compileModuleInfoTask = registerCompileModuleInfoTask(compileModuleInfoTaskName, modulePath, compileKotlinTask.destinationDirectory, moduleInfoSourceFile)
44+
tasks.getByName(compilation.compileAllTaskName).dependsOn(compileModuleInfoTask)
45+
46+
val checkModuleInfoTask = registerCheckModuleInfoTask(checkModuleInfoTaskName, modulePath, moduleInfoClassFile)
47+
checkModuleInfoTask.configure { dependsOn(compilation.compileAllTaskName) }
48+
tasks.getByName("check").dependsOn(checkModuleInfoTask)
49+
}
50+
}
51+
52+
if (multiRelease) {
53+
tasks.getByName<Jar>(target.artifactsTaskName) {
54+
rename("module-info.class", "META-INF/versions/9/module-info.class")
55+
manifest {
56+
attributes("Multi-Release" to true)
57+
}
58+
}
59+
}
60+
}
61+
}
62+
63+
private fun Project.registerCompileModuleInfoTask(taskName: String, modulePath: FileCollection, destinationDir: DirectoryProperty, moduleInfoSourceFile: File) =
64+
tasks.register(taskName, JavaCompile::class) {
65+
dependsOn(modulePath)
66+
source(moduleInfoSourceFile)
67+
classpath = files()
68+
destinationDirectory.set(destinationDir)
69+
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
70+
targetCompatibility = JavaVersion.VERSION_1_9.toString()
71+
doFirst {
72+
options.compilerArgs = listOf(
73+
"--release", "9",
74+
"--module-path", modulePath.asPath,
75+
"-Xlint:-requires-transitive-automatic"
76+
)
77+
}
78+
}
79+
80+
private fun Project.registerCheckModuleInfoTask(taskName: String, modulePath: FileCollection, moduleInfoClassFile: File) =
81+
tasks.register(taskName) {
82+
dependsOn(modulePath)
83+
doLast {
84+
val jdeps = ToolProvider.findFirst("jdeps").orElseThrow { IllegalStateException("Tool 'jdeps' is not available") }
85+
val moduleDescriptor = moduleInfoClassFile.inputStream().use { ModuleDescriptor.read(it) }
86+
val moduleName = moduleDescriptor.name()
87+
val expectedOutput = moduleDescriptor.toJdepsOutput(moduleInfoClassFile)
88+
89+
val outputCaptureStream = ByteArrayOutputStream()
90+
val printStream = PrintStream(outputCaptureStream, true, Charsets.UTF_8)
91+
jdeps.run(
92+
printStream, printStream,
93+
"--multi-release", "9",
94+
"--module-path", (modulePath + files(moduleInfoClassFile.parentFile)).asPath,
95+
"--check", moduleName
96+
)
97+
val actualOutput = outputCaptureStream.toString(Charsets.UTF_8).trim()
98+
99+
if (actualOutput != expectedOutput) {
100+
throw IllegalStateException("Module-info requirements section does not match!\n$actualOutput")
101+
}
102+
}
103+
}
104+
105+
private fun ModuleDescriptor.toJdepsOutput(file: File, separator: String = System.lineSeparator()) =
106+
"${name()} (${file.parentFile.toURI().toString().replace("file:", "file://")})$separator [Module descriptor]$separator" +
107+
requires().sortedBy { it.name() }.joinToString(separator) { requirement -> " requires $requirement;" }
108+
}

core/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ tasks.withType(Jar).named(kotlin.jvm().artifactsTaskName) {
4242
)
4343
}
4444
}
45+
46+
Java9Modularity.configureJava9ModuleInfo(project)

core/jvmMain/src/module-info.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module kotlinx.serialization.core {
2+
requires transitive kotlin.stdlib;
3+
4+
exports kotlinx.serialization;
5+
exports kotlinx.serialization.builtins;
6+
exports kotlinx.serialization.descriptors;
7+
exports kotlinx.serialization.encoding;
8+
exports kotlinx.serialization.internal;
9+
exports kotlinx.serialization.modules;
10+
}

docs/building.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Building Kotlin Serialization from the source
22

3+
## JDK version
4+
5+
To build Kotlin Serialization JDK version 9 or higher is required.
6+
This is needed to compile the `module-info` file included for JPMS support.
7+
38
## Runtime library
49

510
Kotlin Serialization runtime library itself is a [multiplatform](http://kotlinlang.org/docs/reference/multiplatform.html) project.

formats/cbor/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ kotlin {
2929
}
3030
}
3131
}
32+
33+
Java9Modularity.configureJava9ModuleInfo(project)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module kotlinx.serialization.cbor {
2+
requires transitive kotlin.stdlib;
3+
requires transitive kotlinx.serialization.core;
4+
5+
exports kotlinx.serialization.cbor;
6+
}

formats/hocon/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ dependencies {
1313
compile project(':kotlinx-serialization-core')
1414
api 'org.jetbrains.kotlin:kotlin-stdlib'
1515

16-
api 'com.typesafe:config:1.3.2'
16+
api 'com.typesafe:config:1.4.1'
1717

1818
testCompile "org.jetbrains.kotlin:kotlin-test"
1919
testCompile group: 'junit', name: 'junit', version: '4.12'
2020
}
21+
22+
Java9Modularity.configureJava9ModuleInfo(project)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module kotlinx.serialization.hocon {
2+
requires transitive kotlin.stdlib;
3+
requires transitive kotlinx.serialization.core;
4+
requires transitive typesafe.config;
5+
6+
exports kotlinx.serialization.hocon;
7+
}

formats/json/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ kotlin {
2828
compileTestKotlinJsLegacy {
2929
exclude '**/PropertyInitializerTest.kt'
3030
}
31+
32+
Java9Modularity.configureJava9ModuleInfo(project)

0 commit comments

Comments
 (0)