Skip to content

Add explicit module-info for JPMS compatibility #135

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

Merged
merged 17 commits into from
Nov 14, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ git submodule init
git submodule update
```

The path to JDK 8 must be specified either with the environment variable `JDK_8` or
with the gradle property `JDK_8`. For local builds, you can use a later version of JDK if you don't have that
The path to JDK 11 must be specified either with the environment variable `JDK_11` or
with the gradle property `JDK_11`. For local builds, you can use a later version of JDK if you don't have that
version installed.

After that, the project can be opened in IDEA and built with Gradle.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fun jdkPath(version: Int): String {
error("Specify path to JDK $version in JDK_$version environment variable or Gradle property")
}
//val JDK_6 by ext(jdkPath(6))
val JDK_8 by ext(jdkPath(8))
//val JDK_8 by ext(jdkPath(8))
val JDK_11 by ext(jdkPath(11))

allprojects {
repositories {
Expand Down
14 changes: 14 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import org.jetbrains.kotlin.gradle.plugin.*
import java.util.*

plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30")
}
79 changes: 79 additions & 0 deletions buildSrc/src/main/kotlin/Java9Modularity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import org.gradle.api.*
import org.gradle.api.file.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
import org.gradle.kotlin.dsl.*
import org.gradle.util.GUtil.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.targets.jvm.*
import java.io.*

object Java9Modularity {

@JvmStatic
@JvmOverloads
fun configureJava9ModuleInfo(project: Project, multiRelease: Boolean = true) {
val kotlin = project.extensions.findByType<KotlinProjectExtension>() ?: return
project.configureModuleInfoForKotlinProject(kotlin, multiRelease)
}

private fun Project.configureModuleInfoForKotlinProject(kotlin: KotlinProjectExtension, multiRelease: Boolean = true) {
val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*> }
if (jvmTargets.isEmpty()) {
logger.warn("No Kotlin JVM targets found, can't configure compilation of module-info!")
}
jvmTargets.forEach { target ->
target.compilations.forEach { compilation ->
configureModuleInfoForKotlinCompilation(compilation)
}

if (multiRelease) {
tasks.getByName<Jar>(target.artifactsTaskName) {
rename("module-info.class", "META-INF/versions/9/module-info.class")
manifest {
attributes("Multi-Release" to true)
}
}
}
}
}

private fun Project.configureModuleInfoForKotlinCompilation(compilation: KotlinCompilation<*>) {
val defaultSourceSet = compilation.defaultSourceSet.kotlin
val moduleInfoSourceFile = defaultSourceSet.find { it.name == "module-info.java" }

if (moduleInfoSourceFile == null) {
logger.info("No module-info.java file found in ${defaultSourceSet.srcDirs}, can't configure compilation of module-info!")
} else {
val targetName = toCamelCase(compilation.target.targetName)
val compilationName = if (compilation.name != KotlinCompilation.MAIN_COMPILATION_NAME) toCamelCase(compilation.name) else ""
val compileModuleInfoTaskName = "compile${compilationName}ModuleInfo$targetName"

val compileKotlinTask = compilation.compileKotlinTask as AbstractCompile
val modulePath = compileKotlinTask.classpath

val compileModuleInfoTask = registerCompileModuleInfoTask(compileModuleInfoTaskName, modulePath, compileKotlinTask.destinationDirectory, moduleInfoSourceFile)
tasks.getByName(compilation.compileAllTaskName).dependsOn(compileModuleInfoTask)
}
}

private fun Project.registerCompileModuleInfoTask(taskName: String, modulePath: FileCollection, destinationDir: DirectoryProperty, moduleInfoSourceFile: File) =
tasks.register(taskName, JavaCompile::class) {
dependsOn(modulePath)
source(moduleInfoSourceFile)
classpath = files()
destinationDirectory.set(destinationDir)
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
targetCompatibility = JavaVersion.VERSION_1_9.toString()
doFirst {
options.compilerArgs = listOf(
"--release", "9",
"--module-path", modulePath.asPath,
"-Xlint:-requires-transitive-automatic"
)
}
}
}
11 changes: 7 additions & 4 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ base {
}

//val JDK_6: String by project
val JDK_8: String by project
//val JDK_8: String by project
val JDK_11: String by project
val serializationVersion: String by project

kotlin {
Expand Down Expand Up @@ -49,7 +50,7 @@ kotlin {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
jdkHome = JDK_8
jdkHome = JDK_11
}
}

Expand Down Expand Up @@ -94,7 +95,7 @@ kotlin {
val suffix = name.substring(suffixIndex).toLowerCase(Locale.ROOT).takeIf { it != "main" }
// println("SOURCE_SET: $name")
kotlin.srcDir("$targetName/${suffix ?: "src"}")
resources.srcDir("$targetName/${suffix?.let { it + "Resources "} ?: "resources"}")
resources.srcDir("$targetName/${suffix?.let { it + "Resources " } ?: "resources"}")
languageSettings.apply {
// progressiveMode = true
useExperimentalAnnotation("kotlin.Experimental")
Expand Down Expand Up @@ -194,7 +195,7 @@ kotlin {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-js")
api("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation(npm("@js-joda/core", "3.2.0"))
implementation(npm("@js-joda/core", "3.2.0"))
}
}

Expand Down Expand Up @@ -289,3 +290,5 @@ task("downloadWindowsZonesMapping") {
}
}
}

Java9Modularity.configureJava9ModuleInfo(project)
7 changes: 7 additions & 0 deletions core/jvm/src/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module kotlinx.datetime {
requires transitive kotlin.stdlib;
requires transitive static kotlinx.serialization.core;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted here that we need requires static because the dependency is compile only. Does it mean that a client module has to require kotlinx.serialization.core (or another module that requires it transitively) in order to use this aspect of kotlinx.datetime?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and no. Normally a modular application won't start if 1 of the required modules isn't on the module path.
Because of the requires static an application will start anyway, ignoring the 'missing' module.
Obviously if you want to use the datetime serializers you will have to explicitly require kotlinx.serialization.core anyway because all the serializer interfaces and logic is in there.


exports kotlinx.datetime;
exports kotlinx.datetime.serializers;
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ group=org.jetbrains.kotlinx
version=0.2.1
versionSuffix=SNAPSHOT

kotlinVersion=1.5.0
serializationVersion=1.2.1
kotlinVersion=1.5.30
serializationVersion=1.3.0-RC

kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
Expand Down
5 changes: 3 additions & 2 deletions serialization/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ plugins {
kotlin("plugin.serialization")
}

val JDK_8: String by project
//val JDK_8: String by project
val JDK_11: String by project
val serializationVersion: String by project

kotlin {
Expand All @@ -31,7 +32,7 @@ kotlin {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
jdkHome = JDK_8
jdkHome = JDK_11
}
}

Expand Down