diff --git a/compiler/build-tools/kotlin-build-tools-api-tests/build.gradle.kts b/compiler/build-tools/kotlin-build-tools-api-tests/build.gradle.kts new file mode 100644 index 00000000000..eb76bfa6f9e --- /dev/null +++ b/compiler/build-tools/kotlin-build-tools-api-tests/build.gradle.kts @@ -0,0 +1,143 @@ +import org.gradle.configurationcache.extensions.capitalized +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion + +plugins { + kotlin("jvm") + id("jps-compatible") + `jvm-test-suite` +} + +dependencies { + api(kotlinStdlib()) +} + +kotlin { + compilerOptions { + optIn.add("org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi") + } +} + +class BuildToolsApiTestSuit( + val testName: String, + val apiVersion: BuildToolsVersion, + val implVersion: BuildToolsVersion, + /** + * Tells whether only tests marked with the `CompatibilityTest` JUnit tag must be run + */ + val onlyCompatibilityTests: Boolean = true, +) + +val testMatrix = listOf( + BuildToolsApiTestSuit( + "testDefaultToDefault", + BuildToolsVersion(KotlinToolingVersion(project.version.toString()), isCurrent = true), + BuildToolsVersion(KotlinToolingVersion(project.version.toString()), isCurrent = true), + onlyCompatibilityTests = false, + ), + BuildToolsApiTestSuit( + "test1.9.20ToDefault", + BuildToolsVersion(KotlinToolingVersion(1, 9, 20, null)), + BuildToolsVersion(KotlinToolingVersion(project.version.toString()), isCurrent = true), + ), + BuildToolsApiTestSuit( + "testDefaultTo1.9.20", + BuildToolsVersion(KotlinToolingVersion(project.version.toString()), isCurrent = true), + BuildToolsVersion(KotlinToolingVersion(1, 9, 20, null)), + ), +) + +val SourceSet.kotlinCompileTask + get() = tasks.named("compile${name.capitalized()}Kotlin") + +class BuildToolsVersion(val version: KotlinToolingVersion, val isCurrent: Boolean = false) { + override fun toString() = version.toString() +} + +fun KotlinCompile.ensureCompiledAgainstExpectedBuildToolsApiVersion(version: BuildToolsVersion) { + if (version.isCurrent) return + // the check is required for the case when Gradle substitutes external dependencies with project ones + doFirst { + check(libraries.any { "kotlin-build-tools-api-${version}" in it.name }) { + "compilation classpath must contain kotlin-build-tools-api:$version" + } + } +} + +fun Test.ensureExecutedAgainstExpectedBuildToolsImplVersion(version: BuildToolsVersion) { + if (version.isCurrent) return + // the check is required for the case when Gradle substitutes external dependencies with project ones + doFirst { + check(classpath.any { "kotlin-build-tools-impl-${version}" in it.name }) { + "runtime classpath must contain kotlin-build-tools-impl:$version" + } + } +} + +fun SourceSet.configureApiVersionSourceDirectories() { + java.setSrcDirs( + listOf( + layout.projectDirectory.dir("src/testCommon/java"), + ) + ) + kotlin.setSrcDirs( + listOf( + layout.projectDirectory.dir("src/testCommon/java"), + layout.projectDirectory.dir("src/testCommon/kotlin"), + ) + ) + resources.setSrcDirs( + listOf( + layout.projectDirectory.dir("src/testCommon/resources"), + ) + ) +} + +testing { + suites { + for (suitConfig in testMatrix) { + register(suitConfig.testName) { + sources.configureApiVersionSourceDirectories() + dependencies { + useJUnitJupiter(libs.versions.junit5.get()) + + compileOnly(project()) // propagate stdlib from the main dependencies for compilation, + // the runtime dependency provides the actual required version + implementation(project(":kotlin-tooling-core")) // to reuse `KotlinToolingVersion` + + if (suitConfig.apiVersion.isCurrent) { + compileOnly(project(":compiler:build-tools:kotlin-build-tools-api")) + } else { + compileOnly("org.jetbrains.kotlin:kotlin-build-tools-api:${suitConfig.apiVersion}") + } + sources.kotlinCompileTask.configure { + ensureCompiledAgainstExpectedBuildToolsApiVersion(suitConfig.apiVersion) + } + + if (suitConfig.implVersion.isCurrent) { + runtimeOnly(project(":compiler:build-tools:kotlin-build-tools-impl")) + } else { + runtimeOnly("org.jetbrains.kotlin:kotlin-build-tools-impl:${suitConfig.implVersion}") + } + } + + targets.all { + projectTest(taskName = testTask.name, jUnitMode = JUnitMode.JUnit5) { + ensureExecutedAgainstExpectedBuildToolsImplVersion(suitConfig.implVersion) + useJUnitPlatform { + if (suitConfig.onlyCompatibilityTests) { + includeTags("CompatibilityTest") + } + } + systemProperty("kotlin.build-tools-api.log.level", "DEBUG") + systemProperty("kotlin.build-tools-api.impl-version", suitConfig.implVersion.toString()) // TODO: remove after KT-63862 + } + } + } + } + } +} + +tasks.named("check") { + dependsOn(testing.suites) +} \ No newline at end of file diff --git a/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/CompatibilityTest.kt b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/CompatibilityTest.kt new file mode 100644 index 00000000000..890a2d331be --- /dev/null +++ b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/CompatibilityTest.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2023 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.buildtools.api.tests + +import org.junit.jupiter.api.Tag + +@Target(AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Tag("CompatibilityTest") +annotation class CompatibilityTest diff --git a/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/buildToolsVersion.kt b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/buildToolsVersion.kt new file mode 100644 index 00000000000..df032db1e08 --- /dev/null +++ b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCommon/kotlin/buildToolsVersion.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2023 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.buildtools.api.tests + +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion + +// TODO: remove after KT-63862 +const val IMPL_VERSION_SYSTEM_PROPERTY = "kotlin.build-tools-api.impl-version" +val buildToolsVersion = System.getProperty(IMPL_VERSION_SYSTEM_PROPERTY) + ?.let { KotlinToolingVersion(it) } + ?: error("The build tools implementation version must be passed via the `$IMPL_VERSION_SYSTEM_PROPERTY` system property to the tests") diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1541eca71e7..ed1a65c8b55 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3748,12 +3748,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3812,6 +3854,12 @@ + + + + + + @@ -4404,6 +4452,12 @@ + + + + + + diff --git a/settings.gradle b/settings.gradle index c9c7d381cc3..3bcccf7ebee 100644 --- a/settings.gradle +++ b/settings.gradle @@ -422,7 +422,8 @@ if (!buildProperties.inJpsBuildIdeaSync) { } include ":compiler:build-tools:kotlin-build-tools-api", - ":compiler:build-tools:kotlin-build-tools-impl" + ":compiler:build-tools:kotlin-build-tools-impl", + ":compiler:build-tools:kotlin-build-tools-api-tests" // Experimental modules, should be placed into plugins/compose-compiler-plugin folder include ":plugins:compose-compiler-plugin:compiler",