All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.tasks.DefaultKotlinJavaToolchain.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * 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.tasks

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.*
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
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
import org.jetbrains.kotlin.gradle.utils.providerWithLazyConvention
import java.io.File
import javax.inject.Inject
import kotlin.reflect.full.functions

internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
    private val objects: ObjectFactory,
    projectLayout: ProjectLayout,
    gradle: Gradle,
    kotlinCompileTaskProvider: () -> KotlinCompile?
) : KotlinJavaToolchain {

    private val currentGradleVersion = GradleVersion.version(gradle.gradleVersion)

    @get:Internal
    internal val currentJvm: Provider = objects
        .property(Jvm.current())
        .chainedFinalizeValueOnRead()

    @get:Internal
    internal var providedJvmExplicitlySet = false

    @get:Internal
    internal val providedJvm: Property = objects
        .propertyWithConvention(currentJvm)
        .chainedFinalizeValueOnRead()

    final override val javaVersion: Provider = 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(
                        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,
        javaVersionProvider: Provider
    ): Provider {
        return objects
            .propertyWithConvention(
                jvmProvider.flatMap { jvm ->
                    objects.propertyWithConvention(jvm.toolsJar)
                }
            )
            .orElse(javaVersionProvider.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(null)
                }
            })
    }

    @get:Internal
    internal val jdkToolsJar: Provider = getToolsJarFromJvm(providedJvm, javaVersion)

    @get:Internal
    internal val currentJvmJdkToolsJar: Provider = getToolsJarFromJvm(
        currentJvm,
        currentJvm.map {
            // Current JVM should always have java version
            it.javaVersion!!
        }
    )

    final override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter(
        providedJvm,
        currentGradleVersion,
        objects,
        { providedJvmExplicitlySet = true },
        kotlinCompileTaskProvider
    )

    private val defaultJavaToolchainSetter by lazy(LazyThreadSafetyMode.NONE) {
        if (currentGradleVersion >= TOOLCHAIN_SUPPORTED_VERSION) {
            DefaultJavaToolchainSetter(providedJvm, kotlinCompileTaskProvider)
        } else {
            null
        }
    }

    final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter
        get() = defaultJavaToolchainSetter
            ?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION")

    /**
     * Updates [task] 'jvmTarget' if user has configured toolchain and not 'jvmTarget'.
     *
     *  Should be called on execution state to ensure 'jvmTarget' is set to correct value even on
     *  reusing configuration cache.
     *
     * Should be called on task execution phase!
     */
    internal fun updateJvmTarget(
        task: KotlinCompile,
        args: K2JVMCompilerArguments
    ) {
        check(task.state.executing) {
            "\"updateJvmTarget()\" method should be called only on task execution!"
        }

        if (providedJvmExplicitlySet) {
            val jdkVersion = javaVersion.get()

            // parentKotlinOptionsImpl is set from 'kotlin-android' plugin
            val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull)
                .mapNotNull { (it as KotlinJvmOptionsImpl).jvmTargetField }

            if (appliedJvmTargets.isEmpty()) {
                // For Java 9 and Java 10 JavaVersion returns "1.9" or "1.10" accordingly
                // that is not accepted by Kotlin compiler
                val toolchainJvmTarget = when (jdkVersion) {
                    JavaVersion.VERSION_1_9 -> "9"
                    JavaVersion.VERSION_1_10 -> "10"
                    else -> jdkVersion.toString()
                }
                task.kotlinOptions.jvmTarget = toolchainJvmTarget
                args.jvmTarget = toolchainJvmTarget
            }
        }
    }

    private abstract class JvmTargetUpdater(
        private val kotlinCompileTaskProvider: () -> KotlinCompile?
    ) {
        fun updateJvmTarget(
            jdkVersion: JavaVersion
        ) {
            kotlinCompileTaskProvider()?.let { task ->
                // parentKotlinOptionsImpl is set from 'kotlin-android' plugin
                val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull)
                    .mapNotNull { (it as KotlinJvmOptionsImpl).jvmTargetField }

                if (appliedJvmTargets.isEmpty()) {
                    // For Java 9 and Java 10 JavaVersion returns "1.9" or "1.10" accordingly
                    // that is not accepted by Kotlin compiler
                    task.kotlinOptions.jvmTarget = when (jdkVersion) {
                        JavaVersion.VERSION_1_9 -> "9"
                        JavaVersion.VERSION_1_10 -> "10"
                        else -> jdkVersion.toString()
                    }
                }
            }
        }
    }

    private class DefaultJdkSetter(
        private val providedJvm: Property,
        private val currentGradleVersion: GradleVersion,
        private val objects: ObjectFactory,
        private val updateProvidedJdkCallback: () -> Unit,
        kotlinCompileTaskProvider: () -> KotlinCompile?
    ) : JvmTargetUpdater(kotlinCompileTaskProvider),
        KotlinJavaToolchain.JdkSetter {

        override fun use(
            jdkHomeLocation: File,
            jdkVersion: JavaVersion
        ) {
            require(jdkHomeLocation.isDirectory) {
                "Supplied jdkHomeLocation must be a valid directory. You supplied: $jdkHomeLocation"
            }
            require(jdkHomeLocation.exists()) {
                "Supplied jdkHomeLocation does not exists. You supplied: $jdkHomeLocation"
            }

            updateProvidedJdkCallback.invoke()
            providedJvm.set(
                objects.providerWithLazyConvention {
                    updateJvmTarget(jdkVersion)
                    if (currentGradleVersion < GradleVersion.version("6.2.0")) {
                        // Before Gradle 6.2.0 'Jvm.discovered' does not have 'implementationJavaVersion' parameter
                        Jvm::class.functions
                            .first { it.name == "discovered" }
                            .call(jdkHomeLocation, jdkVersion) as Jvm
                    } else {
                        Jvm.discovered(jdkHomeLocation, null, jdkVersion)
                    }
                }
            )
        }
    }

    private inner class DefaultJavaToolchainSetter(
        private val providedJvm: Property,
        kotlinCompileTaskProvider: () -> KotlinCompile?
    ) : JvmTargetUpdater(kotlinCompileTaskProvider),
        KotlinJavaToolchain.JavaToolchainSetter {
        override fun use(
            javaLauncher: Provider
        ) {
            providedJvmExplicitlySet = true
            providedJvm.set(
                javaLauncher.map {
                    val metadata = javaLauncher.get().metadata
                    val javaVersion = JavaVersion.toVersion(metadata.languageVersion.asInt())
                    updateJvmTarget(javaVersion)
                    Jvm.discovered(
                        metadata.installationPath.asFile,
                        null,
                        javaVersion
                    )
                }
            )
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy