From 4c943e7cd1b92d2355f410ef91bba5106f84d7ae Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Fri, 21 May 2021 16:45:55 +0200 Subject: [PATCH] Allow to configure non-default JDK using toolchain via extension. Add method into "kotlin" extension to set toolchain for all tasks. This will also set toolchain for java compile tasks. Also user may set toolchain via "java" extension and it will be applied to Kotlin tasks as well. ^KT-43092 In Progress --- .../gradle/tasks/UsesKotlinJavaToolchain.kt | 5 + .../kotlin/gradle/KotlinJavaToolchainTest.kt | 133 ++++++++-- .../gradle/dsl/KotlinProjectExtension.kt | 14 +- .../kotlin/gradle/dsl/ToolchainDsl.kt | 99 ++++++++ .../kotlin/gradle/internal/kapt/KaptTask.kt | 7 +- .../internal/kapt/KaptWithKotlincTask.kt | 4 +- .../internal/kapt/KaptWithoutKotlincTask.kt | 18 +- .../tasks/KotlinJavaToolchainProvider.kt | 230 ++++++++++++------ .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 10 +- .../kotlin/gradle/utils/providerApiUtils.kt | 7 +- 10 files changed, 427 insertions(+), 100 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/ToolchainDsl.kt 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())