Consider toolchain only as input for 'jdkHome' compilation option.

This change will limit toolchain scope only to the JVM compilations,
as Kotlin compiler itself does not care about JDK version it is running
 on and should always produce same output for non-JVM backends.
For JVM backend there is one condition - 'jdkHome' should always point
 to the same JDK version.

Few additional changes:
- allow to set specific JDK location even on Gradle versions 6.7+
- Fix typo in deprecation message

^KT-45611 In Progress
^KT-43095 In Progress
This commit is contained in:
Yahor Berdnikau
2021-06-15 12:04:57 +02:00
committed by Space
parent 1bb64ffa22
commit 46d5df4991
11 changed files with 223 additions and 285 deletions
@@ -21,14 +21,15 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
@GradleTestVersions(additionalVersions = ["6.7.1"]) @GradleTestVersions(additionalVersions = ["6.7.1"])
@GradleTest @GradleTest
@DisplayName("Should use by default same jvm as Gradle daemon") @DisplayName("Should use by default same jvm as Gradle daemon for jdkHome")
internal fun byDefaultShouldUseGradleJDK(gradleVersion: GradleVersion) { internal fun byDefaultShouldUseGradleJDK(gradleVersion: GradleVersion) {
project( project(
projectName = "simple".fullProjectName, projectName = "simple".fullProjectName,
gradleVersion = gradleVersion, gradleVersion = gradleVersion
) { ) {
build("assemble") { build("assemble", enableGradleDebug = true) {
assertDaemonIsUsingJdk(getUserJdk().javaExecutableRealPath) assertOutputDoesNotContain("'kotlinOptions.jdkHome' is deprecated and will be ignored in Kotlin 1.7!")
assertJdkHomeIsUsingJdk(getUserJdk().javaHomeRealPath)
} }
} }
} }
@@ -41,7 +42,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
projectName = "simple".fullProjectName, projectName = "simple".fullProjectName,
gradleVersion = gradleVersion gradleVersion = gradleVersion
) { ) {
//lang=Groovy //language=Groovy
rootBuildGradle.append( rootBuildGradle.append(
""" """
@@ -65,23 +66,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
useJdk9ToCompile() useJdk9ToCompile()
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk(getJdk9().javaExecutableRealPath) assertJdkHomeIsUsingJdk(getJdk9().javaHomeRealPath)
} }
} }
} }
@Test
@DisplayName("Should fail the build on setting custom JDK when toolchain is available")
internal fun errorOnSettingJdkWhenToolchainIsAvailable() {
project(
projectName = "simple".fullProjectName,
gradleVersion = GradleVersion.version("6.7.1"),
) {
useJdk9ToCompile()
buildAndFail("assemble")
}
}
@GradleTest @GradleTest
@DisplayName("KotlinCompile task should use build cache when using provided JDK") @DisplayName("KotlinCompile task should use build cache when using provided JDK")
internal fun customJdkBuildCache(gradleVersion: GradleVersion) { internal fun customJdkBuildCache(gradleVersion: GradleVersion) {
@@ -174,11 +163,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
) )
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk( assertJdkHomeIsUsingJdk(
if (shouldUseToolchain(gradleVersion)) { if (shouldUseToolchain(gradleVersion)) {
getToolchainExecPathFromLogs() getToolchainExecPathFromLogs()
} else { } else {
getJdk9().javaExecutableRealPath getJdk9().javaHomeRealPath
} }
) )
@@ -281,7 +270,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
} }
} }
@DisplayName("jdkHome Kotlin option should produce deprecation warning on Gradle builds") @DisplayName("User provided jdkHome Kotlin option should produce deprecation warning on Gradle builds")
@GradleTest @GradleTest
internal fun jdkHomeIsDeprecated(gradleVersion: GradleVersion) { internal fun jdkHomeIsDeprecated(gradleVersion: GradleVersion) {
project( project(
@@ -302,9 +291,8 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
""".trimIndent() """.trimIndent()
) )
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk(getUserJdk().javaExecutableRealPath) assertJdkHomeIsUsingJdk(getJdk9().javaHomeRealPath)
assertOutputContains("'kotlinOptions.jdkHome' is deprecated and will ignored in Kotlin 1.6!") assertOutputContains("'kotlinOptions.jdkHome' is deprecated and will be ignored in Kotlin 1.7!")
assertOutputContains("-jdk-home ${getJdk9().javaHome.absolutePath}")
} }
} }
} }
@@ -320,7 +308,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
) { ) {
useToolchainToCompile(11) useToolchainToCompile(11)
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk(getToolchainExecPathFromLogs()) assertJdkHomeIsUsingJdk(getToolchainExecPathFromLogs())
} }
} }
} }
@@ -336,21 +324,21 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
) { ) {
useToolchainExtension(11) useToolchainExtension(11)
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk(getToolchainExecPathFromLogs()) assertJdkHomeIsUsingJdk(getToolchainExecPathFromLogs())
} }
} }
} }
private fun BuildResult.assertDaemonIsUsingJdk( private fun BuildResult.assertJdkHomeIsUsingJdk(
javaexecPath: String javaexecPath: String
) = assertOutputContains("i: connected to the daemon. Daemon is using following 'java' executable to run itself: $javaexecPath") ) = assertOutputContains("[KOTLIN] Kotlin compilation 'jdkHome' argument: $javaexecPath")
private fun getUserJdk(): JavaInfo = Jvm.forHome(File(System.getenv("JAVA_HOME"))) private fun getUserJdk(): JavaInfo = Jvm.forHome(File(System.getenv("JAVA_HOME")))
private fun getJdk9(): JavaInfo = Jvm.forHome(File(System.getenv("JDK_9"))) private fun getJdk9(): JavaInfo = Jvm.forHome(File(System.getenv("JDK_9")))
// replace required for windows paths so Groovy will not complain about unexpected char '\' // replace required for windows paths so Groovy will not complain about unexpected char '\'
private fun getJdk9Path(): String = getJdk9().javaHome.absolutePath.replace("\\", "\\\\") private fun getJdk9Path(): String = getJdk9().javaHome.absolutePath.replace("\\", "\\\\")
private val JavaInfo.javaExecutableRealPath private val JavaInfo.javaHomeRealPath
get() = javaExecutable get() = javaHome
.toPath() .toPath()
.toRealPath() .toRealPath()
.toAbsolutePath() .toAbsolutePath()
@@ -399,7 +387,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
} }
afterEvaluate { afterEvaluate {
logger.info("Toolchain exec path: ${'$'}{defaultLauncher.get().executablePath.asFile.absolutePath}") logger.info("Toolchain jdk path: ${'$'}{defaultLauncher.get().metadata.installationPath.asFile.absolutePath}")
} }
""".trimIndent() """.trimIndent()
) )
@@ -425,7 +413,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain
def service = project.extensions.getByType(JavaToolchainService.class) def service = project.extensions.getByType(JavaToolchainService.class)
def defaultLauncher = service.launcherFor(toolchain) def defaultLauncher = service.launcherFor(toolchain)
logger.info("Toolchain exec path: ${'$'}{defaultLauncher.get().executablePath.asFile.absolutePath}") logger.info("Toolchain jdk path: ${'$'}{defaultLauncher.get().metadata.installationPath.asFile.absolutePath}")
} }
""".trimIndent() """.trimIndent()
) )
@@ -435,6 +423,6 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
private fun BuildResult.getToolchainExecPathFromLogs() = output private fun BuildResult.getToolchainExecPathFromLogs() = output
.lineSequence() .lineSequence()
.first { it.startsWith("Toolchain exec path:") } .first { it.startsWith("Toolchain jdk path:") }
.substringAfter("Toolchain exec path: ") .substringAfter("Toolchain jdk path: ")
} }
@@ -18,10 +18,9 @@ import java.io.File
*/ */
internal class GradleCompilerRunnerWithWorkers( internal class GradleCompilerRunnerWithWorkers(
taskProvider: GradleCompileTaskProvider, taskProvider: GradleCompileTaskProvider,
javaExecutable: File,
jdkToolsJar: File?, jdkToolsJar: File?,
private val workerExecutor: WorkerExecutor private val workerExecutor: WorkerExecutor
) : GradleCompilerRunner(taskProvider, javaExecutable, jdkToolsJar) { ) : GradleCompilerRunner(taskProvider, jdkToolsJar) {
override fun runCompilerAsync(workArgs: GradleKotlinCompilerWorkArguments) { override fun runCompilerAsync(workArgs: GradleKotlinCompilerWorkArguments) {
loggerProvider.kotlinDebug { "Starting Kotlin compiler work from task '${pathProvider}'" } loggerProvider.kotlinDebug { "Starting Kotlin compiler work from task '${pathProvider}'" }
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.daemon.common.CompilerId
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
@@ -55,7 +56,6 @@ is not assignable to 'org.gradle.api.tasks.TaskProvider'" exception
*/ */
internal open class GradleCompilerRunner( internal open class GradleCompilerRunner(
protected val taskProvider: GradleCompileTaskProvider, protected val taskProvider: GradleCompileTaskProvider,
protected val javaExecutable: File,
protected val jdkToolsJar: File? protected val jdkToolsJar: File?
) { ) {
@@ -78,12 +78,15 @@ internal open class GradleCompilerRunner(
javaSourceRoots: Iterable<File>, javaSourceRoots: Iterable<File>,
javaPackagePrefix: String?, javaPackagePrefix: String?,
args: K2JVMCompilerArguments, args: K2JVMCompilerArguments,
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment,
jdkHome: File
) { ) {
args.freeArgs += sourcesToCompile.map { it.absolutePath } args.freeArgs += sourcesToCompile.map { it.absolutePath }
args.commonSources = commonSources.map { it.absolutePath }.toTypedArray() args.commonSources = commonSources.map { it.absolutePath }.toTypedArray()
args.javaSourceRoots = javaSourceRoots.map { it.absolutePath }.toTypedArray() args.javaSourceRoots = javaSourceRoots.map { it.absolutePath }.toTypedArray()
args.javaPackagePrefix = javaPackagePrefix args.javaPackagePrefix = javaPackagePrefix
if (args.jdkHome == null) args.jdkHome = jdkHome.absolutePath
loggerProvider.kotlinInfo("Kotlin compilation 'jdkHome' argument: ${args.jdkHome}")
runCompilerAsync(KotlinCompilerClass.JVM, args, environment) runCompilerAsync(KotlinCompilerClass.JVM, args, environment)
} }
@@ -165,8 +168,7 @@ internal open class GradleCompilerRunner(
taskPath = pathProvider, taskPath = pathProvider,
reportingSettings = environment.reportingSettings, reportingSettings = environment.reportingSettings,
kotlinScriptExtensions = environment.kotlinScriptExtensions, kotlinScriptExtensions = environment.kotlinScriptExtensions,
allWarningsAsErrors = compilerArgs.allWarningsAsErrors, allWarningsAsErrors = compilerArgs.allWarningsAsErrors
javaExecutable = javaExecutable
) )
TaskLoggers.put(pathProvider, loggerProvider) TaskLoggers.put(pathProvider, loggerProvider)
runCompilerAsync(workArgs) runCompilerAsync(workArgs)
@@ -183,11 +185,10 @@ internal open class GradleCompilerRunner(
clientIsAliveFlagFile: File, clientIsAliveFlagFile: File,
sessionIsAliveFlagFile: File, sessionIsAliveFlagFile: File,
compilerFullClasspath: List<File>, compilerFullClasspath: List<File>,
javaExecutable: File,
messageCollector: MessageCollector, messageCollector: MessageCollector,
isDebugEnabled: Boolean isDebugEnabled: Boolean
): CompileServiceSession? { ): CompileServiceSession? {
val compilerId = CompilerId.makeCompilerId(compilerFullClasspath, javaExecutable) val compilerId = CompilerId.makeCompilerId(compilerFullClasspath)
val additionalJvmParams = arrayListOf<String>() val additionalJvmParams = arrayListOf<String>()
return KotlinCompilerRunnerUtils.newDaemonConnection( return KotlinCompilerRunnerUtils.newDaemonConnection(
compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile, compilerId, clientIsAliveFlagFile, sessionIsAliveFlagFile,
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.build.ExecutionStrategy
import org.jetbrains.kotlin.build.report.metrics.* import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
@@ -64,8 +62,7 @@ internal class GradleKotlinCompilerWorkArguments(
val taskPath: String, val taskPath: String,
val reportingSettings: ReportingSettings, val reportingSettings: ReportingSettings,
val kotlinScriptExtensions: Array<String>, val kotlinScriptExtensions: Array<String>,
val allWarningsAsErrors: Boolean, val allWarningsAsErrors: Boolean
val javaExecutable: File
) : Serializable { ) : Serializable {
companion object { companion object {
const val serialVersionUID: Long = 0 const val serialVersionUID: Long = 0
@@ -108,7 +105,6 @@ internal class GradleKotlinCompilerWork @Inject constructor(
private val buildDir = config.projectFiles.buildDir private val buildDir = config.projectFiles.buildDir
private val metrics = if (reportingSettings.reportMetrics) BuildMetricsReporterImpl() else DoNothingBuildMetricsReporter private val metrics = if (reportingSettings.reportMetrics) BuildMetricsReporterImpl() else DoNothingBuildMetricsReporter
private var icLogLines: List<String> = emptyList() private var icLogLines: List<String> = emptyList()
private val javaExecutable = config.javaExecutable
private val log: KotlinLogger = private val log: KotlinLogger =
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } } TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
@@ -178,7 +174,6 @@ internal class GradleKotlinCompilerWork @Inject constructor(
clientIsAliveFlagFile, clientIsAliveFlagFile,
sessionFlagFile, sessionFlagFile,
compilerFullClasspath, compilerFullClasspath,
javaExecutable,
daemonMessageCollector, daemonMessageCollector,
isDebugEnabled = isDebugEnabled isDebugEnabled = isDebugEnabled
) )
@@ -114,7 +114,7 @@ internal open class KotlinJvmCompilerArgumentsContributor(
?.run { ?.run {
logger.warn( logger.warn(
""" """
'kotlinOptions.jdkHome' is deprecated and will ignored in Kotlin 1.6! 'kotlinOptions.jdkHome' is deprecated and will be ignored in Kotlin 1.7!
Consider using JavaToolchain on Gradle 6.7+: Consider using JavaToolchain on Gradle 6.7+:
kotlin { kotlin {
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
@@ -113,8 +112,7 @@ abstract class KaptWithKotlincTask @Inject constructor(
val compilerRunner = GradleCompilerRunner( val compilerRunner = GradleCompilerRunner(
taskProvider, taskProvider,
kotlinJavaToolchainProvider.get().jdkProvider.javaExecutable.get().asFile, kotlinJavaToolchainProvider.get().currentJvmJdkToolsJar.orNull
kotlinJavaToolchainProvider.get().jdkProvider.jdkToolsJar.orNull
) )
compilerRunner.runJvmCompilerAsync( compilerRunner.runJvmCompilerAsync(
sourcesToCompile = emptyList(), sourcesToCompile = emptyList(),
@@ -122,7 +120,8 @@ abstract class KaptWithKotlincTask @Inject constructor(
javaSourceRoots = source.files, javaSourceRoots = source.files,
javaPackagePrefix = javaPackagePrefix.orNull, javaPackagePrefix = javaPackagePrefix.orNull,
args = args, args = args,
environment = environment environment = environment,
jdkHome = kotlinJavaToolchainProvider.get().providedJvm.get().javaHome
) )
} }
} }
@@ -158,7 +158,6 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
val isolationMode = getWorkerIsolationMode() val isolationMode = getWorkerIsolationMode()
logger.info("Using workers $isolationMode isolation mode to run kapt") logger.info("Using workers $isolationMode isolation mode to run kapt")
val toolsJarURLSpec = kotlinJavaToolchainProvider.get() val toolsJarURLSpec = kotlinJavaToolchainProvider.get()
.jdkProvider
.jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty() .jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty()
submitWork( submitWork(
@@ -170,12 +169,12 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
} }
private fun getWorkerIsolationMode(): IsolationMode { private fun getWorkerIsolationMode(): IsolationMode {
val jdkProvider = kotlinJavaToolchainProvider.get().jdkProvider val toolchainProvider = kotlinJavaToolchainProvider.get()
val gradleJvm = jdkProvider.currentJvm.get() val gradleJvm = toolchainProvider.currentJvm.get()
// Ensuring Gradle build JDK is set to kotlin toolchain by also comparing javaExecutable paths, // Ensuring Gradle build JDK is set to kotlin toolchain by also comparing javaExecutable paths,
// as user may set JDK with same major Java version, but from different vendor // as user may set JDK with same major Java version, but from different vendor
val isRunningOnGradleJvm = gradleJvm.javaVersion == jdkProvider.javaVersion.get() && val isRunningOnGradleJvm = gradleJvm.javaVersion == toolchainProvider.javaVersion.get() &&
gradleJvm.javaExecutable.absolutePath == jdkProvider.javaExecutable.get().asFile.absolutePath gradleJvm.javaExecutable.absolutePath == toolchainProvider.javaExecutable.get().asFile.absolutePath
val isolationModeStr = getValue("kapt.workers.isolation")?.toLowerCase() val isolationModeStr = getValue("kapt.workers.isolation")?.toLowerCase()
return when { return when {
(isolationModeStr == null || isolationModeStr == "none") && isRunningOnGradleJvm -> IsolationMode.NONE (isolationModeStr == null || isolationModeStr == "none") && isRunningOnGradleJvm -> IsolationMode.NONE
@@ -201,7 +200,6 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
it.forkOptions.jvmArgs("-verbose:class") it.forkOptions.jvmArgs("-verbose:class")
} }
it.forkOptions.executable = kotlinJavaToolchainProvider.get() it.forkOptions.executable = kotlinJavaToolchainProvider.get()
.jdkProvider
.javaExecutable .javaExecutable
.asFile.get() .asFile.get()
.absolutePath .absolutePath
@@ -6,11 +6,12 @@
package org.jetbrains.kotlin.gradle.targets.js.ir package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.CacheableTask
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider import org.jetbrains.kotlin.gradle.utils.propertyWithConvention
import java.io.File
import javax.inject.Inject import javax.inject.Inject
@CacheableTask @CacheableTask
@@ -18,15 +19,16 @@ internal abstract class KotlinJsIrLinkWithWorkers
@Inject @Inject
constructor( constructor(
objectFactory: ObjectFactory, objectFactory: ObjectFactory,
private val workerExecutor: WorkerExecutor workerExecutor: WorkerExecutor
) : KotlinJsIrLink(objectFactory) { ) : KotlinJsIrLink(objectFactory) {
override fun compilerRunner( override val compilerRunner: Provider<GradleCompilerRunner> =
javaExecutable: File, objects.propertyWithConvention(
jdkToolsJar: File? gradleCompileTaskProvider.map {
) = GradleCompilerRunnerWithWorkers( GradleCompilerRunnerWithWorkers(
gradleCompileTaskProvider, it,
javaExecutable, null,
jdkToolsJar, workerExecutor
workerExecutor ) as GradleCompilerRunner
) }
)
} }
@@ -23,190 +23,141 @@ import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.gradle.utils.propertyWithConvention import org.jetbrains.kotlin.gradle.utils.propertyWithConvention
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.full.functions
abstract class KotlinJavaToolchainProvider @Inject constructor( abstract class KotlinJavaToolchainProvider @Inject constructor(
objects: ObjectFactory, private val objects: ObjectFactory,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
gradle: Gradle gradle: Gradle
) : KotlinJavaToolchain { ) : KotlinJavaToolchain {
private val defaultJdkSetter by lazy(LazyThreadSafetyMode.NONE) { private val currentGradleVersion = GradleVersion.version(gradle.gradleVersion)
DefaultJdkSetter(
objects, @get:Internal
projectLayout, internal val currentJvm: Provider<Jvm> = objects
GradleVersion.version(gradle.gradleVersion) .property(Jvm.current())
.chainedFinalizeValueOnRead()
@get:Internal
internal val providedJvm: Property<Jvm> = objects
.propertyWithConvention(currentJvm)
.chainedFinalizeValueOnRead()
final override val javaVersion: Provider<JavaVersion> = objects
.property(
providedJvm.map { jvm ->
jvm.javaVersion
?: throw GradleException(
"Kotlin could not get java version for the JDK installation: " +
jvm.javaHome?.let { "'$it' " }.orEmpty()
)
}
) )
.chainedFinalizeValueOnRead()
@get:Internal
internal val javaExecutable: RegularFileProperty = objects
.fileProperty()
.value(
providedJvm.flatMap { jvm ->
projectLayout.file(
objects.property<File>(
jvm.javaExecutable
?: throw GradleException(
"Kotlin could not find 'java' executable in the JDK installation: " +
jvm.javaHome?.let { "'$it' " }.orEmpty()
)
)
)
}
)
.chainedFinalizeValueOnRead()
private fun getToolsJarFromJvm(jvmProvider: Provider<Jvm>): Provider<File?> {
return objects
.propertyWithConvention(
jvmProvider.flatMap { jvm ->
objects.propertyWithConvention(jvm.toolsJar)
}
)
.orElse(javaVersion.flatMap {
if (it < JavaVersion.VERSION_1_9) {
throw GradleException(
"Kotlin could not find the required JDK tools in the Java installation. " +
"Make sure Kotlin compilation is running on a JDK, not JRE."
)
} else {
objects.propertyWithConvention<File?>(null)
}
})
} }
@get:Internal
internal val jdkToolsJar: Provider<File?> = getToolsJarFromJvm(providedJvm)
@get:Internal
internal val currentJvmJdkToolsJar: Provider<File?> = getToolsJarFromJvm(currentJvm)
final override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter(providedJvm, currentGradleVersion)
private val defaultJavaToolchainSetter by lazy(LazyThreadSafetyMode.NONE) { private val defaultJavaToolchainSetter by lazy(LazyThreadSafetyMode.NONE) {
if (GradleVersion.version(gradle.gradleVersion) >= TOOLCHAIN_SUPPORTED_VERSION) { if (currentGradleVersion >= TOOLCHAIN_SUPPORTED_VERSION) {
DefaultJavaToolchainSetter(objects) DefaultJavaToolchainSetter(providedJvm)
} else { } else {
null null
} }
} }
@get:Internal
internal val jdkProvider: JdkProvider =
if (GradleVersion.version(gradle.gradleVersion) < TOOLCHAIN_SUPPORTED_VERSION) {
defaultJdkSetter
} else {
defaultJavaToolchainSetter!!
}
final override val javaVersion: Provider<JavaVersion>
get() = jdkProvider.javaVersion
final override val jdk: KotlinJavaToolchain.JdkSetter get() = defaultJdkSetter
final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter
get() = defaultJavaToolchainSetter get() = defaultJavaToolchainSetter
?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION") ?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION")
internal interface JdkProvider {
val currentJvm: Property<Jvm>
val javaExecutable: RegularFileProperty
val javaVersion: Property<JavaVersion>
val jdkToolsJar: Provider<File?>
companion object {
fun jdkToolsProperty(
objectsFactory: ObjectFactory,
currentJvm: Provider<Jvm>
): Property<File?> = objectsFactory
.propertyWithConvention(
currentJvm.flatMap { jvm ->
objectsFactory.propertyWithConvention(jvm.toolsJar)
}
)
fun defaultJdkToolsJarProvider(
objectsFactory: ObjectFactory,
javaVersion: Property<JavaVersion>,
jdkTools: Property<File?>
): Provider<File?> = jdkTools
.orElse(javaVersion.flatMap {
if (it < JavaVersion.VERSION_1_9) {
throw GradleException(
"Kotlin could not find the required JDK tools in the Java installation. " +
"Make sure Kotlin compilation is running on a JDK, not JRE."
)
} else {
objectsFactory.propertyWithConvention<File?>(null)
}
})
}
}
private class DefaultJdkSetter( private class DefaultJdkSetter(
private val objectsFactory: ObjectFactory, private val providedJvm: Property<Jvm>,
projectLayout: ProjectLayout,
private val currentGradleVersion: GradleVersion private val currentGradleVersion: GradleVersion
) : KotlinJavaToolchain.JdkSetter, ) : KotlinJavaToolchain.JdkSetter {
JdkProvider {
override val currentJvm: Property<Jvm> = objectsFactory
.property(Jvm.current())
.chainedFinalizeValueOnRead()
override val javaExecutable: RegularFileProperty = objectsFactory
.fileProperty()
.convention(
currentJvm.flatMap { jvm ->
projectLayout.file(
objectsFactory.property<File>(
jvm.javaExecutable
?: throw GradleException(
"Kotlin could not find 'java' executable in the JDK installation: " +
jvm.javaHome?.let { "'$it' " }.orEmpty()
)
)
)
}
)
.chainedFinalizeValueOnRead()
override val javaVersion: Property<JavaVersion> = objectsFactory
.propertyWithConvention(
currentJvm.map { jvm ->
jvm.javaVersion
?: throw GradleException(
"Kotlin could not get java version for the JDK installation: " +
jvm.javaHome?.let { "'$it' " }.orEmpty()
)
}
)
private val _jdkToolsJar: Property<File?> = JdkProvider.jdkToolsProperty(
objectsFactory,
currentJvm
)
override val jdkToolsJar: Provider<File?> = JdkProvider.defaultJdkToolsJarProvider(
objectsFactory,
javaVersion,
_jdkToolsJar
)
override fun use( override fun use(
jdkHomeLocation: File, jdkHomeLocation: File,
jdkVersion: JavaVersion jdkVersion: JavaVersion
) { ) {
if (currentGradleVersion >= TOOLCHAIN_SUPPORTED_VERSION) { require(jdkHomeLocation.isDirectory) {
throw GradleException( "Supplied jdkHomeLocation must be a valid directory. You supplied: $jdkHomeLocation"
"Please use Java toolchains instead" }
) require(jdkHomeLocation.exists()) {
"Supplied jdkHomeLocation does not exists. You supplied: $jdkHomeLocation"
} }
val jvm = Jvm.forHome(jdkHomeLocation) as Jvm if (currentGradleVersion < GradleVersion.version("6.2.0")) {
// Before Gradle 6.2.0 'Jvm.discovered' does not have 'implementationJavaVersion' parameter
javaExecutable.set(jvm.javaExecutable) val jvm = Jvm::class.functions
_jdkToolsJar.set(jvm.toolsJar) .first { it.name == "discovered" }
javaVersion.set(jdkVersion) .call(jdkHomeLocation, jdkVersion) as Jvm
providedJvm.set(jvm)
} else {
providedJvm.set(
Jvm.discovered(jdkHomeLocation, null, jdkVersion)
)
}
} }
} }
private class DefaultJavaToolchainSetter( private inner class DefaultJavaToolchainSetter(
objectsFactory: ObjectFactory private val providedJvm: Property<Jvm>
) : KotlinJavaToolchain.JavaToolchainSetter, ) : KotlinJavaToolchain.JavaToolchainSetter {
JdkProvider {
private val javaLauncher: Property<JavaLauncher> = objectsFactory.property()
override val currentJvm: Property<Jvm> = objectsFactory
.propertyWithConvention(
javaLauncher.map {
Jvm.forHome(it.metadata.installationPath.asFile) as Jvm
}
)
override val javaExecutable: RegularFileProperty = objectsFactory
.fileProperty()
.apply {
set(javaLauncher.map { it.executablePath })
}
override val javaVersion: Property<JavaVersion> = objectsFactory
.property(
javaLauncher.map {
JavaVersion.toVersion(it.metadata.languageVersion.asInt())
}
)
private val _jdkToolsJar = JdkProvider.jdkToolsProperty(
objectsFactory,
currentJvm
)
override val jdkToolsJar: Provider<File?> = JdkProvider.defaultJdkToolsJarProvider(
objectsFactory,
javaVersion,
_jdkToolsJar
)
override fun use( override fun use(
javaLauncher: Provider<JavaLauncher> javaLauncher: Provider<JavaLauncher>
) { ) {
this.javaLauncher.set(javaLauncher) providedJvm.set(
javaLauncher.map {
val metadata = javaLauncher.get().metadata
Jvm.discovered(
metadata.installationPath.asFile,
null,
JavaVersion.toVersion(metadata.languageVersion.asInt())
)
}
)
} }
} }
} }
@@ -138,7 +138,7 @@ class GradleCompileTaskProvider(task: Task) {
} }
} }
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> : AbstractKotlinCompileTool<T>(), UsesKotlinJavaToolchain { abstract class AbstractKotlinCompile<T : CommonCompilerArguments> : AbstractKotlinCompileTool<T>() {
open class Configurator<T : AbstractKotlinCompile<*>>(protected val compilation: KotlinCompilationData<*>) : TaskConfigurator<T> { open class Configurator<T : AbstractKotlinCompile<*>>(protected val compilation: KotlinCompilationData<*>) : TaskConfigurator<T> {
override fun configure(task: T) { override fun configure(task: T) {
@@ -256,40 +256,18 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> : AbstractKotl
private val kotlinLogger by lazy { GradleKotlinLogger(logger) } private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> = objects @get:Internal
.propertyWithNewInstance( protected val gradleCompileTaskProvider: Provider<GradleCompileTaskProvider> = objects
project.gradle .providerWithLazyConvention { GradleCompileTaskProvider(this) }
)
@get:Internal @get:Internal
internal val compilerRunner: Provider<GradleCompilerRunner> = internal open val compilerRunner: Provider<GradleCompilerRunner> =
objects.propertyWithConvention( objects.propertyWithConvention(
kotlinJavaToolchainProvider.map { gradleCompileTaskProvider.map {
compilerRunner( GradleCompilerRunner(it, null)
it.jdkProvider.javaExecutable.get().asFile,
it.jdkProvider.jdkToolsJar.orNull
)
} }
) )
// Moved creation here to not violate Gradle configuration cache as [compilerRunner] method is called
// at execution time
// by lazy is added so properties of task extending this one are captured - required for incremental
// compilation
@get:Internal
protected val gradleCompileTaskProvider by lazy {
GradleCompileTaskProvider(this)
}
internal open fun compilerRunner(
javaExecutable: File,
jdkToolsJar: File?
): GradleCompilerRunner = GradleCompilerRunner(
gradleCompileTaskProvider,
javaExecutable,
jdkToolsJar
)
private val systemPropertiesService = CompilerSystemPropertiesService.registerIfAbsent(project.gradle) private val systemPropertiesService = CompilerSystemPropertiesService.registerIfAbsent(project.gradle)
@TaskAction @TaskAction
@@ -403,7 +381,9 @@ internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
@CacheableTask @CacheableTask
abstract class KotlinCompile @Inject constructor( abstract class KotlinCompile @Inject constructor(
override val kotlinOptions: KotlinJvmOptions override val kotlinOptions: KotlinJvmOptions
) : AbstractKotlinCompile<K2JVMCompilerArguments>(), KotlinJvmCompile { ) : AbstractKotlinCompile<K2JVMCompilerArguments>(),
KotlinJvmCompile,
UsesKotlinJavaToolchain {
class Configurator(kotlinCompilation: KotlinCompilationData<*>) : AbstractKotlinCompile.Configurator<KotlinCompile>(kotlinCompilation) { class Configurator(kotlinCompilation: KotlinCompilationData<*>) : AbstractKotlinCompile.Configurator<KotlinCompile>(kotlinCompilation) {
} }
@@ -438,6 +418,24 @@ abstract class KotlinCompile @Inject constructor(
@get:Input @get:Input
abstract val useClasspathSnapshot: Property<Boolean> abstract val useClasspathSnapshot: Property<Boolean>
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> = objects
.propertyWithNewInstance(
project.gradle
)
@get:Internal
override val compilerRunner: Provider<GradleCompilerRunner> = objects.propertyWithConvention(
// From Gradle 6.6 better to replace flatMap with provider.zip()
kotlinJavaToolchainProvider.flatMap { toolchain ->
objects.property(gradleCompileTaskProvider.map {
GradleCompilerRunner(
it,
toolchain.currentJvmJdkToolsJar.orNull
)
})
}
)
init { init {
incremental = true incremental = true
} }
@@ -495,7 +493,8 @@ abstract class KotlinCompile @Inject constructor(
sourceRoots.javaSourceRoots, sourceRoots.javaSourceRoots,
javaPackagePrefix, javaPackagePrefix,
args, args,
environment environment,
kotlinJavaToolchainProvider.get().providedJvm.get().javaHome
) )
} }
@@ -543,52 +542,53 @@ abstract class KotlinCompile @Inject constructor(
@CacheableTask @CacheableTask
internal abstract class KotlinCompileWithWorkers @Inject constructor( internal abstract class KotlinCompileWithWorkers @Inject constructor(
kotlinOptions: KotlinJvmOptions, kotlinOptions: KotlinJvmOptions,
private val workerExecutor: WorkerExecutor workerExecutor: WorkerExecutor
) : KotlinCompile(kotlinOptions) { ) : KotlinCompile(kotlinOptions) {
override val compilerRunner: Provider<GradleCompilerRunner> =
override fun compilerRunner( objects.propertyWithConvention(
javaExecutable: File, gradleCompileTaskProvider.map {
jdkToolsJar: File? GradleCompilerRunnerWithWorkers(
) = GradleCompilerRunnerWithWorkers( it,
gradleCompileTaskProvider, null,
javaExecutable, workerExecutor
jdkToolsJar, ) as GradleCompilerRunner
workerExecutor }
) )
} }
@CacheableTask @CacheableTask
internal abstract class Kotlin2JsCompileWithWorkers @Inject constructor( internal abstract class Kotlin2JsCompileWithWorkers @Inject constructor(
kotlinOptions: KotlinJsOptions, kotlinOptions: KotlinJsOptions,
objectFactory: ObjectFactory, objectFactory: ObjectFactory,
private val workerExecutor: WorkerExecutor workerExecutor: WorkerExecutor
) : Kotlin2JsCompile(kotlinOptions, objectFactory) { ) : Kotlin2JsCompile(kotlinOptions, objectFactory) {
override val compilerRunner: Provider<GradleCompilerRunner> =
override fun compilerRunner( objects.propertyWithConvention(
javaExecutable: File, gradleCompileTaskProvider.map {
jdkToolsJar: File? GradleCompilerRunnerWithWorkers(
) = GradleCompilerRunnerWithWorkers( it,
gradleCompileTaskProvider, null,
javaExecutable, workerExecutor
jdkToolsJar, ) as GradleCompilerRunner
workerExecutor }
) )
} }
@CacheableTask @CacheableTask
internal abstract class KotlinCompileCommonWithWorkers @Inject constructor( internal abstract class KotlinCompileCommonWithWorkers @Inject constructor(
kotlinOptions: KotlinMultiplatformCommonOptions, kotlinOptions: KotlinMultiplatformCommonOptions,
private val workerExecutor: WorkerExecutor workerExecutor: WorkerExecutor
) : KotlinCompileCommon(kotlinOptions) { ) : KotlinCompileCommon(kotlinOptions) {
override fun compilerRunner( override val compilerRunner: Provider<GradleCompilerRunner> =
javaExecutable: File, objects.propertyWithConvention(
jdkToolsJar: File? gradleCompileTaskProvider.map {
) = GradleCompilerRunnerWithWorkers( GradleCompilerRunnerWithWorkers(
gradleCompileTaskProvider, it,
javaExecutable, null,
jdkToolsJar, workerExecutor
workerExecutor ) as GradleCompilerRunner
) }
)
} }
@CacheableTask @CacheableTask
@@ -601,7 +601,8 @@ abstract class Kotlin2JsCompile @Inject constructor(
incremental = true incremental = true
} }
open class Configurator<T : Kotlin2JsCompile>(compilation: KotlinCompilationData<*>) : AbstractKotlinCompile.Configurator<T>(compilation) { open class Configurator<T : Kotlin2JsCompile>(compilation: KotlinCompilationData<*>) :
AbstractKotlinCompile.Configurator<T>(compilation) {
override fun configure(task: T) { override fun configure(task: T) {
super.configure(task) super.configure(task)
@@ -52,6 +52,10 @@ internal inline fun <reified T : Any?> ObjectFactory.propertyWithConvention(
conventionValue: T conventionValue: T
) = property<T>().convention(conventionValue) ) = property<T>().convention(conventionValue)
internal inline fun <reified T : Any?> ObjectFactory.providerWithLazyConvention(
noinline lazyConventionValue: () -> T
) = property(lazyConventionValue).map { it.invoke() }
internal inline fun <reified T : Any> ObjectFactory.newInstance() = newInstance(T::class.java) internal inline fun <reified T : Any> ObjectFactory.newInstance() = newInstance(T::class.java)
internal inline fun <reified T : Any> ObjectFactory.newInstance(vararg parameters: Any) = internal inline fun <reified T : Any> ObjectFactory.newInstance(vararg parameters: Any) =