diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UsesKotlinJavaToolchain.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UsesKotlinJavaToolchain.kt index fcb37a5603f..fe8fe968bc2 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UsesKotlinJavaToolchain.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UsesKotlinJavaToolchain.kt @@ -11,6 +11,7 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.util.GradleVersion import java.io.File interface UsesKotlinJavaToolchain { @@ -66,4 +67,8 @@ interface KotlinJavaToolchain { javaLauncher: Provider ) } + + companion object { + val TOOLCHAIN_SUPPORTED_VERSION: GradleVersion = GradleVersion.version("6.7") + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinJavaToolchainTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinJavaToolchainTest.kt index a5965ce7466..4b88b7b7c28 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinJavaToolchainTest.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinJavaToolchainTest.kt @@ -12,12 +12,14 @@ import org.gradle.testkit.runner.BuildResult import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import java.io.File @SimpleGradlePluginPTests @DisplayName("Kotlin Java Toolchain support") class KotlinJavaToolchainTest : KGPBaseTest() { + @GradleTestVersions(additionalVersions = ["6.7.1"]) @GradleTest @DisplayName("Should use by default same jvm as Gradle daemon") internal fun byDefaultShouldUseGradleJDK(gradleVersion: GradleVersion) { @@ -31,6 +33,28 @@ class KotlinJavaToolchainTest : KGPBaseTest() { } } + @GradleTestVersions(minVersion = "6.7.1") + @GradleTest + @DisplayName("Default Kotlin toolchain should still allow to set Java source and target compatibility") + internal fun shouldNotFailWithDefaultJdkAndCompatibility(gradleVersion: GradleVersion) { + project( + projectName = "simple".fullProjectName, + gradleVersion = gradleVersion + ) { + //lang=Groovy + rootBuildGradle.append( + """ + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + """.trimIndent() + ) + + build("assemble") + } + } + + @GradleTestVersions(maxVersion = "6.6.1") @GradleTest @DisplayName("Should use provided jdk location to compile Kotlin sources") internal fun customJdkHomeLocation(gradleVersion: GradleVersion) { @@ -46,6 +70,18 @@ class KotlinJavaToolchainTest : KGPBaseTest() { } } + @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 @DisplayName("KotlinCompile task should use build cache when using provided JDK") internal fun customJdkBuildCache(gradleVersion: GradleVersion) { @@ -57,7 +93,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() { buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true) ) { enableLocalBuildCache(buildCache) - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } build("assemble") } @@ -69,7 +109,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() { buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true) ) { enableLocalBuildCache(buildCache) - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } build("assemble") { assertTasksFromCache(":compileKotlin") @@ -90,7 +134,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() { ) { enableLocalBuildCache(buildCache) enableBuildCacheDebug() - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } build("assemble") } @@ -116,13 +164,23 @@ class KotlinJavaToolchainTest : KGPBaseTest() { projectName = "simpleWithKapt".fullProjectName, gradleVersion = gradleVersion, ) { - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } gradleProperties.append( "kapt.workers.isolation = none" ) build("assemble") { - assertDaemonIsUsingJdk(getJdk9().javaExecutableRealPath) + assertDaemonIsUsingJdk( + if (shouldUseToolchain(gradleVersion)) { + getToolchainExecPathFromLogs() + } else { + getJdk9().javaExecutableRealPath + } + ) assertOutputContains("Using workers PROCESS isolation mode to run kapt") assertOutputContains("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!") @@ -155,7 +213,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() { buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true) ) { enableLocalBuildCache(buildCache) - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } build("assemble") } @@ -167,7 +229,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() { buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true), ) { enableLocalBuildCache(buildCache) - useJdk9ToCompile() + if (shouldUseToolchain(gradleVersion)) { + useToolchainExtension(11) + } else { + useJdk9ToCompile() + } build("assemble") { assertTasksFromCache( @@ -244,7 +310,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() { } @DisplayName("Should allow to set JDK version for tasks via Java toolchain") - @GradleTestVersions(minVersion = "6.7") + @GradleTestVersions(minVersion = "6.7.1") @GradleTest internal fun setJdkUsingJavaToolchain(gradleVersion: GradleVersion) { project( @@ -259,6 +325,22 @@ class KotlinJavaToolchainTest : KGPBaseTest() { } } + @DisplayName("Should allow to set Java toolchain via extension") + @GradleTestVersions(minVersion = "6.7.1") + @GradleTest + internal fun setJdkUsingJavaToolchainViaExtension(gradleVersion: GradleVersion) { + project( + projectName = "simple".fullProjectName, + gradleVersion = gradleVersion, + forceOutput = true + ) { + useToolchainExtension(11) + build("assemble") { + assertDaemonIsUsingJdk(getToolchainExecPathFromLogs()) + } + } + } + private fun BuildResult.assertDaemonIsUsingJdk( javaexecPath: String ) = assertOutputContains("i: connected to the daemon. Daemon is using following 'java' executable to run itself: $javaexecPath") @@ -303,13 +385,8 @@ class KotlinJavaToolchainTest : KGPBaseTest() { """ import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of($jdkVersion)) - } - } - def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain + toolchain.languageVersion.set(JavaLanguageVersion.of($jdkVersion)) def service = project.extensions.getByType(JavaToolchainService.class) def defaultLauncher = service.launcherFor(toolchain) @@ -328,6 +405,34 @@ class KotlinJavaToolchainTest : KGPBaseTest() { ) } + private fun TestProject.useToolchainExtension( + jdkVersion: Int + ) { + //language=Groovy + rootBuildGradle.append( + """ + import org.gradle.api.plugins.JavaPluginExtension + import org.gradle.jvm.toolchain.JavaLanguageVersion + import org.gradle.jvm.toolchain.JavaToolchainService + + kotlin { + toolchain { + languageVersion.set(JavaLanguageVersion.of($jdkVersion)) + } + } + + afterEvaluate { + def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain + def service = project.extensions.getByType(JavaToolchainService.class) + def defaultLauncher = service.launcherFor(toolchain) + logger.info("Toolchain exec path: ${'$'}{defaultLauncher.get().executablePath.asFile.absolutePath}") + } + """.trimIndent() + ) + } + + private fun shouldUseToolchain(gradleVersion: GradleVersion) = gradleVersion >= GradleVersion.version("6.7") + private fun BuildResult.getToolchainExecPathFromLogs() = output .lineSequence() .first { it.startsWith("Toolchain exec path:") } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index c3c8b394424..2b0ec625ca3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -6,9 +6,11 @@ package org.jetbrains.kotlin.gradle.dsl import groovy.lang.Closure +import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.internal.plugins.DslObject +import org.gradle.jvm.toolchain.JavaToolchainSpec import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget @@ -53,13 +55,23 @@ internal val Project.multiplatformExtension: KotlinMultiplatformExtension internal val Project.pm20Extension: KotlinPm20ProjectExtension get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinPm20ProjectExtension - open class KotlinTopLevelExtension (internal val project: Project) { val experimental: ExperimentalExtension get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java) lateinit var coreLibrariesVersion: String + private val toolchainSupport = ToolchainSupport.createToolchain(project) + + /** + * Configures [Java toolchain](https://docs.gradle.org/current/userguide/toolchains.html) both for Kotlin and Java tasks. + * + * @param action - action to configure [JavaToolchainSpec]. You could safely cast `Any` into `JavaToolchainSpec`. + */ + fun toolchain(action: Action) { + toolchainSupport.applyToolchain(action) + } + var explicitApi: ExplicitApiMode? = null fun explicitApi() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/ToolchainDsl.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/ToolchainDsl.kt new file mode 100644 index 00000000000..fd1e2e221d6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/ToolchainDsl.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.dsl + +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.internal.jvm.Jvm +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.jvm.toolchain.JavaToolchainSpec +import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinJavaToolchain.Companion.TOOLCHAIN_SUPPORTED_VERSION +import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain +import org.jetbrains.kotlin.gradle.tasks.withType +import org.jetbrains.kotlin.gradle.utils.newInstance +import javax.inject.Inject + +internal interface ToolchainSupport { + fun applyToolchain(action: Action) + + companion object { + internal fun createToolchain( + project: Project + ): ToolchainSupport { + val currentVersion = GradleVersion.version(project.gradle.gradleVersion) + return when { + currentVersion < TOOLCHAIN_SUPPORTED_VERSION -> project.objects.newInstance() + else -> project.objects.newInstance(project.extensions).also { + configureDefaultToolchain(project) + } + } + } + + private fun configureDefaultToolchain(project: Project) { + project.plugins.withId("org.gradle.java-base") { + project + .tasks + .withType() + .configureEach { + val javaToolchainService = project + .extensions + .findByType(JavaToolchainService::class.java)!! + val javaToolchainSpec = project + .extensions + .getByType(JavaPluginExtension::class.java) + .toolchain + + val toolchainSpec = if (!javaToolchainSpec.languageVersion.isPresent) { + // We can't reuse java toolchain and just set 'languageVersion' to it + // as build will fail when java source and/or target compatibility is set + project.objects.newInstance().apply { + languageVersion.set( + JavaLanguageVersion.of( + Jvm.current().javaVersion!!.majorVersion + ) + ) + } + } else { + javaToolchainSpec + } + + it.kotlinJavaToolchain.toolchain.use( + javaToolchainService.launcherFor(toolchainSpec) + ) + } + } + } + } +} + +internal abstract class NonExistingToolchainSupport : ToolchainSupport { + override fun applyToolchain( + action: Action + ) { + throw GradleException("JavaToolchain support is only available from Gradle 6.7") + } +} + +internal abstract class DefaultToolchainSupport @Inject constructor( + private val extensions: ExtensionContainer +) : ToolchainSupport { + private val toolchainSpec: JavaToolchainSpec + get() = extensions + .getByType(JavaPluginExtension::class.java) + .toolchain + + override fun applyToolchain( + action: Action + ) { + action.execute(toolchainSpec) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt index ece818ebc6c..1401f84fbf8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt @@ -7,7 +7,6 @@ import org.gradle.api.internal.ConventionTask import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.ProviderFactory import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.api.tasks.incremental.IncrementalTaskInputs @@ -121,8 +120,10 @@ abstract class KaptTask @Inject constructor( @get:Internal abstract val classpath: ConfigurableFileCollection - final override val kotlinJavaToolchainProvider: Provider = - objectFactory.propertyWithNewInstance() + final override val kotlinJavaToolchainProvider: Provider = objectFactory + .propertyWithNewInstance( + project.gradle + ) @Suppress("unused", "DeprecatedCallableAddReplaceWith") @Deprecated( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt index 7e3ea01938a..ed3f7bbd484 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt @@ -114,8 +114,8 @@ abstract class KaptWithKotlincTask @Inject constructor( val compilerRunner = GradleCompilerRunner( taskProvider, - kotlinJavaToolchainProvider.get().javaExecutable.get().asFile, - kotlinJavaToolchainProvider.get().jdkToolsJar.orNull + kotlinJavaToolchainProvider.get().jdkProvider.javaExecutable.get().asFile, + kotlinJavaToolchainProvider.get().jdkProvider.jdkToolsJar.orNull ) compilerRunner.runJvmCompilerAsync( sourcesToCompile = emptyList(), diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt index fd1126dcd77..5b970a3fee9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt @@ -160,7 +160,9 @@ abstract class KaptWithoutKotlincTask @Inject constructor( val kaptClasspath = kaptJars.toList() + kotlinStdlibClasspath val isolationMode = getWorkerIsolationMode() logger.info("Using workers $isolationMode isolation mode to run kapt") - val toolsJarURLSpec = kotlinJavaToolchainProvider.get().jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty() + val toolsJarURLSpec = kotlinJavaToolchainProvider.get() + .jdkProvider + .jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty() submitWork( isolationMode, @@ -171,12 +173,12 @@ abstract class KaptWithoutKotlincTask @Inject constructor( } private fun getWorkerIsolationMode(): IsolationMode { - val kotlinJavaToolchain = kotlinJavaToolchainProvider.get() - val gradleJvm = kotlinJavaToolchain.currentJvm.get() + val jdkProvider = kotlinJavaToolchainProvider.get().jdkProvider + val gradleJvm = jdkProvider.currentJvm.get() // 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 - val isRunningOnGradleJvm = gradleJvm.javaVersion == kotlinJavaToolchain.javaVersion.get() && - gradleJvm.javaExecutable.absolutePath == kotlinJavaToolchain.javaExecutable.get().asFile.absolutePath + val isRunningOnGradleJvm = gradleJvm.javaVersion == jdkProvider.javaVersion.get() && + gradleJvm.javaExecutable.absolutePath == jdkProvider.javaExecutable.get().asFile.absolutePath val isolationModeStr = getValue("kapt.workers.isolation")?.toLowerCase() return when { (isolationModeStr == null || isolationModeStr == "none") && isRunningOnGradleJvm -> IsolationMode.NONE @@ -201,7 +203,11 @@ abstract class KaptWithoutKotlincTask @Inject constructor( // for tests it.forkOptions.jvmArgs("-verbose:class") } - it.forkOptions.executable = kotlinJavaToolchainProvider.get().javaExecutable.asFile.get().absolutePath + it.forkOptions.executable = kotlinJavaToolchainProvider.get() + .jdkProvider + .javaExecutable + .asFile.get() + .absolutePath logger.info("Kapt worker classpath: ${it.classpath}") } IsolationMode.NONE -> workerExecutor.noIsolation() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJavaToolchainProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJavaToolchainProvider.kt index adcf3e874b2..dbe8599cda7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJavaToolchainProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinJavaToolchainProvider.kt @@ -9,12 +9,15 @@ import org.gradle.api.GradleException import org.gradle.api.JavaVersion import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFileProperty +import org.gradle.api.invocation.Gradle import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal import org.gradle.internal.jvm.Jvm -import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.jvm.toolchain.* +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinJavaToolchain.Companion.TOOLCHAIN_SUPPORTED_VERSION import org.jetbrains.kotlin.gradle.utils.chainedFinalizeValueOnRead import org.jetbrains.kotlin.gradle.utils.property import org.jetbrains.kotlin.gradle.utils.propertyWithConvention @@ -23,98 +26,187 @@ import javax.inject.Inject abstract class KotlinJavaToolchainProvider @Inject constructor( objects: ObjectFactory, - files: ProjectLayout + projectLayout: ProjectLayout, + gradle: Gradle ) : KotlinJavaToolchain { - @get:Internal - internal val currentJvm: Property = objects - .property(Jvm.current()) - .chainedFinalizeValueOnRead() - private val _javaVersion: Property = objects - .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 defaultJdkSetter by lazy(LazyThreadSafetyMode.NONE) { + DefaultJdkSetter( + objects, + projectLayout, + GradleVersion.version(gradle.gradleVersion) ) + } + + private val defaultJavaToolchainSetter by lazy(LazyThreadSafetyMode.NONE) { + if (GradleVersion.version(gradle.gradleVersion) >= TOOLCHAIN_SUPPORTED_VERSION) { + DefaultJavaToolchainSetter(objects) + } else { + null + } + } + + @get:Internal + internal val jdkProvider: JdkProvider = + if (GradleVersion.version(gradle.gradleVersion) < TOOLCHAIN_SUPPORTED_VERSION) { + defaultJdkSetter + } else { + defaultJavaToolchainSetter!! + } final override val javaVersion: Provider - get() = _javaVersion + get() = jdkProvider.javaVersion - @get:Internal - internal val javaExecutable: RegularFileProperty = objects - .fileProperty() - .convention( - currentJvm.flatMap { jvm -> - files.file( - objects.property( - jvm.javaExecutable - ?: throw GradleException( - "Kotlin could not find 'java' executable in the JDK installation: " + - jvm.javaHome?.let { "'$it' " }.orEmpty() - ) + final override val jdk: KotlinJavaToolchain.JdkSetter get() = defaultJdkSetter + + final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter + get() = defaultJavaToolchainSetter + ?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION") + + internal interface JdkProvider { + val currentJvm: Property + val javaExecutable: RegularFileProperty + val javaVersion: Property + val jdkToolsJar: Provider + + companion object { + fun jdkToolsProperty( + objectsFactory: ObjectFactory, + currentJvm: Provider + ): Property = objectsFactory + .propertyWithConvention( + currentJvm.flatMap { jvm -> + objectsFactory.propertyWithConvention(jvm.toolsJar) + } + ) + + fun defaultJdkToolsJarProvider( + objectsFactory: ObjectFactory, + javaVersion: Property, + jdkTools: Property + ): Provider = 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(null) + } + }) + } + } + + private class DefaultJdkSetter( + private val objectsFactory: ObjectFactory, + projectLayout: ProjectLayout, + private val currentGradleVersion: GradleVersion + ) : KotlinJavaToolchain.JdkSetter, + JdkProvider { + + override val currentJvm: Property = objectsFactory + .property(Jvm.current()) + .chainedFinalizeValueOnRead() + + override val javaExecutable: RegularFileProperty = objectsFactory + .fileProperty() + .convention( + currentJvm.flatMap { jvm -> + projectLayout.file( + objectsFactory.property( + jvm.javaExecutable + ?: throw GradleException( + "Kotlin could not find 'java' executable in the JDK installation: " + + jvm.javaHome?.let { "'$it' " }.orEmpty() + ) + ) ) - ) - } + } + ) + .chainedFinalizeValueOnRead() + + override val javaVersion: Property = 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 = JdkProvider.jdkToolsProperty( + objectsFactory, + currentJvm ) - .chainedFinalizeValueOnRead() - - private val _jdkToolsJar = objects - .propertyWithConvention( - currentJvm.flatMap { jvm -> - objects.propertyWithConvention(jvm.toolsJar) - } + override val jdkToolsJar: Provider = JdkProvider.defaultJdkToolsJarProvider( + objectsFactory, + javaVersion, + _jdkToolsJar ) - .chainedFinalizeValueOnRead() - @get:Internal - internal val jdkToolsJar: Provider = _jdkToolsJar - .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." - ) - } - - objects.propertyWithConvention(null) - }) - - override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter() - - override val toolchain: KotlinJavaToolchain.JavaToolchainSetter = DefaultJavaToolchainSetter() - - private inner class DefaultJdkSetter : KotlinJavaToolchain.JdkSetter { override fun use( jdkHomeLocation: File, jdkVersion: JavaVersion ) { + if (currentGradleVersion >= TOOLCHAIN_SUPPORTED_VERSION) { + throw GradleException( + "Please use Java toolchains instead" + ) + } + val jvm = Jvm.forHome(jdkHomeLocation) as Jvm javaExecutable.set(jvm.javaExecutable) _jdkToolsJar.set(jvm.toolsJar) - _javaVersion.set(jdkVersion) + javaVersion.set(jdkVersion) } } - private inner class DefaultJavaToolchainSetter : KotlinJavaToolchain.JavaToolchainSetter { + private class DefaultJavaToolchainSetter( + objectsFactory: ObjectFactory + ) : KotlinJavaToolchain.JavaToolchainSetter, + JdkProvider { + + private val javaLauncher: Property = objectsFactory.property() + + override val currentJvm: Property = 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 = objectsFactory + .property( + javaLauncher.map { + JavaVersion.toVersion(it.metadata.languageVersion.asInt()) + } + ) + + private val _jdkToolsJar = JdkProvider.jdkToolsProperty( + objectsFactory, + currentJvm + ) + + override val jdkToolsJar: Provider = JdkProvider.defaultJdkToolsJarProvider( + objectsFactory, + javaVersion, + _jdkToolsJar + ) + override fun use( javaLauncher: Provider ) { - javaExecutable.set(javaLauncher.map { it.executablePath }) - _javaVersion.set( - javaLauncher.map { - JavaVersion.toVersion(it.metadata.languageVersion) - } - ) - _jdkToolsJar.set( - javaLauncher.map { - Jvm.forHome(it.metadata.installationPath.asFile).toolsJar - } - ) + this.javaLauncher.set(javaLauncher) } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 76d5ade54a8..3b783a8ccf1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -245,16 +245,18 @@ abstract class AbstractKotlinCompile : AbstractKotl private val kotlinLogger by lazy { GradleKotlinLogger(logger) } - final override val kotlinJavaToolchainProvider: Provider = - objects.propertyWithNewInstance() + final override val kotlinJavaToolchainProvider: Provider = objects + .propertyWithNewInstance( + project.gradle + ) @get:Internal internal val compilerRunner: Provider = objects.propertyWithConvention( kotlinJavaToolchainProvider.map { compilerRunner( - it.javaExecutable.get().asFile, - it.jdkToolsJar.orNull + it.jdkProvider.javaExecutable.get().asFile, + it.jdkProvider.jdkToolsJar.orNull ) } ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt index dc79725e1eb..2c9030050bb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt @@ -42,6 +42,8 @@ internal inline fun ObjectFactory.property() = property(T::cl internal inline fun ObjectFactory.property(initialValue: T) = property().value(initialValue) +internal inline fun ObjectFactory.property(initialValue: Provider) = property().value(initialValue) + internal inline fun ObjectFactory.propertyWithConvention( conventionValue: Provider ) = property().convention(conventionValue) @@ -52,9 +54,12 @@ internal inline fun ObjectFactory.propertyWithConvention( internal inline fun ObjectFactory.newInstance() = newInstance(T::class.java) +internal inline fun ObjectFactory.newInstance(vararg parameters: Any) = + newInstance(T::class.java, *parameters) + internal inline fun ObjectFactory.propertyWithNewInstance( vararg parameters: Any -) = propertyWithConvention(newInstance(T::class.java, parameters)) +) = propertyWithConvention(newInstance(T::class.java, *parameters)) internal inline fun ObjectFactory.propertyWithNewInstance() = propertyWithConvention(newInstance())