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
This commit is contained in:
Yahor Berdnikau
2021-05-21 16:45:55 +02:00
committed by Space
parent 8e1c420c9f
commit 4c943e7cd1
10 changed files with 427 additions and 100 deletions
@@ -11,6 +11,7 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Nested
import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.util.GradleVersion
import java.io.File import java.io.File
interface UsesKotlinJavaToolchain { interface UsesKotlinJavaToolchain {
@@ -66,4 +67,8 @@ interface KotlinJavaToolchain {
javaLauncher: Provider<JavaLauncher> javaLauncher: Provider<JavaLauncher>
) )
} }
companion object {
val TOOLCHAIN_SUPPORTED_VERSION: GradleVersion = GradleVersion.version("6.7")
}
} }
@@ -12,12 +12,14 @@ import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.io.File import java.io.File
@SimpleGradlePluginPTests @SimpleGradlePluginPTests
@DisplayName("Kotlin Java Toolchain support") @DisplayName("Kotlin Java Toolchain support")
class KotlinJavaToolchainTest : KGPBaseTest() { class KotlinJavaToolchainTest : KGPBaseTest() {
@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")
internal fun byDefaultShouldUseGradleJDK(gradleVersion: GradleVersion) { 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 @GradleTest
@DisplayName("Should use provided jdk location to compile Kotlin sources") @DisplayName("Should use provided jdk location to compile Kotlin sources")
internal fun customJdkHomeLocation(gradleVersion: GradleVersion) { 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 @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) {
@@ -57,7 +93,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true) buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)
) { ) {
enableLocalBuildCache(buildCache) enableLocalBuildCache(buildCache)
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
build("assemble") build("assemble")
} }
@@ -69,7 +109,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true) buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)
) { ) {
enableLocalBuildCache(buildCache) enableLocalBuildCache(buildCache)
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
build("assemble") { build("assemble") {
assertTasksFromCache(":compileKotlin") assertTasksFromCache(":compileKotlin")
@@ -90,7 +134,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
) { ) {
enableLocalBuildCache(buildCache) enableLocalBuildCache(buildCache)
enableBuildCacheDebug() enableBuildCacheDebug()
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
build("assemble") build("assemble")
} }
@@ -116,13 +164,23 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
projectName = "simpleWithKapt".fullProjectName, projectName = "simpleWithKapt".fullProjectName,
gradleVersion = gradleVersion, gradleVersion = gradleVersion,
) { ) {
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
gradleProperties.append( gradleProperties.append(
"kapt.workers.isolation = none" "kapt.workers.isolation = none"
) )
build("assemble") { build("assemble") {
assertDaemonIsUsingJdk(getJdk9().javaExecutableRealPath) assertDaemonIsUsingJdk(
if (shouldUseToolchain(gradleVersion)) {
getToolchainExecPathFromLogs()
} else {
getJdk9().javaExecutableRealPath
}
)
assertOutputContains("Using workers PROCESS isolation mode to run kapt") assertOutputContains("Using workers PROCESS isolation mode to run kapt")
assertOutputContains("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!") 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) buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)
) { ) {
enableLocalBuildCache(buildCache) enableLocalBuildCache(buildCache)
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
build("assemble") build("assemble")
} }
@@ -167,7 +229,11 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true), buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true),
) { ) {
enableLocalBuildCache(buildCache) enableLocalBuildCache(buildCache)
useJdk9ToCompile() if (shouldUseToolchain(gradleVersion)) {
useToolchainExtension(11)
} else {
useJdk9ToCompile()
}
build("assemble") { build("assemble") {
assertTasksFromCache( assertTasksFromCache(
@@ -244,7 +310,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
} }
@DisplayName("Should allow to set JDK version for tasks via Java toolchain") @DisplayName("Should allow to set JDK version for tasks via Java toolchain")
@GradleTestVersions(minVersion = "6.7") @GradleTestVersions(minVersion = "6.7.1")
@GradleTest @GradleTest
internal fun setJdkUsingJavaToolchain(gradleVersion: GradleVersion) { internal fun setJdkUsingJavaToolchain(gradleVersion: GradleVersion) {
project( 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( private fun BuildResult.assertDaemonIsUsingJdk(
javaexecPath: String javaexecPath: String
) = assertOutputContains("i: connected to the daemon. Daemon is using following 'java' executable to run itself: $javaexecPath") ) = 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 import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of($jdkVersion))
}
}
def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain
toolchain.languageVersion.set(JavaLanguageVersion.of($jdkVersion))
def service = project.extensions.getByType(JavaToolchainService.class) def service = project.extensions.getByType(JavaToolchainService.class)
def defaultLauncher = service.launcherFor(toolchain) 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 private fun BuildResult.getToolchainExecPathFromLogs() = output
.lineSequence() .lineSequence()
.first { it.startsWith("Toolchain exec path:") } .first { it.startsWith("Toolchain exec path:") }
@@ -6,9 +6,11 @@
package org.jetbrains.kotlin.gradle.dsl package org.jetbrains.kotlin.gradle.dsl
import groovy.lang.Closure import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.plugins.DslObject import org.gradle.api.internal.plugins.DslObject
import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
@@ -53,13 +55,23 @@ internal val Project.multiplatformExtension: KotlinMultiplatformExtension
internal val Project.pm20Extension: KotlinPm20ProjectExtension internal val Project.pm20Extension: KotlinPm20ProjectExtension
get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinPm20ProjectExtension get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinPm20ProjectExtension
open class KotlinTopLevelExtension (internal val project: Project) { open class KotlinTopLevelExtension (internal val project: Project) {
val experimental: ExperimentalExtension val experimental: ExperimentalExtension
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java) get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)
lateinit var coreLibrariesVersion: String 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<Any>) {
toolchainSupport.applyToolchain(action)
}
var explicitApi: ExplicitApiMode? = null var explicitApi: ExplicitApiMode? = null
fun explicitApi() { fun explicitApi() {
@@ -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<Any>)
companion object {
internal fun createToolchain(
project: Project
): ToolchainSupport {
val currentVersion = GradleVersion.version(project.gradle.gradleVersion)
return when {
currentVersion < TOOLCHAIN_SUPPORTED_VERSION -> project.objects.newInstance<NonExistingToolchainSupport>()
else -> project.objects.newInstance<DefaultToolchainSupport>(project.extensions).also {
configureDefaultToolchain(project)
}
}
}
private fun configureDefaultToolchain(project: Project) {
project.plugins.withId("org.gradle.java-base") {
project
.tasks
.withType<UsesKotlinJavaToolchain>()
.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<DefaultToolchainSpec>().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<Any>
) {
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<Any>
) {
action.execute(toolchainSpec)
}
}
@@ -7,7 +7,6 @@ import org.gradle.api.internal.ConventionTask
import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.provider.Provider 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
@@ -121,8 +120,10 @@ abstract class KaptTask @Inject constructor(
@get:Internal @get:Internal
abstract val classpath: ConfigurableFileCollection abstract val classpath: ConfigurableFileCollection
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> = final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> = objectFactory
objectFactory.propertyWithNewInstance() .propertyWithNewInstance(
project.gradle
)
@Suppress("unused", "DeprecatedCallableAddReplaceWith") @Suppress("unused", "DeprecatedCallableAddReplaceWith")
@Deprecated( @Deprecated(
@@ -114,8 +114,8 @@ abstract class KaptWithKotlincTask @Inject constructor(
val compilerRunner = GradleCompilerRunner( val compilerRunner = GradleCompilerRunner(
taskProvider, taskProvider,
kotlinJavaToolchainProvider.get().javaExecutable.get().asFile, kotlinJavaToolchainProvider.get().jdkProvider.javaExecutable.get().asFile,
kotlinJavaToolchainProvider.get().jdkToolsJar.orNull kotlinJavaToolchainProvider.get().jdkProvider.jdkToolsJar.orNull
) )
compilerRunner.runJvmCompilerAsync( compilerRunner.runJvmCompilerAsync(
sourcesToCompile = emptyList(), sourcesToCompile = emptyList(),
@@ -160,7 +160,9 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
val kaptClasspath = kaptJars.toList() + kotlinStdlibClasspath val kaptClasspath = kaptJars.toList() + kotlinStdlibClasspath
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().jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty() val toolsJarURLSpec = kotlinJavaToolchainProvider.get()
.jdkProvider
.jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty()
submitWork( submitWork(
isolationMode, isolationMode,
@@ -171,12 +173,12 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
} }
private fun getWorkerIsolationMode(): IsolationMode { private fun getWorkerIsolationMode(): IsolationMode {
val kotlinJavaToolchain = kotlinJavaToolchainProvider.get() val jdkProvider = kotlinJavaToolchainProvider.get().jdkProvider
val gradleJvm = kotlinJavaToolchain.currentJvm.get() val gradleJvm = jdkProvider.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 == kotlinJavaToolchain.javaVersion.get() && val isRunningOnGradleJvm = gradleJvm.javaVersion == jdkProvider.javaVersion.get() &&
gradleJvm.javaExecutable.absolutePath == kotlinJavaToolchain.javaExecutable.get().asFile.absolutePath gradleJvm.javaExecutable.absolutePath == jdkProvider.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 +203,11 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
// for tests // for tests
it.forkOptions.jvmArgs("-verbose:class") 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}") logger.info("Kapt worker classpath: ${it.classpath}")
} }
IsolationMode.NONE -> workerExecutor.noIsolation() IsolationMode.NONE -> workerExecutor.noIsolation()
@@ -9,12 +9,15 @@ import org.gradle.api.GradleException
import org.gradle.api.JavaVersion import org.gradle.api.JavaVersion
import org.gradle.api.file.ProjectLayout import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty import org.gradle.api.file.RegularFileProperty
import org.gradle.api.invocation.Gradle
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.provider.Provider
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Internal
import org.gradle.internal.jvm.Jvm 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.chainedFinalizeValueOnRead
import org.jetbrains.kotlin.gradle.utils.property import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.gradle.utils.propertyWithConvention import org.jetbrains.kotlin.gradle.utils.propertyWithConvention
@@ -23,98 +26,187 @@ import javax.inject.Inject
abstract class KotlinJavaToolchainProvider @Inject constructor( abstract class KotlinJavaToolchainProvider @Inject constructor(
objects: ObjectFactory, objects: ObjectFactory,
files: ProjectLayout projectLayout: ProjectLayout,
gradle: Gradle
) : KotlinJavaToolchain { ) : KotlinJavaToolchain {
@get:Internal
internal val currentJvm: Property<Jvm> = objects
.property<Jvm>(Jvm.current())
.chainedFinalizeValueOnRead()
private val _javaVersion: Property<JavaVersion> = objects private val defaultJdkSetter by lazy(LazyThreadSafetyMode.NONE) {
.propertyWithConvention( DefaultJdkSetter(
currentJvm.map { jvm -> objects,
jvm.javaVersion projectLayout,
?: throw GradleException( GradleVersion.version(gradle.gradleVersion)
"Kotlin could not get java version for the JDK installation: " +
jvm.javaHome?.let { "'$it' " }.orEmpty()
)
}
) )
}
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<JavaVersion> final override val javaVersion: Provider<JavaVersion>
get() = _javaVersion get() = jdkProvider.javaVersion
@get:Internal final override val jdk: KotlinJavaToolchain.JdkSetter get() = defaultJdkSetter
internal val javaExecutable: RegularFileProperty = objects
.fileProperty() final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter
.convention( get() = defaultJavaToolchainSetter
currentJvm.flatMap { jvm -> ?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION")
files.file(
objects.property<File>( internal interface JdkProvider {
jvm.javaExecutable val currentJvm: Property<Jvm>
?: throw GradleException( val javaExecutable: RegularFileProperty
"Kotlin could not find 'java' executable in the JDK installation: " + val javaVersion: Property<JavaVersion>
jvm.javaHome?.let { "'$it' " }.orEmpty() 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 val objectsFactory: ObjectFactory,
projectLayout: ProjectLayout,
private val currentGradleVersion: GradleVersion
) : 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
) )
.chainedFinalizeValueOnRead() override val jdkToolsJar: Provider<File?> = JdkProvider.defaultJdkToolsJarProvider(
objectsFactory,
private val _jdkToolsJar = objects javaVersion,
.propertyWithConvention( _jdkToolsJar
currentJvm.flatMap { jvm ->
objects.propertyWithConvention(jvm.toolsJar)
}
) )
.chainedFinalizeValueOnRead()
@get:Internal
internal val jdkToolsJar: Provider<File?> = _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<File?>(null)
})
override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter()
override val toolchain: KotlinJavaToolchain.JavaToolchainSetter = DefaultJavaToolchainSetter()
private inner class DefaultJdkSetter : KotlinJavaToolchain.JdkSetter {
override fun use( override fun use(
jdkHomeLocation: File, jdkHomeLocation: File,
jdkVersion: JavaVersion jdkVersion: JavaVersion
) { ) {
if (currentGradleVersion >= TOOLCHAIN_SUPPORTED_VERSION) {
throw GradleException(
"Please use Java toolchains instead"
)
}
val jvm = Jvm.forHome(jdkHomeLocation) as Jvm val jvm = Jvm.forHome(jdkHomeLocation) as Jvm
javaExecutable.set(jvm.javaExecutable) javaExecutable.set(jvm.javaExecutable)
_jdkToolsJar.set(jvm.toolsJar) _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<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>
) { ) {
javaExecutable.set(javaLauncher.map { it.executablePath }) this.javaLauncher.set(javaLauncher)
_javaVersion.set(
javaLauncher.map {
JavaVersion.toVersion(it.metadata.languageVersion)
}
)
_jdkToolsJar.set(
javaLauncher.map {
Jvm.forHome(it.metadata.installationPath.asFile).toolsJar
}
)
} }
} }
} }
@@ -245,16 +245,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> = final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> = objects
objects.propertyWithNewInstance() .propertyWithNewInstance(
project.gradle
)
@get:Internal @get:Internal
internal val compilerRunner: Provider<GradleCompilerRunner> = internal val compilerRunner: Provider<GradleCompilerRunner> =
objects.propertyWithConvention( objects.propertyWithConvention(
kotlinJavaToolchainProvider.map { kotlinJavaToolchainProvider.map {
compilerRunner( compilerRunner(
it.javaExecutable.get().asFile, it.jdkProvider.javaExecutable.get().asFile,
it.jdkToolsJar.orNull it.jdkProvider.jdkToolsJar.orNull
) )
} }
) )
@@ -42,6 +42,8 @@ internal inline fun <reified T : Any?> ObjectFactory.property() = property(T::cl
internal inline fun <reified T : Any?> ObjectFactory.property(initialValue: T) = property<T>().value(initialValue) internal inline fun <reified T : Any?> ObjectFactory.property(initialValue: T) = property<T>().value(initialValue)
internal inline fun <reified T : Any?> ObjectFactory.property(initialValue: Provider<T>) = property<T>().value(initialValue)
internal inline fun <reified T : Any?> ObjectFactory.propertyWithConvention( internal inline fun <reified T : Any?> ObjectFactory.propertyWithConvention(
conventionValue: Provider<T> conventionValue: Provider<T>
) = property<T>().convention(conventionValue) ) = property<T>().convention(conventionValue)
@@ -52,9 +54,12 @@ internal inline fun <reified T : Any?> ObjectFactory.propertyWithConvention(
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) =
newInstance(T::class.java, *parameters)
internal inline fun <reified T : Any> ObjectFactory.propertyWithNewInstance( internal inline fun <reified T : Any> ObjectFactory.propertyWithNewInstance(
vararg parameters: Any vararg parameters: Any
) = propertyWithConvention(newInstance(T::class.java, parameters)) ) = propertyWithConvention(newInstance(T::class.java, *parameters))
internal inline fun <reified T : Any> ObjectFactory.propertyWithNewInstance() = internal inline fun <reified T : Any> ObjectFactory.propertyWithNewInstance() =
propertyWithConvention(newInstance<T>()) propertyWithConvention(newInstance<T>())