diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt index 9c3bcf47df0..b28c5eb66f4 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheIT.kt @@ -16,215 +16,208 @@ package org.jetbrains.kotlin.gradle -import org.jetbrains.kotlin.gradle.util.modify -import org.junit.Test -import java.io.File +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.readText +import kotlin.io.path.writeText -class BuildCacheIT : BaseGradleIT() { - override fun defaultBuildOptions(): BuildOptions = - super.defaultBuildOptions().copy(withBuildCache = true) +@ExperimentalPathApi +@DisplayName("Local build cache") +@SimpleGradlePluginTests +class BuildCacheIT : KGPBaseTest() { - companion object { - private val GRADLE_VERSION = GradleVersionRequired.None - } + override val defaultBuildOptions: BuildOptions = + super.defaultBuildOptions.copy(buildCacheEnabled = true) - @Test - fun testKotlinCachingEnabledFlag() = with(Project("simpleProject", GRADLE_VERSION)) { - prepareLocalBuildCache() + private val localBuildCacheDir get() = workingDir.resolve("custom-jdk-build-cache") - build("assemble") { - assertSuccessful() - assertTaskPackedToCache(":compileKotlin") - } + @DisplayName("kotlin.caching.enabled flag should enable caching for Kotlin tasks") + @GradleTest + fun testKotlinCachingEnabledFlag(gradleVersion: GradleVersion) { + project("simpleProject", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) - build("clean", "assemble", "-Dkotlin.caching.enabled=false") { - assertSuccessful() - assertNotContains(":compileKotlin FROM-CACHE") - } - } - - @Test - fun testCacheHitAfterClean() = with(Project("simpleProject", GRADLE_VERSION)) { - prepareLocalBuildCache() - - build("assemble") { - assertSuccessful() - assertTaskPackedToCache(":compileKotlin") - } - build("clean", "assemble") { - assertSuccessful() - assertContains(":compileKotlin FROM-CACHE") - assertContains(":compileJava FROM-CACHE") - } - } - - @Test - fun testCacheHitAfterCacheHit() = with(Project("simpleProject", GRADLE_VERSION)) { - prepareLocalBuildCache() - - build("assemble") { - assertSuccessful() - // Should store the output into the cache: - assertTaskPackedToCache(":compileKotlin") - } - - val sourceFile = File(projectDir, "src/main/kotlin/helloWorld.kt") - val originalSource: String = sourceFile.readText() - val modifiedSource: String = originalSource.replace(" and ", " + ") - sourceFile.writeText(modifiedSource) - - build("assemble") { - assertSuccessful() - assertTaskPackedToCache(":compileKotlin") - } - - sourceFile.writeText(originalSource) - - build("assemble") { - assertSuccessful() - // Should load the output from cache: - assertContains(":compileKotlin FROM-CACHE") - } - - sourceFile.writeText(modifiedSource) - - build("assemble") { - assertSuccessful() - // And should load the output from cache again, without compilation: - assertContains(":compileKotlin FROM-CACHE") - } - } - - @Test - fun testKotlinCompileIncrementalBuildWithoutRelocation() = with(Project("buildCacheSimple", GRADLE_VERSION)) { - prepareLocalBuildCache() - - checkKotlinCompileCachingIncrementalBuild(projectDir, this) - } - - @Test - fun testKotlinCompileCachingIncrementalBuildWithRelocation() { - with(Project("buildCacheSimple", GRADLE_VERSION)) { - prepareLocalBuildCache() - - // Copy the project to a different directory - val copyProjectParentDir = projectDir.resolveSibling("copy_${projectDir.name}").also { it.mkdirs() } - copyRecursively(projectDir, copyProjectParentDir) - - checkKotlinCompileCachingIncrementalBuild(copyProjectParentDir.resolve(projectName), this) - } - } - - @Test - fun testKaptCachingIncrementalBuildWithoutRelocation() { - with(Project("kaptAvoidance", GRADLE_VERSION, directoryPrefix = "kapt2")) { - prepareLocalBuildCache() - - checkKaptCachingIncrementalBuild(projectDir, this) - } - } - - @Test - fun testKaptCachingIncrementalBuildWithRelocation() { - with(Project("kaptAvoidance", GRADLE_VERSION, directoryPrefix = "kapt2")) { - prepareLocalBuildCache() - - // Copy the project to a different directory - val copyProjectParentDir = projectDir.resolveSibling("copy_${projectDir.name}").also { it.mkdirs() } - copyRecursively(projectDir, copyProjectParentDir) - - checkKaptCachingIncrementalBuild(copyProjectParentDir.resolve(projectName), this) - } - } - - private fun checkKotlinCompileCachingIncrementalBuild(projectToBeModified: File, project: BaseGradleIT.Project) { - with(project) { - // First build, should be stored into the build cache: build("assemble") { - assertSuccessful() - assertTaskPackedToCache(":compileKotlin") + assertTasksPackedToCache(":compileKotlin") } - // A cache hit: a clean build without any changes to the project - build("clean", "assemble", projectDir = projectToBeModified) { - assertSuccessful() - assertContains(":compileKotlin FROM-CACHE") - } - - // Change the return type of foo() from Int to String in foo.kt, and check that fooUsage.kt is recompiled as well: - File(projectToBeModified, "src/main/kotlin/foo.kt").modify { it.replace("Int = 1", "String = \"abc\"") } - build("assemble", projectDir = projectToBeModified) { - assertSuccessful() - assertCompiledKotlinSources(relativize(allKotlinFiles)) - } - - // Revert the change to the return type of foo(), and check if we get a cache hit - File(projectToBeModified, "src/main/kotlin/foo.kt").modify { it.replace("String = \"abc\"", "Int = 1") } - build("clean", "assemble", projectDir = projectToBeModified) { - assertSuccessful() - assertContains(":compileKotlin FROM-CACHE") + build("clean", "assemble", "-Dkotlin.caching.enabled=false") { + assertTasksExecuted(":compileKotlin") } } } - private fun checkKaptCachingIncrementalBuild(projectToBeModified: File, project: BaseGradleIT.Project) { - with(project) { - val options = defaultBuildOptions().copy( - kaptOptions = KaptOptions( - verbose = true, - useWorkers = false, - incrementalKapt = true, - includeCompileClasspath = false - ) + @DisplayName("Kotlin JVM task should be taken from cache") + @GradleTest + fun testCacheHitAfterClean(gradleVersion: GradleVersion) { + project("simpleProject", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + + build("assemble") { + assertTasksPackedToCache(":compileKotlin") + } + + build("clean", "assemble") { + assertTasksFromCache(":compileKotlin", ":compileJava") + } + } + } + + @DisplayName("Should correctly handle modification/restoration of source file") + @GradleTest + fun testCacheHitAfterCacheHit(gradleVersion: GradleVersion) { + project("simpleProject", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + + build("assemble") { + // Should store the output into the cache: + assertTasksPackedToCache(":compileKotlin") + } + + val sourceFile = projectPath.resolve("src/main/kotlin/helloWorld.kt") + val originalSource: String = sourceFile.readText() + val modifiedSource: String = originalSource.replace(" and ", " + ") + sourceFile.writeText(modifiedSource) + + build("assemble") { + assertTasksPackedToCache(":compileKotlin") + } + + sourceFile.writeText(originalSource) + + build("assemble") { + // Should load the output from cache: + assertTasksFromCache(":compileKotlin") + } + + sourceFile.writeText(modifiedSource) + + build("assemble") { + // And should load the output from cache again, without compilation: + assertTasksFromCache(":compileKotlin") + } + } + } + + @DisplayName("Incremental compilation works with cache") + @GradleTest + fun testKotlinCompileIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) { + project("buildCacheSimple", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + + checkKotlinCompileCachingIncrementalBuild(this, this) + } + } + + // TODO: move it into relocatable build cache tests + @DisplayName("Incremental compilation build cache does not break relocated cache") + @GradleTest + fun testKotlinCompileCachingIncrementalBuildWithRelocation(gradleVersion: GradleVersion) { + val firstProject = project("buildCacheSimple", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + } + + val secondProject = project("buildCacheSimple", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + } + + checkKotlinCompileCachingIncrementalBuild(firstProject, secondProject) + } + + // TODO: move it into relocatable build cache tests + @DisplayName("Kapt incremental compilation works with cache") + @GradleTest + fun testKaptCachingIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) { + project("kapt2/kaptAvoidance", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + + checkKaptCachingIncrementalBuild(this, this) + } + } + + // TODO: move it into build cache relocation tests + @DisplayName("Kapt incremental compilation build does not break relocated build cache") + @GradleTest + fun testKaptCachingIncrementalBuildWithRelocation(gradleVersion: GradleVersion) { + val firstProject = project("kapt2/kaptAvoidance", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + } + + val secondProject = project("kapt2/kaptAvoidance", gradleVersion) { + enableLocalBuildCache(localBuildCacheDir) + } + + checkKaptCachingIncrementalBuild(firstProject, secondProject) + } + + private fun checkKotlinCompileCachingIncrementalBuild( + firstProject: TestProject, + secondProject: TestProject + ) { + // First build, should be stored into the build cache: + firstProject.build("assemble") { + assertTasksPackedToCache(":compileKotlin") + } + + // A cache hit: a clean build without any changes to the project + secondProject.build("clean", "assemble") { + assertTasksFromCache(":compileKotlin") + } + + // Change the return type of foo() from Int to String in foo.kt, and check that fooUsage.kt is recompiled as well: + val fooKtSourceFile = secondProject.projectPath.resolve("src/main/kotlin/foo.kt") + fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") } + secondProject.build("assemble", forceOutput = true) { + assertIncrementalCompilation(modifiedFiles = setOf(fooKtSourceFile)) + } + + // Revert the change to the return type of foo(), and check if we get a cache hit + fooKtSourceFile.modify { it.replace("String = \"abc\"", "Int = 1") } + secondProject.build("clean", "assemble") { + assertTasksFromCache(":compileKotlin") + } + } + + private fun checkKaptCachingIncrementalBuild( + firstProject: TestProject, + secondProject: TestProject + ) { + val options = defaultBuildOptions.copy( + kaptOptions = BuildOptions.KaptOptions( + verbose = true, + useWorkers = false, + incrementalKapt = true, + includeCompileClasspath = false ) + ) - // First build, should be stored into the build cache: - build(options = options, params = *arrayOf("clean", ":app:build")) { - assertSuccessful() - assertTaskPackedToCache(":app:kaptGenerateStubsKotlin") - assertTaskPackedToCache(":app:kaptKotlin") - } + // First build, should be stored into the build cache: + firstProject.build("clean", ":app:build", buildOptions = options) { + assertTasksPackedToCache(":app:kaptGenerateStubsKotlin", ":app:kaptKotlin") + } - // A cache hit: a clean build without any changes to the project - build(options = options, params = *arrayOf("clean", ":app:build"), projectDir = projectToBeModified) { - assertSuccessful() - assertContains(":app:kaptGenerateStubsKotlin FROM-CACHE") - assertContains(":app:kaptKotlin FROM-CACHE") - } + // A cache hit: a clean build without any changes to the project + secondProject.build("clean", ":app:build", buildOptions = options) { + assertTasksFromCache(":app:kaptGenerateStubsKotlin", ":app:kaptKotlin") + } - // Make changes to annotated class and check kapt tasks are re-executed - File(projectToBeModified, "app/src/main/kotlin/AppClass.kt").modify { - it.replace("val testVal: String = \"text\"", "val testVal: Int = 1") - } - build(options = options, params = *arrayOf("build"), projectDir = projectToBeModified) { - assertSuccessful() - assertContains("':app:kaptGenerateStubsKotlin' is not up-to-date") - assertContains("':app:kaptKotlin' is not up-to-date") - } + // Make changes to annotated class and check kapt tasks are re-executed + val appClassKtSourceFile = secondProject.projectPath.resolve("app/src/main/kotlin/AppClass.kt") + appClassKtSourceFile.modify { + it.replace("val testVal: String = \"text\"", "val testVal: Int = 1") + } + secondProject.build("build", buildOptions = options) { + assertTasksExecuted(":app:kaptGenerateStubsKotlin", ":app:kaptKotlin") + } - // Revert changes and check kapt tasks are from cache - File(projectToBeModified, "app/src/main/kotlin/AppClass.kt").modify { - it.replace("val testVal: Int = 1", "val testVal: String = \"text\"") - } - build(options = options, params = *arrayOf("clean", "build"), projectDir = projectToBeModified) { - assertSuccessful() - assertContains(":app:kaptGenerateStubsKotlin FROM-CACHE") - assertContains(":app:kaptKotlin FROM-CACHE") - } + // Revert changes and check kapt tasks are from cache + appClassKtSourceFile.modify { + it.replace("val testVal: Int = 1", "val testVal: String = \"text\"") + } + secondProject.build("clean", "build", buildOptions = options) { + assertTasksFromCache(":app:kaptGenerateStubsKotlin", ":app:kaptKotlin") } } } - -fun BaseGradleIT.Project.prepareLocalBuildCache(directory: File = File(projectDir.parentFile, "buildCache").apply { mkdir() }): File { - if (!projectDir.exists()) { - setupWorkingDir() - } - File(projectDir, "settings.gradle").appendText("\nbuildCache.local.directory = '${directory.absolutePath.replace("\\", "/")}'") - return directory -} - -internal fun BaseGradleIT.CompiledProject.assertTaskPackedToCache(taskPath: String) { - with(project.testCase) { - assertContainsRegex(Regex("(?:Packing|Stored cache entry for) task '${Regex.escape(taskPath)}'")) - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt index ffaca182b90..092601986b4 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt @@ -206,4 +206,18 @@ class BuildCacheRelocationIT : BaseGradleIT() { .map { it.relativeTo(dir) to it.readBytes().contentHashCode() } .toList() } + + private fun BaseGradleIT.Project.prepareLocalBuildCache(directory: File = File(projectDir.parentFile, "buildCache").apply { mkdir() }): File { + if (!projectDir.exists()) { + setupWorkingDir() + } + File(projectDir, "settings.gradle").appendText("\nbuildCache.local.directory = '${directory.absolutePath.replace("\\", "/")}'") + return directory + } + + private fun CompiledProject.assertTaskPackedToCache(taskPath: String) { + with(project.testCase) { + assertContainsRegex(Regex("(?:Packing|Stored cache entry for) task '${Regex.escape(taskPath)}'")) + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildCacheSimple/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildCacheSimple/build.gradle index 1d23fc46979..466d1c83e8c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildCacheSimple/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/buildCacheSimple/build.gradle @@ -1,21 +1,9 @@ -buildscript { - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'java' } -apply plugin: "kotlin" -apply plugin: 'java' - repositories { mavenLocal() mavenCentral() } - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/app/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/app/build.gradle index 9c3f7de4d94..990882a8745 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/app/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/app/build.gradle @@ -1,9 +1,10 @@ -apply plugin: "kotlin" -apply plugin: "kotlin-kapt" +plugins { + id "org.jetbrains.kotlin.jvm" + id "org.jetbrains.kotlin.kapt" +} dependencies { implementation project(":lib") - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version" kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/build.gradle index 6068289ccdb..45f1eb2cf27 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/build.gradle @@ -1,14 +1,8 @@ -buildscript { - repositories { - mavenLocal() - mavenCentral() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } +plugins { + id "org.jetbrains.kotlin.jvm" apply false + id "org.jetbrains.kotlin.kapt" apply false } - subprojects { repositories { mavenLocal() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/lib/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/lib/build.gradle index e0b0571f046..546933fb729 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/lib/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/lib/build.gradle @@ -1,5 +1,3 @@ -apply plugin: "kotlin" - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" -} \ No newline at end of file +plugins { + id "org.jetbrains.kotlin.jvm" +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/settings.gradle index c0b70ce1c99..1b7aef22c7b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/settings.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/kaptAvoidance/settings.gradle @@ -1 +1,13 @@ +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + } + + plugins { + id "org.jetbrains.kotlin.jvm" version "$kotlin_version" + id "org.jetbrains.kotlin.kapt" version "$kotlin_version" + } +} + include ':app', ':lib' \ No newline at end of file