Fix toolchain does not configure project level jvm target convention

Now JDK toolchain configures project level compiler options jvm target
convention and does not override convention for task.

For use case when toolchain is configured via task DSL jvm target
convention overrides project level convention.

^KT-57393 Fixed
This commit is contained in:
Yahor Berdnikau
2023-05-08 23:23:23 +02:00
committed by Space Team
parent aa5261395e
commit ac2d00916c
6 changed files with 150 additions and 30 deletions
@@ -12,6 +12,7 @@ import org.gradle.internal.jvm.Jvm
import org.gradle.testkit.runner.BuildResult 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.jetbrains.kotlin.test.TestMetadata
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import java.io.File import java.io.File
import kotlin.io.path.appendText import kotlin.io.path.appendText
@@ -702,6 +703,61 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
} }
} }
@DisplayName("Toolchain should not override Jvm target configured in project level DSL")
@TestMetadata("kotlin-java-toolchain/simple")
@GradleTest
fun toolchainNotOverrideProjectJvmTarget(gradleVersion: GradleVersion) {
project(
projectName = "simple".fullProjectName,
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
buildGradle.appendText(
//language=groovy
"""
|
|kotlin.compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
|
""".trimMargin()
)
build(":compileKotlin") {
assertTasksExecuted(":compileKotlin")
assertCompilerArgument(":compileKotlin", "-jvm-target 11")
}
}
}
@AndroidGradlePluginTests
@DisplayName("Toolchain should not override Jvm taget configured via kotlinOptions in android project")
@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_42)
@GradleAndroidTest
internal fun kotlinOptionsAndroidAndToolchainNotOverrideJvmTarget(
gradleVersion: GradleVersion,
agpVersion: String,
providedJdk: JdkVersions.ProvidedJdk
) {
project(
"android".fullProjectName,
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = agpVersion, logLevel = LogLevel.DEBUG),
buildJdk = providedJdk.location
) {
buildGradle.appendText(
//language=groovy
"""
|
|android.kotlinOptions.jvmTarget = "11"
""".trimMargin()
)
build(":compileDebugKotlin") {
assertTasksExecuted(":compileDebugKotlin")
assertCompilerArgument(":compileDebugKotlin", "-jvm-target 11")
}
}
}
private fun BuildResult.assertJdkHomeIsUsingJdk( private fun BuildResult.assertJdkHomeIsUsingJdk(
javaexecPath: String javaexecPath: String
) = assertOutputContains("[KOTLIN] Kotlin compilation 'jdkHome' argument: $javaexecPath") ) = assertOutputContains("[KOTLIN] Kotlin compilation 'jdkHome' argument: $javaexecPath")
@@ -75,7 +75,7 @@ abstract class KotlinTopLevelExtension(internal val project: Project) : KotlinTo
override lateinit var coreLibrariesVersion: String override lateinit var coreLibrariesVersion: String
private val toolchainSupport = ToolchainSupport.createToolchain(project) private val toolchainSupport = ToolchainSupport.createToolchain(project, this)
/** /**
* Configures [Java toolchain](https://docs.gradle.org/current/userguide/toolchains.html) both for Kotlin JVM and Java tasks. * Configures [Java toolchain](https://docs.gradle.org/current/userguide/toolchains.html) both for Kotlin JVM and Java tasks.
@@ -14,6 +14,7 @@ import org.gradle.api.tasks.TaskContainer
import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.jvm.toolchain.JavaToolchainSpec import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.jetbrains.kotlin.gradle.tasks.DefaultKotlinJavaToolchain import org.jetbrains.kotlin.gradle.tasks.DefaultKotlinJavaToolchain
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain import org.jetbrains.kotlin.gradle.tasks.UsesKotlinJavaToolchain
import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.newInstance import org.jetbrains.kotlin.gradle.utils.newInstance
@@ -24,12 +25,14 @@ internal interface ToolchainSupport {
companion object { companion object {
internal fun createToolchain( internal fun createToolchain(
project: Project project: Project,
kotlinExtension: KotlinTopLevelExtensionConfig
): ToolchainSupport { ): ToolchainSupport {
return project.objects.newInstance<DefaultToolchainSupport>( return project.objects.newInstance<DefaultToolchainSupport>(
project.extensions, project.extensions,
project.tasks, project.tasks,
project.plugins project.plugins,
kotlinExtension
) )
} }
} }
@@ -38,7 +41,8 @@ internal interface ToolchainSupport {
internal abstract class DefaultToolchainSupport @Inject constructor( internal abstract class DefaultToolchainSupport @Inject constructor(
private val extensions: ExtensionContainer, private val extensions: ExtensionContainer,
private val tasks: TaskContainer, private val tasks: TaskContainer,
private val plugins: PluginContainer private val plugins: PluginContainer,
private val kotlinExtension: KotlinTopLevelExtensionConfig
) : ToolchainSupport { ) : ToolchainSupport {
private val toolchainSpec: JavaToolchainSpec private val toolchainSpec: JavaToolchainSpec
get() = extensions get() = extensions
@@ -60,12 +64,28 @@ internal abstract class DefaultToolchainSupport @Inject constructor(
val toolchainService = extensions.findByType(JavaToolchainService::class.java) val toolchainService = extensions.findByType(JavaToolchainService::class.java)
?: error("Gradle JavaToolchainService is not available!") ?: error("Gradle JavaToolchainService is not available!")
val javaLauncher = toolchainService.launcherFor(toolchainSpec) val javaLauncher = toolchainService.launcherFor(toolchainSpec)
tasks tasks
.withType<UsesKotlinJavaToolchain>() .withType<UsesKotlinJavaToolchain>()
.configureEach { .configureEach {
(it.kotlinJavaToolchain.toolchain as DefaultKotlinJavaToolchain.DefaultJavaToolchainSetter) (it.kotlinJavaToolchain.toolchain as DefaultKotlinJavaToolchain.DefaultJavaToolchainSetter)
.useAsConvention(javaLauncher) .useAsConvention(javaLauncher)
} }
if (kotlinExtension !is KotlinJvmProjectExtension &&
kotlinExtension !is KotlinAndroidProjectExtension
) {
tasks
.withType<UsesKotlinJavaToolchain>()
.configureEach { task ->
if (task is KotlinJvmCompile) {
DefaultKotlinJavaToolchain.wireJvmTargetToJvm(
task.compilerOptions,
(task.kotlinJavaToolchain as DefaultKotlinJavaToolchain).providedJvm
)
}
}
}
} }
} }
} }
@@ -33,6 +33,10 @@ internal open class KotlinJvmPlugin(
) { ) {
extensionCompilerOptions.verbose.convention(logger.isDebugEnabled) extensionCompilerOptions.verbose.convention(logger.isDebugEnabled)
extensionCompilerOptions.moduleName.convention(baseModuleName()) extensionCompilerOptions.moduleName.convention(baseModuleName())
DefaultKotlinJavaToolchain.wireJvmTargetToToolchain(
extensionCompilerOptions,
project
)
compilationsContainer.configureEach { compilationsContainer.configureEach {
val jvmCompilerOptions = it.compilerOptions.options as KotlinJvmCompilerOptions val jvmCompilerOptions = it.compilerOptions.options as KotlinJvmCompilerOptions
KotlinJvmCompilerOptionsHelper.syncOptionsAsConvention( KotlinJvmCompilerOptionsHelper.syncOptionsAsConvention(
@@ -7,15 +7,18 @@ package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.JavaVersion import org.gradle.api.JavaVersion
import org.gradle.api.Project
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.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPluginExtension
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.* import org.gradle.jvm.toolchain.*
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.*
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
@@ -27,7 +30,7 @@ import javax.inject.Inject
internal abstract class DefaultKotlinJavaToolchain @Inject constructor( internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
private val objects: ObjectFactory, private val objects: ObjectFactory,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
private val kotlinCompileTaskProvider: () -> KotlinCompile? jvmCompilerOptions: () -> KotlinJvmCompilerOptions?
) : KotlinJavaToolchain { ) : KotlinJavaToolchain {
@get:Internal @get:Internal
@@ -60,27 +63,6 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
) )
.chainedFinalizeValueOnRead() .chainedFinalizeValueOnRead()
init {
wireJvmTargetToToolchain()
}
private fun wireJvmTargetToToolchain() {
kotlinCompileTaskProvider()?.let { task ->
task.compilerOptions.jvmTarget.convention(
providedJvm.map { jvm ->
// For Java 9 and Java 10 JavaVersion returns "1.9" or "1.10" accordingly
// that is not accepted by Kotlin compiler
val normalizedVersion = when (jvm.javaVersion) {
JavaVersion.VERSION_1_9 -> "9"
JavaVersion.VERSION_1_10 -> "10"
else -> jvm.javaVersion.toString()
}
JvmTarget.fromTarget(normalizedVersion)
}.orElse(JvmTarget.DEFAULT)
)
}
}
@get:Internal @get:Internal
internal val javaExecutable: RegularFileProperty = objects internal val javaExecutable: RegularFileProperty = objects
.fileProperty() .fileProperty()
@@ -134,15 +116,20 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
final override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter( final override val jdk: KotlinJavaToolchain.JdkSetter = DefaultJdkSetter(
providedJvm, providedJvm,
objects objects,
jvmCompilerOptions
) )
final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter = final override val toolchain: KotlinJavaToolchain.JavaToolchainSetter =
DefaultJavaToolchainSetter(providedJvm) DefaultJavaToolchainSetter(
providedJvm,
jvmCompilerOptions
)
private class DefaultJdkSetter( private class DefaultJdkSetter(
private val providedJvm: Property<Jvm>, private val providedJvm: Property<Jvm>,
private val objects: ObjectFactory, private val objects: ObjectFactory,
private val jvmCompilerOptions: () -> KotlinJvmCompilerOptions?
) : KotlinJavaToolchain.JdkSetter { ) : KotlinJavaToolchain.JdkSetter {
override fun use( override fun use(
@@ -161,11 +148,16 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
Jvm.discovered(jdkHomeLocation, null, jdkVersion) Jvm.discovered(jdkHomeLocation, null, jdkVersion)
} }
) )
jvmCompilerOptions()?.let {
wireJvmTargetToJvm(it, providedJvm)
}
} }
} }
internal class DefaultJavaToolchainSetter( internal class DefaultJavaToolchainSetter(
private val providedJvm: Property<Jvm> private val providedJvm: Property<Jvm>,
private val jvmCompilerOptions: () -> KotlinJvmCompilerOptions?
) : KotlinJavaToolchain.JavaToolchainSetter { ) : KotlinJavaToolchain.JavaToolchainSetter {
internal fun useAsConvention( internal fun useAsConvention(
@@ -178,6 +170,54 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
javaLauncher: Provider<JavaLauncher> javaLauncher: Provider<JavaLauncher>
) { ) {
providedJvm.set(javaLauncher.map(::mapToJvm)) providedJvm.set(javaLauncher.map(::mapToJvm))
jvmCompilerOptions()?.let {
wireJvmTargetToJvm(it, providedJvm)
}
}
}
companion object {
internal fun wireJvmTargetToJvm(
jvmCompilerOptions: KotlinJvmCompilerOptions,
toolchainJvm: Provider<Jvm>
) {
jvmCompilerOptions.jvmTarget.convention(
toolchainJvm.map { jvm ->
// For Java 9 and Java 10 JavaVersion returns "1.9" or "1.10" accordingly
// that is not accepted by Kotlin compiler
val normalizedVersion = when (jvm.javaVersion) {
JavaVersion.VERSION_1_9 -> "9"
JavaVersion.VERSION_1_10 -> "10"
else -> jvm.javaVersion.toString()
}
JvmTarget.fromTarget(normalizedVersion)
}.orElse(JvmTarget.DEFAULT)
)
}
private fun wireJvmTargetToToolchain(
jvmCompilerOptions: KotlinJvmCompilerOptions,
javaLauncher: Provider<JavaLauncher>
): Unit = wireJvmTargetToJvm(
jvmCompilerOptions,
javaLauncher.map(::mapToJvm)
)
internal fun wireJvmTargetToToolchain(
compilerOptions: KotlinJvmCompilerOptions,
project: Project,
) {
project.plugins.withId("org.gradle.java-base") {
val toolchainService = project.extensions.findByType(JavaToolchainService::class.java)
?: error("Gradle JavaToolchainService is not available!")
val toolchainSpec = project.extensions
.getByType(JavaPluginExtension::class.java)
.toolchain
val javaLauncher = toolchainService.launcherFor(toolchainSpec)
wireJvmTargetToToolchain(compilerOptions, javaLauncher)
}
} }
private fun mapToJvm(javaLauncher: JavaLauncher): Jvm { private fun mapToJvm(javaLauncher: JavaLauncher): Jvm {
@@ -144,7 +144,7 @@ abstract class KotlinCompile @Inject constructor(
@get:Internal @get:Internal
final override val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory final override val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
.propertyWithNewInstance({ this }) .propertyWithNewInstance({ compilerOptions })
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchain> = defaultKotlinJavaToolchain.cast() final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchain> = defaultKotlinJavaToolchain.cast()