Validate Java and Kotlin target compatibility have same value.
This will ensure compiled java sources and kotlin sources class files has the same bytecode version. ^KT-45611 Fixed ^KT-43095 Fixed
This commit is contained in:
+188
-22
@@ -34,28 +34,6 @@ 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
|
||||
) {
|
||||
//language=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) {
|
||||
@@ -63,6 +41,8 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
projectName = "simple".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
) {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_9)
|
||||
|
||||
useJdkToCompile(
|
||||
getJdk9Path(),
|
||||
JavaVersion.VERSION_1_9
|
||||
@@ -88,6 +68,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
if (shouldUseToolchain(gradleVersion)) {
|
||||
useToolchainExtension(11)
|
||||
} else {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_9)
|
||||
useJdkToCompile(
|
||||
getJdk9Path(),
|
||||
JavaVersion.VERSION_1_9
|
||||
@@ -107,6 +88,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
if (shouldUseToolchain(gradleVersion)) {
|
||||
useToolchainExtension(11)
|
||||
} else {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_9)
|
||||
useJdkToCompile(
|
||||
getJdk9Path(),
|
||||
JavaVersion.VERSION_1_9
|
||||
@@ -134,6 +116,7 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
if (shouldUseToolchain(gradleVersion)) {
|
||||
useToolchainExtension(11)
|
||||
} else {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_9)
|
||||
useJdkToCompile(
|
||||
getJdk9Path(),
|
||||
JavaVersion.VERSION_1_9
|
||||
@@ -440,6 +423,173 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Should produce warning if java and kotlin jvm targets are different")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun shouldWarnIfJavaAndKotlinJvmTargetsAreDifferent(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.WARN)
|
||||
) {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_8)
|
||||
useToolchainToCompile(11)
|
||||
|
||||
build("assemble") {
|
||||
assertOutputContains(
|
||||
"'compileJava' task (current target is 1.8) and 'compileKotlin' task (current target is 11) jvm target compatibility " +
|
||||
"should be set to the same Java version."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Should fail the build if verification mode is 'error' and kotlin and java targets are different")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun shouldFailBuildIfJavaAndKotlinJvmTargetsAreDifferent(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion
|
||||
) {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_8)
|
||||
useToolchainToCompile(11)
|
||||
//language=properties
|
||||
gradleProperties.append(
|
||||
"""
|
||||
kotlin.jvm.target.validation.mode = error
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
buildAndFail("assemble") {
|
||||
assertOutputContains(
|
||||
"'compileJava' task (current target is 1.8) and 'compileKotlin' task (current target is 11) jvm target compatibility " +
|
||||
"should be set to the same Java version."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Should ignore if verification mode is 'ignore' and kotlin and java targets are different")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun shouldNotPrintAnythingIfJavaAndKotlinJvmTargetsAreDifferent(
|
||||
gradleVersion: GradleVersion
|
||||
) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion
|
||||
) {
|
||||
setJavaCompilationCompatibility(JavaVersion.VERSION_1_8)
|
||||
useToolchainToCompile(11)
|
||||
//language=properties
|
||||
gradleProperties.append(
|
||||
"""
|
||||
kotlin.jvm.target.validation.mode = ignore
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
build("assemble") {
|
||||
assertOutputDoesNotContain(
|
||||
"'compileJava' task (current target is 1.8) and 'compileKotlin' task (current target is 11) jvm target compatibility " +
|
||||
"should be set to the same Java version."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Should not produce warning when java and kotlin jvm targets are the same")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun shouldNotWarnOnJavaAndKotlinSameJvmTargets(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion
|
||||
) {
|
||||
useToolchainToCompile(11)
|
||||
|
||||
build("build") {
|
||||
assertOutputDoesNotContain(
|
||||
"'compileJava' task (current target is 1.8) and 'compileKotlin' task (current target is 11) jvm target compatibility " +
|
||||
"should be set to the same Java version."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Should produce Java-Kotlin jvm target incompatibility warning only for related tasks")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun shouldProduceJavaKotlinJvmTargetDifferenceWarningOnlyForRelatedTasks(
|
||||
gradleVersion: GradleVersion
|
||||
) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion
|
||||
) {
|
||||
useToolchainToCompile(11)
|
||||
|
||||
JavaVersion.VERSION_1_8
|
||||
//language=Groovy
|
||||
rootBuildGradle.append(
|
||||
"""
|
||||
|
||||
tasks
|
||||
.matching {
|
||||
it instanceof JavaCompile && it.name == "compileTestJava"
|
||||
}
|
||||
.configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
build("build") {
|
||||
assertOutputContains(
|
||||
"'compileTestJava' task (current target is 1.8) and 'compileTestKotlin' task (current target is 11) jvm target " +
|
||||
"compatibility should be set to the same Java version."
|
||||
)
|
||||
assertOutputDoesNotContain(
|
||||
"'compileJava' task (current target is 1.8) and 'compileKotlin' task (current target is 11) jvm target compatibility " +
|
||||
"should be set to the same Java version."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Setting toolchain via java extension should also affect Kotlin compilations")
|
||||
@GradleTestVersions(minVersion = "6.7.1")
|
||||
@GradleTest
|
||||
internal fun settingToolchainViaJavaShouldAlsoWork(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "kotlinJavaProject".fullProjectName,
|
||||
gradleVersion = gradleVersion
|
||||
) {
|
||||
//language=groovy
|
||||
rootBuildGradle.append(
|
||||
"""
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(11))
|
||||
}
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
//language=properties
|
||||
gradleProperties.append(
|
||||
"""
|
||||
kotlin.jvm.target.validation.mode = error
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
build("build")
|
||||
}
|
||||
}
|
||||
|
||||
private fun BuildResult.assertJdkHomeIsUsingJdk(
|
||||
javaexecPath: String
|
||||
) = assertOutputContains("[KOTLIN] Kotlin compilation 'jdkHome' argument: $javaexecPath")
|
||||
@@ -475,6 +625,22 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestProject.setJavaCompilationCompatibility(
|
||||
target: JavaVersion
|
||||
) {
|
||||
//language=Groovy
|
||||
rootBuildGradle.append(
|
||||
"""
|
||||
|
||||
tasks.withType(JavaCompile.class).configureEach {
|
||||
sourceCompatibility = JavaVersion.${target.name}
|
||||
targetCompatibility = JavaVersion.${target.name}
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestProject.useJdkToCompile(
|
||||
jdkPath: String,
|
||||
jdkVersion: JavaVersion
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "java"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.guava:guava:12.0'
|
||||
|
||||
testImplementation 'org.testng:testng:6.8'
|
||||
}
|
||||
|
||||
test {
|
||||
useTestNG()
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package demo;
|
||||
|
||||
/**
|
||||
* Created by Nikita.Skvortsov
|
||||
* Date: 3/1/13, 10:53 AM
|
||||
*/
|
||||
public class Greeter {
|
||||
private final String myGreeting;
|
||||
|
||||
public Greeter(String greeting) {
|
||||
myGreeting = greeting;
|
||||
}
|
||||
|
||||
public String getGreeting() {
|
||||
return myGreeting;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package demo;
|
||||
|
||||
/**
|
||||
* Created by Nikita.Skvortsov
|
||||
* Date: 3/1/13, 10:50 AM
|
||||
*/
|
||||
public class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
final KotlinGreetingJoiner example = new KotlinGreetingJoiner(new Greeter("Hi"));
|
||||
|
||||
example.addName("Harry");
|
||||
example.addName("Ron");
|
||||
example.addName(null);
|
||||
example.addName("Hermione");
|
||||
|
||||
System.out.println(example.getJoinedGreeting());
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package demo
|
||||
|
||||
import com.google.common.primitives.Ints
|
||||
import com.google.common.base.Joiner
|
||||
import java.util.ArrayList
|
||||
|
||||
class KotlinGreetingJoiner(val greeter : Greeter) {
|
||||
|
||||
val names = ArrayList<String?>()
|
||||
|
||||
fun addName(name : String?): Unit{
|
||||
names.add(name)
|
||||
}
|
||||
|
||||
fun getJoinedGreeting() : String? {
|
||||
val joiner = Joiner.on(" and ").skipNulls();
|
||||
return "${greeter.getGreeting()} ${joiner.join(names)}"
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package demo;
|
||||
|
||||
public class HelloWorldTest {
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package demo
|
||||
|
||||
import com.google.common.primitives.Ints
|
||||
import com.google.common.base.Joiner
|
||||
import org.testng.Assert.*
|
||||
import org.testng.annotations.AfterMethod
|
||||
import org.testng.annotations.BeforeMethod
|
||||
import org.testng.annotations.Test as test
|
||||
|
||||
class TestSource() {
|
||||
@test fun f() {
|
||||
val example : KotlinGreetingJoiner = KotlinGreetingJoiner(Greeter("Hi"))
|
||||
example.addName("Harry")
|
||||
example.addName("Ron")
|
||||
example.addName(null)
|
||||
example.addName("Hermione")
|
||||
|
||||
assertEquals(example.getJoinedGreeting(), "Hi Harry and Ron and Hermione")
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -42,6 +42,7 @@ internal fun PropertiesProvider.mapKotlinTaskProperties(task: AbstractKotlinComp
|
||||
task.kotlinOptions.useFir = true
|
||||
}
|
||||
}
|
||||
task.jvmTargetValidationMode.set(jvmTargetValidationMode)
|
||||
}
|
||||
|
||||
if (task is Kotlin2JsCompile) {
|
||||
@@ -340,6 +341,13 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val kotlinTestInferJvmVariant: Boolean
|
||||
get() = booleanProperty("kotlin.test.infer.jvm.variant") ?: true
|
||||
|
||||
enum class JvmTargetValidationMode {
|
||||
IGNORE, WARNING, ERROR
|
||||
}
|
||||
|
||||
val jvmTargetValidationMode: JvmTargetValidationMode
|
||||
get() = enumProperty("kotlin.jvm.target.validation.mode", JvmTargetValidationMode.WARNING)
|
||||
|
||||
private fun propertyWithDeprecatedVariant(propName: String, deprecatedPropName: String): String? {
|
||||
val deprecatedProperty = property(deprecatedPropName)
|
||||
if (deprecatedProperty != null) {
|
||||
@@ -351,6 +359,11 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
private fun booleanProperty(propName: String): Boolean? =
|
||||
property(propName)?.toBoolean()
|
||||
|
||||
private inline fun <reified T : Enum<T>> enumProperty(
|
||||
propName: String,
|
||||
defaultValue: T
|
||||
): T = property(propName)?.let { enumValueOf<T>(it.toUpperCase()) } ?: defaultValue
|
||||
|
||||
private fun property(propName: String): String? =
|
||||
if (project.hasProperty(propName)) {
|
||||
project.property(propName) as? String
|
||||
|
||||
-1
@@ -119,7 +119,6 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
|
||||
get() = defaultJavaToolchainSetter
|
||||
?: throw GradleException("Toolchain support is available from $TOOLCHAIN_SUPPORTED_VERSION")
|
||||
|
||||
|
||||
private abstract class JvmTargetUpdater(
|
||||
private val kotlinCompileTaskProvider: () -> KotlinCompileTask<KotlinJvmOptionsBase>?
|
||||
) {
|
||||
|
||||
+49
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.*
|
||||
@@ -46,6 +47,7 @@ import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
@@ -428,6 +430,25 @@ abstract class KotlinCompile @Inject constructor(
|
||||
UsesKotlinJavaToolchain {
|
||||
|
||||
class Configurator(kotlinCompilation: KotlinCompilationData<*>) : AbstractKotlinCompile.Configurator<KotlinCompile>(kotlinCompilation) {
|
||||
override fun configure(task: KotlinCompile) {
|
||||
super.configure(task)
|
||||
|
||||
val compileJavaTaskProvider = when (compilation) {
|
||||
is KotlinJvmCompilation -> compilation.compileJavaTaskProvider
|
||||
is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider
|
||||
is KotlinWithJavaCompilation -> compilation.compileJavaTaskProvider
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (compileJavaTaskProvider != null) {
|
||||
task.associatedJavaCompileTaskTargetCompatibility.set(
|
||||
compileJavaTaskProvider.map { it.targetCompatibility }
|
||||
)
|
||||
task.associatedJavaCompileTaskName.set(
|
||||
compileJavaTaskProvider.map { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@get:Internal
|
||||
@@ -482,6 +503,15 @@ abstract class KotlinCompile @Inject constructor(
|
||||
}
|
||||
)
|
||||
|
||||
@get:Internal
|
||||
internal abstract val associatedJavaCompileTaskTargetCompatibility: Property<String>
|
||||
|
||||
@get:Internal
|
||||
internal abstract val associatedJavaCompileTaskName: Property<String>
|
||||
|
||||
@get:Internal
|
||||
internal abstract val jvmTargetValidationMode: Property<PropertiesProvider.JvmTargetValidationMode>
|
||||
|
||||
init {
|
||||
incremental = true
|
||||
}
|
||||
@@ -511,6 +541,8 @@ abstract class KotlinCompile @Inject constructor(
|
||||
override fun callCompilerAsync(args: K2JVMCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
|
||||
sourceRoots as SourceRoots.ForJvm
|
||||
|
||||
validateKotlinAndJavaHasSameTargetCompatibility(args)
|
||||
|
||||
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
|
||||
val outputItemCollector = OutputItemsCollectorImpl()
|
||||
val compilerRunner = compilerRunner.get()
|
||||
@@ -544,6 +576,23 @@ abstract class KotlinCompile @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun validateKotlinAndJavaHasSameTargetCompatibility(args: K2JVMCompilerArguments) {
|
||||
associatedJavaCompileTaskTargetCompatibility.orNull?.let { targetCompatibility ->
|
||||
val normalizedJavaVersion = if (targetCompatibility == "1.9") "9" else targetCompatibility
|
||||
if (normalizedJavaVersion != args.jvmTarget) {
|
||||
val javaTaskName = associatedJavaCompileTaskName.get()
|
||||
val errorMessage = "'$javaTaskName' task (current target is $targetCompatibility) and " +
|
||||
"'$name' task (current target is ${args.jvmTarget}) " +
|
||||
"jvm target compatibility should be set to the same Java version."
|
||||
when (jvmTargetValidationMode.get()) {
|
||||
PropertiesProvider.JvmTargetValidationMode.ERROR -> throw GradleException(errorMessage)
|
||||
PropertiesProvider.JvmTargetValidationMode.WARNING -> logger.warn(errorMessage)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@get:Input
|
||||
val disableMultiModuleIC: Boolean by lazy {
|
||||
if (!isIncrementalCompilationEnabled() || !javaOutputDir.isPresent) {
|
||||
|
||||
Reference in New Issue
Block a user