summaryrefslogtreecommitdiff
path: root/buildSrc/src/main/kotlin/Java9Modularity.kt
diff options
context:
space:
mode:
Diffstat (limited to 'buildSrc/src/main/kotlin/Java9Modularity.kt')
-rw-r--r--buildSrc/src/main/kotlin/Java9Modularity.kt208
1 files changed, 167 insertions, 41 deletions
diff --git a/buildSrc/src/main/kotlin/Java9Modularity.kt b/buildSrc/src/main/kotlin/Java9Modularity.kt
index 05052972..2743b00f 100644
--- a/buildSrc/src/main/kotlin/Java9Modularity.kt
+++ b/buildSrc/src/main/kotlin/Java9Modularity.kt
@@ -1,20 +1,41 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
import org.gradle.api.*
+import org.gradle.api.file.*
+import org.gradle.api.provider.*
+import org.gradle.api.tasks.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
+import org.gradle.jvm.toolchain.*
import org.gradle.kotlin.dsl.*
+import org.gradle.language.base.plugins.LifecycleBasePlugin.*
+import org.gradle.process.*
+import org.jetbrains.kotlin.gradle.*
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.plugin.mpp.pm20.util.*
import org.jetbrains.kotlin.gradle.targets.jvm.*
+import org.jetbrains.kotlin.gradle.tasks.*
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
+import org.jetbrains.kotlin.tooling.core.*
import java.io.*
+import kotlin.reflect.*
+import kotlin.reflect.full.*
object Java9Modularity {
@JvmStatic
@JvmOverloads
fun Project.configureJava9ModuleInfo(multiRelease: Boolean = true) {
+ val disableJPMS = this.rootProject.extra.has("disableJPMS")
+ val ideaActive = System.getProperty("idea.active") == "true"
+ if (disableJPMS || ideaActive) return
val kotlin = extensions.findByType<KotlinProjectExtension>() ?: return
- val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*> }
+ 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!")
}
@@ -28,75 +49,180 @@ object Java9Modularity {
}
target.compilations.forEach { compilation ->
- val compileKotlinTask = compilation.compileKotlinTask as AbstractCompile
+ val compileKotlinTask = compilation.compileKotlinTask as KotlinCompile
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
+ val targetDirectory = compileKotlinTask.destinationDirectory.map {
+ it.dir("../${it.asFile.name}Module")
+ }
// only configure the compilation if necessary
if (sourceFile != null) {
- // the default source set depends on this new source set
- defaultSourceSet.dependsOn(this)
+ // register and wire a task to verify module-info.java content
+ //
+ // this will compile the whole sources again with a JPMS-aware target Java version,
+ // so that the Kotlin compiler can do the necessary verifications
+ // while compiling with `jdk-release=1.8` those verifications are not done
+ //
+ // this task is only going to be executed when running with `check` or explicitly,
+ // not during normal build operations
+ val verifyModuleTask = registerVerifyModuleTask(
+ compileKotlinTask,
+ sourceFile
+ )
+ tasks.named("check") {
+ dependsOn(verifyModuleTask)
+ }
// register a new compile module task
- val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetFile)
+ val compileModuleTask = registerCompileModuleTask(
+ compileKotlinTask,
+ sourceFile,
+ targetDirectory
+ )
// add the resulting module descriptor to this target's artifact
- artifactTask.dependsOn(compileModuleTask)
- artifactTask.from(targetFile) {
+ artifactTask.from(compileModuleTask.map { it.destinationDirectory }) {
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)
}
+
+ // 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)
+ /**
+ * Add a Kotlin compile task that compiles `module-info.java` source file and Kotlin sources together,
+ * the Kotlin compiler will parse and check module dependencies,
+ * but it currently won't compile to a module-info.class file.
+ */
+ private fun Project.registerVerifyModuleTask(
+ compileTask: KotlinCompile,
+ sourceFile: File
+ ): TaskProvider<out KotlinJvmCompile> {
+ apply<KotlinApiPlugin>()
+ val verifyModuleTaskName = "verify${compileTask.name.removePrefix("compile").capitalize()}Module"
+ // work-around for https://youtrack.jetbrains.com/issue/KT-60542
+ val kotlinApiPlugin = plugins.getPlugin(KotlinApiPlugin::class)
+ val verifyModuleTask = kotlinApiPlugin.registerKotlinJvmCompileTask(
+ verifyModuleTaskName,
+ compileTask.compilerOptions.moduleName.get()
+ )
+ verifyModuleTask {
+ group = VERIFICATION_GROUP
+ description = "Verify Kotlin sources for JPMS problems"
+ libraries.from(compileTask.libraries)
+ source(compileTask.sources)
+ source(compileTask.javaSources)
+ // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541
+ @Suppress("INVISIBLE_MEMBER")
+ source(compileTask.scriptSources)
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"
+ destinationDirectory.set(temporaryDir)
+ multiPlatformEnabled.set(compileTask.multiPlatformEnabled)
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_9)
+ // To support LV override when set in aggregate builds
+ languageVersion.set(compileTask.compilerOptions.languageVersion)
+ freeCompilerArgs.addAll(
+ listOf("-Xjdk-release=9", "-Xsuppress-version-warnings", "-Xexpect-actual-classes")
)
+ optIn.addAll(compileTask.kotlinOptions.options.optIn)
+ }
+ // work-around for https://youtrack.jetbrains.com/issue/KT-60583
+ inputs.files(
+ libraries.asFileTree.elements.map { libs ->
+ libs
+ .filter { it.asFile.exists() }
+ .map {
+ zipTree(it.asFile).filter { it.name == "module-info.class" }
+ }
+ }
+ ).withPropertyName("moduleInfosOfLibraries")
+ this as KotlinCompile
+ val kotlinPluginVersion = KotlinToolingVersion(kotlinApiPlugin.pluginVersion)
+ if (kotlinPluginVersion <= KotlinToolingVersion("1.9.255")) {
+ // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541
+ @Suppress("UNCHECKED_CAST")
+ val ownModuleNameProp = (this::class.superclasses.first() as KClass<AbstractKotlinCompile<*>>)
+ .declaredMemberProperties
+ .find { it.name == "ownModuleName" }
+ ?.get(this) as? Property<String>
+ ownModuleNameProp?.set(compileTask.kotlinOptions.moduleName)
}
- 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)
+ val taskKotlinLanguageVersion = compilerOptions.languageVersion.orElse(KotlinVersion.DEFAULT)
+ @OptIn(InternalKotlinGradlePluginApi::class)
+ if (taskKotlinLanguageVersion.get() < KotlinVersion.KOTLIN_2_0) {
+ // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541
+ @Suppress("INVISIBLE_MEMBER")
+ commonSourceSet.from(compileTask.commonSourceSet)
+ } else {
+ multiplatformStructure.refinesEdges.set(compileTask.multiplatformStructure.refinesEdges)
+ multiplatformStructure.fragments.set(compileTask.multiplatformStructure.fragments)
}
+ // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541
+ // and work-around for https://youtrack.jetbrains.com/issue/KT-60582
+ incremental = false
}
+ return verifyModuleTask
+ }
+
+ private fun Project.registerCompileModuleTask(
+ compileTask: KotlinCompile,
+ sourceFile: File,
+ targetDirectory: Provider<out Directory>
+ ) = tasks.register("${compileTask.name}Module", JavaCompile::class) {
+ // Configure the module compile task.
+ source(sourceFile)
+ classpath = files()
+ destinationDirectory.set(targetDirectory)
+ // use a Java 11 toolchain with release 9 option
+ // because for some OS / architecture combinations
+ // there are no Java 9 builds available
+ javaCompiler.set(
+ this@registerCompileModuleTask.the<JavaToolchainService>().compilerFor {
+ languageVersion.set(JavaLanguageVersion.of(11))
+ }
+ )
+ options.release.set(9)
+
+ options.compilerArgumentProviders.add(object : CommandLineArgumentProvider {
+ @get:CompileClasspath
+ val compileClasspath = compileTask.libraries
+
+ @get:CompileClasspath
+ val compiledClasses = compileTask.destinationDirectory
+
+ @get:Input
+ val moduleName = sourceFile
+ .readLines()
+ .single { it.contains("module ") }
+ .substringAfter("module ")
+ .substringBefore(' ')
+ .trim()
+
+ override fun asArguments() = mutableListOf(
+ // 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.
+ "--module-path",
+ compileClasspath.asPath,
+ "--patch-module",
+ "$moduleName=${compiledClasses.get()}",
+ "-Xlint:-requires-transitive-automatic"
+ )
+ })
+ }
}