From 203b7a4da3e29186d01099f4657236f5545daa84 Mon Sep 17 00:00:00 2001 From: Evgenii Mazhukin Date: Mon, 9 Oct 2023 11:23:02 +0000 Subject: [PATCH] [KGP] Treat friendPaths as non-incremental Input property When friendPaths change, internal symbols from other modules become hidden or visible. This might change which functions are called, which types are returned, etc. So we should recompile the affected classes. Gradle doesn't allow Input policy on FileCollection properties, so we introduce friendPathsSet. It allows us to keep the ABI of BaseKotlinCompile unchanged, detect non-incremental configuration changes on Gradle side, and use normal incremental compilation for everything else. ^KT-61918 Fixed Merge-request: KT-MR-12372 Merged-by: Evgenii Mazhukin --- .../kotlin/gradle/AssociatedCompilationIT.kt | 93 +++++++++++++++++++ .../build.gradle | 23 +++++ .../src/integrationTest/kotlin/test.kt | 1 + .../src/main/kotlin/util.kt | 1 + .../gradle/tasks/AbstractKotlinCompile.kt | 10 ++ 5 files changed, 128 insertions(+) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AssociatedCompilationIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/integrationTest/kotlin/test.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/main/kotlin/util.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AssociatedCompilationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AssociatedCompilationIT.kt new file mode 100644 index 00000000000..28ebce8bfe7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AssociatedCompilationIT.kt @@ -0,0 +1,93 @@ +/* + * 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.gradle + +import org.gradle.api.logging.LogLevel +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.replaceFirst +import org.jetbrains.kotlin.gradle.util.replaceText +import org.junit.jupiter.api.DisplayName + +@DisplayName("Incremental scenarios with associated compilation") +class AssociatedCompilationIT : KGPBaseTest() { + override val defaultBuildOptions: BuildOptions + get() = super.defaultBuildOptions.copy(logLevel = LogLevel.DEBUG) + + @JvmGradlePluginTests + @DisplayName("Rebuild forced if associated compilation changes") + @GradleTest + fun testRebuildOnAssociatedCompilationChange(gradleVersion: GradleVersion) { + val project = getBaseProject(gradleVersion) + + val testSrc = project.kotlinSourcesDir("integrationTest").resolve("test.kt") + val utilSrc = project.kotlinSourcesDir("main").resolve("util.kt") + + project.build("compileIntegrationTestKotlin") { + assertCompiledKotlinSources(listOf(testSrc, utilSrc).relativizeTo(project.projectPath), output) + } + + project.buildGradle.replaceFirst("associateWith(getByName(\"main\"))", "") + + project.buildAndFail("compileIntegrationTestKotlin") { + assertTasksFailed(":compileIntegrationTestKotlin") + assertOutputContains("Cannot access 'fun doWork(): Int': it is internal in file") + } + } + + @JvmGradlePluginTests + @DisplayName("Rebuild after used internal ABI change in associated compilation") + @GradleTest + fun testRebuildOnUsedApiChangeInAssociatedCompilation(gradleVersion: GradleVersion) { + val project = getBaseProject(gradleVersion) + + val testSrc = project.kotlinSourcesDir("integrationTest").resolve("test.kt") + val utilSrc = project.kotlinSourcesDir("main").resolve("util.kt") + + project.build("compileIntegrationTestKotlin") + utilSrc.replaceFirst("fun doWork() = 4", "fun doWork() = \"okay\"") + + project.build("compileIntegrationTestKotlin") { + assertCompiledKotlinSources(listOf(testSrc, utilSrc).relativizeTo(project.projectPath), output) + } + } + + @JvmGradlePluginTests + @DisplayName("No rebuild after private ABI change in associated compilation") + @GradleTest + fun testNoRebuildOnPrivateChangeInAssociatedCompilation(gradleVersion: GradleVersion) { + val project = getBaseProject(gradleVersion) + + val utilSrc = project.kotlinSourcesDir("main").resolve("util.kt") + + project.build("compileIntegrationTestKotlin") + utilSrc.append("private val bar = 2") + + project.build("compileIntegrationTestKotlin") { + assertCompiledKotlinSources(listOf(utilSrc).relativizeTo(project.projectPath), output) + } + } + + @JvmGradlePluginTests + @DisplayName("No rebuild after unused ABI change in associated compilation") + @GradleTest + fun testNoRebuildOnUnusedInternalChangeInAssociatedCompilation(gradleVersion: GradleVersion) { + val project = getBaseProject(gradleVersion) + + val utilSrc = project.kotlinSourcesDir("main").resolve("util.kt") + + project.build("compileIntegrationTestKotlin") + utilSrc.append("internal val bar = 2") + + project.build("compileIntegrationTestKotlin") { + assertCompiledKotlinSources(listOf(utilSrc).relativizeTo(project.projectPath), output) + } + } + + private fun getBaseProject(gradleVersion: GradleVersion): TestProject { + return project("kt-61918-associate-with-incremental", gradleVersion) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/build.gradle new file mode 100644 index 00000000000..275f5040253 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/build.gradle @@ -0,0 +1,23 @@ +plugins { + id "org.jetbrains.kotlin.jvm" +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin.target.compilations { + create("integrationTest") { + defaultSourceSet { + dependencies { + implementation(getByName("main").compileDependencyFiles + getByName("main").output.classesDirs) + } + } + } + + //force task instantiation: + tasks.getByName("compileIntegrationTestKotlin") + + named("integrationTest").configure { associateWith(getByName("main")) } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/integrationTest/kotlin/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/integrationTest/kotlin/test.kt new file mode 100644 index 00000000000..7786a948206 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/integrationTest/kotlin/test.kt @@ -0,0 +1 @@ +val result = doWork() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/main/kotlin/util.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/main/kotlin/util.kt new file mode 100644 index 00000000000..c711cf15947 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-61918-associate-with-incremental/src/main/kotlin/util.kt @@ -0,0 +1 @@ +internal fun doWork() = 4 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt index 2f4cd49390b..18a1eb68c95 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt @@ -102,6 +102,16 @@ abstract class AbstractKotlinCompile @Inject constr internal open fun isIncrementalCompilationEnabled(): Boolean = incremental + // This allows us to treat friendPaths as Input rather than InputFiles + @get:Input + internal val friendPathsSet: Set + get() { + val buildDirFile = projectLayout.buildDirectory.asFile.get() + return friendPaths + .filter { it.exists() } + .map { it.normalize().relativeTo(buildDirFile).path }.toSet() + } + @Deprecated("Scheduled for removal with Kotlin 2.0", ReplaceWith("moduleName")) @get:Input abstract val ownModuleName: Property