Skip to content
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 for JPMS compatibility #135

Merged
merged 17 commits into from
Nov 14, 2021
Merged
Show file tree
Hide file tree
Changes from 8 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 @@ -362,8 +362,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
17 changes: 17 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import java.util.Properties

plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
}

val kotlinVersion = file("../gradle.properties").inputStream().use {
Properties().apply { load(it) }
}.getProperty("kotlinVersion") ?: throw IllegalStateException("Property 'kotlinVersion' must be defined in ../gradle.properties")
Copy link
Member

Choose a reason for hiding this comment

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

This approach significantly limits the ways how kotlinVersion can be defined. Due to that we'd like to avoid having the code that depends on kotlin plugin in buildSrc, especially considering that it is only needed in one subproject so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally agree, this was actually the most annoying part. I just wished that the buildSrc project had access to the properties of the root project, but that is unfortunately not the case.

Anyway, I'll move the relevant code to an (simplified) inline block inside the core subproject, since that is the only place that uses this.


dependencies {
implementation(kotlin("gradle-plugin", kotlinVersion))
}
106 changes: 106 additions & 0 deletions buildSrc/src/main/kotlin/Java9Modularity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import org.gradle.api.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.dsl.*
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 ->
val artifactTask = tasks.getByName<Jar>(target.artifactsTaskName) {
if (multiRelease) {
manifest {
attributes("Multi-Release" to true)
}
}
}

target.compilations.forEach { compilation ->
val compileKotlinTask = compilation.compileKotlinTask as AbstractCompile
val defaultSourceSet = compilation.defaultSourceSet

// derive the names of the source set and compile module task
val sourceSetName = defaultSourceSet.name + "Module"
val compileModuleTaskName = compileKotlinTask.name + "Module"

kotlin.sourceSets.create(sourceSetName) {
val sourceFile = this.kotlin.find { it.name == "module-info.java" }
val targetFile = compileKotlinTask.destinationDirectory.file("../module-info.class").get().asFile

// only configure the compilation if necessary
if (sourceFile != null) {
// the default source set depends on this new source set
defaultSourceSet.dependsOn(this)

// register a new compile module task
val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetFile)

// add the resulting module descriptor to this target's artifact
artifactTask.dependsOn(compileModuleTask)
artifactTask.from(targetFile) {
if (multiRelease) {
into("META-INF/versions/9/")
}
}
} else {
logger.info("No module-info.java file found in ${this.kotlin.srcDirs}, can't configure compilation of module-info!")
// remove the source set to prevent Gradle warnings
kotlin.sourceSets.remove(this)
}
}
}
}
}

private fun Project.registerCompileModuleTask(taskName: String, compileTask: AbstractCompile, sourceFile: File, targetFile: File) =
tasks.register(taskName, JavaCompile::class) {
// Also add the module-info.java source file to the Kotlin compile task;
// the Kotlin compiler will parse and check module dependencies,
// but it currently won't compile to a module-info.class file.
compileTask.source(sourceFile)


// Configure the module compile task.
dependsOn(compileTask)
source(sourceFile)
outputs.file(targetFile)
classpath = files()
destinationDirectory.set(compileTask.destinationDirectory)
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
targetCompatibility = JavaVersion.VERSION_1_9.toString()

doFirst {
// Provide the module path to the compiler instead of using a classpath.
// The module path should be the same as the classpath of the compiler.
options.compilerArgs = listOf(
"--release", "9",
"--module-path", compileTask.classpath.asPath,
"-Xlint:-requires-transitive-automatic"
)
}

doLast {
// Move the compiled file out of the Kotlin compile task's destination dir,
// so it won't disturb Gradle's caching mechanisms.
val compiledFile = destinationDirectory.file(targetFile.name).get().asFile
targetFile.parentFile.mkdirs()
compiledFile.renameTo(targetFile)
}
}
}
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 @@ -53,7 +54,7 @@ kotlin {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
jdkHome = JDK_8
jdkHome = JDK_11
}
}

Expand Down Expand Up @@ -98,7 +99,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 @@ -198,7 +199,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 @@ -293,3 +294,5 @@ task("downloadWindowsZonesMapping") {
}
}
}

Java9Modularity.configureJava9ModuleInfo(project)
7 changes: 7 additions & 0 deletions core/jvmMain/module/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;

exports kotlinx.datetime;
exports kotlinx.datetime.serializers;
}
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 Down Expand Up @@ -35,7 +36,7 @@ kotlin {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
jdkHome = JDK_8
jdkHome = JDK_11
}
}

Expand Down