diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIncrementalCompilationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIncrementalCompilationIT.kt new file mode 100644 index 00000000000..bcdfc5af511 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIncrementalCompilationIT.kt @@ -0,0 +1,141 @@ +/* + * 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.native + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.junit.jupiter.api.DisplayName +import java.nio.file.Path +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +@DisplayName("Tests for K/N incremental compilation") +@NativeGradlePluginTests +class NativeIncrementalCompilationIT : KGPBaseTest() { + + override val defaultBuildOptions = super.defaultBuildOptions.copy( + nativeOptions = BuildOptions.NativeOptions( + cacheKind = NativeCacheKind.STATIC, + incremental = true + ) + ) + + @DisplayName("Smoke test") + @GradleTest + fun checkIncrementalCacheIsCreated(gradleVersion: GradleVersion) { + nativeProject("native-incremental-simple", gradleVersion) { + build("linkDebugExecutableHost") { + assertDirectoryExists( + getFileCache("native-incremental-simple", "src/hostMain/kotlin/main.kt", "") + ) + } + } + } + + @DisplayName("IC works after compilation error (test 1)") + @GradleTest + fun compilationError1(gradleVersion: GradleVersion) { + nativeProject("native-incremental-multifile", gradleVersion) { + var mainKtCacheModified = 0L + var fooKtCacheModified = 0L + val mainKtCache = getFileCache("native-incremental-multifile", "src/hostMain/kotlin/main.kt", "") + val fooKtCache = getFileCache("native-incremental-multifile", "src/hostMain/kotlin/foo.kt", "") + build("linkDebugExecutableHost") { + assertDirectoryExists(mainKtCache) + assertDirectoryExists(fooKtCache) + mainKtCacheModified = mainKtCache.toFile().lastModified() + fooKtCacheModified = fooKtCache.toFile().lastModified() + } + + val fooKt = kotlinSourcesDir("hostMain").resolve("foo.kt") + fooKt.writeText("fun foo(): Int = \"zzz\"") + + buildAndFail("linkDebugExecutableHost") { + assertTasksFailed(":compileKotlinHost") + } + + fooKt.writeText("fun foo(): Int = 42") + + build("linkDebugExecutableHost") { + assertDirectoryExists(mainKtCache) + assertDirectoryExists(fooKtCache) + assertEquals(mainKtCacheModified, mainKtCache.toFile().lastModified()) + assertEquals(fooKtCacheModified, fooKtCache.toFile().lastModified()) + } + } + } + + @DisplayName("IC works after compilation error (test 2)") + @GradleTest + fun compilationError2(gradleVersion: GradleVersion) { + nativeProject("native-incremental-multifile", gradleVersion) { + var mainKtCacheModified = 0L + var fooKtCacheModified = 0L + val mainKtCache = getFileCache("native-incremental-multifile", "src/hostMain/kotlin/main.kt", "") + val fooKtCache = getFileCache("native-incremental-multifile", "src/hostMain/kotlin/foo.kt", "") + build("linkDebugExecutableHost") { + assertDirectoryExists(mainKtCache) + assertDirectoryExists(fooKtCache) + mainKtCacheModified = mainKtCache.toFile().lastModified() + fooKtCacheModified = fooKtCache.toFile().lastModified() + } + + val fooKt = kotlinSourcesDir("hostMain").resolve("foo.kt") + fooKt.writeText("fun foo(): Int = \"zzz\"") + + buildAndFail("linkDebugExecutableHost") { + assertTasksFailed(":compileKotlinHost") + } + + fooKt.writeText("fun foo(): String = \"zzz\"") + + build("linkDebugExecutableHost") { + assertDirectoryExists(mainKtCache) + assertDirectoryExists(fooKtCache) + assertNotEquals(mainKtCacheModified, mainKtCache.toFile().lastModified()) + assertNotEquals(fooKtCacheModified, fooKtCache.toFile().lastModified()) + } + } + } + + @DisplayName("Check dependencies on project level") + @GradleTest + fun inProjectDependencies(gradleVersion: GradleVersion) { + nativeProject("native-incremental-multi-project", gradleVersion, configureSubProjects = true) { + var fooKtCacheModified = 0L + var barKtCacheModified = 0L + var mainKtCacheModified = 0L + val fooKtCache = getFileCache("program", "MultiProject:library", "library/src/hostMain/kotlin/foo.kt", "") + val barKtCache = getFileCache("program", "MultiProject:program", "program/src/hostMain/kotlin/bar.kt", "") + val mainKtCache = getFileCache("program", "MultiProject:program", "program/src/hostMain/kotlin/main.kt", "") + build("linkDebugExecutableHost") { + assertDirectoryExists(fooKtCache) + assertDirectoryExists(barKtCache) + assertDirectoryExists(mainKtCache) + fooKtCacheModified = fooKtCache.toFile().lastModified() + barKtCacheModified = barKtCache.toFile().lastModified() + mainKtCacheModified = mainKtCache.toFile().lastModified() + } + + val fooKt = projectPath.resolve("library/src/hostMain/kotlin").resolve("foo.kt") + fooKt.writeText("fun foo(): Int = 41") + + build("linkDebugExecutableHost") { + assertDirectoryExists(fooKtCache) + assertDirectoryExists(barKtCache) + assertDirectoryExists(mainKtCache) + assertNotEquals(fooKtCacheModified, fooKtCache.toFile().lastModified()) + assertEquals(barKtCacheModified, barKtCache.toFile().lastModified()) + assertNotEquals(mainKtCacheModified, mainKtCache.toFile().lastModified()) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt index 1d8152f3bf8..6d073235538 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt @@ -93,6 +93,7 @@ data class BuildOptions( val useXcodeMessageStyle: Boolean? = null, val version: String? = null, val cacheOrchestration: String? = null, + val incremental: Boolean? = null, ) fun toArguments( @@ -263,6 +264,9 @@ data class BuildOptions( nativeOptions.cacheOrchestration?.let { arguments.add("-Pkotlin.native.cacheOrchestration=${it}") } + nativeOptions.incremental?.let { + arguments.add("-Pkotlin.incremental.native=${it}") + } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/nativeTestHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/nativeTestHelpers.kt index b7054f26756..ca18d92170a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/nativeTestHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/nativeTestHelpers.kt @@ -5,9 +5,11 @@ package org.jetbrains.kotlin.gradle.testbase +import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.presetName +import java.nio.file.Path import java.util.* val DEFAULT_CURRENT_PLATFORM_TARGET_NAME_POSTFIX = HostManager.host.presetName.lowercase(Locale.getDefault()) @@ -88,4 +90,33 @@ internal object MPPNativeTargets { } val supported = listOf("linux64", "macos64", "mingw64").filter { !unsupported.contains(it) } -} \ No newline at end of file +} + +fun computeCacheDirName( + testTarget: KonanTarget, + cacheKind: String, + debuggable: Boolean, + partialLinkageEnabled: Boolean +) = "$testTarget${if (debuggable) "-g" else ""}$cacheKind${if (partialLinkageEnabled) "-pl" else ""}" + +fun TestProject.getFileCache(fileProjectName: String, fileRelativePath: String, fqName: String) = + getFileCache("", fileProjectName, fileRelativePath, fqName) + +fun TestProject.getFileCache( + executableProjectName: String, + fileProjectName: String, + fileRelativePath: String, + fqName: String, +): Path { + val cacheFlavor = computeCacheDirName(HostManager.host, NativeCacheKind.STATIC.name, true, true) + val libCacheDir = getICCacheDir(executableProjectName).resolve(cacheFlavor).resolve("$fileProjectName-per-file-cache") + val fileId = cacheFileId(fqName, projectPath.resolve(fileRelativePath).toFile().canonicalPath) + return libCacheDir.resolve(fileId) +} + +private fun TestProject.getICCacheDir(projectName: String = "") = + (if (projectName == "") projectPath else projectPath.resolve(projectName)) + .resolve("build/kotlin-native-ic-cache") + +private fun cacheFileId(fqName: String, filePath: String) = + "${if (fqName == "") "ROOT" else fqName}.${filePath.hashCode().toString(Character.MAX_RADIX)}" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/build.gradle.kts new file mode 100644 index 00000000000..888e05c3887 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +allprojects { + repositories { + mavenCentral() + mavenLocal() + } +} + +kotlin { + ("host") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/build.gradle.kts new file mode 100644 index 00000000000..cb606735e21 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + ("host") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/src/hostMain/kotlin/foo.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/src/hostMain/kotlin/foo.kt new file mode 100644 index 00000000000..5730b06eda6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/library/src/hostMain/kotlin/foo.kt @@ -0,0 +1 @@ +fun foo(): Int = 42 \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/build.gradle.kts new file mode 100644 index 00000000000..02e6f04c85b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + ("host") { + binaries { + executable { + entryPoint = "main" + } + } + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":library")) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/bar.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/bar.kt new file mode 100644 index 00000000000..c08022952a3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/bar.kt @@ -0,0 +1 @@ +fun bar(): Int = 117 \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/main.kt new file mode 100644 index 00000000000..bb050588c61 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/program/src/hostMain/kotlin/main.kt @@ -0,0 +1,4 @@ + +fun main() { + println(foo() + bar()) +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/settings.gradle.kts new file mode 100644 index 00000000000..41e435ea4f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multi-project/settings.gradle.kts @@ -0,0 +1,3 @@ +rootProject.name = "MultiProject" +include(":library") +include(":program") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/build.gradle.kts new file mode 100644 index 00000000000..bdcd64b735e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + ("host") { + binaries { + executable { + entryPoint = "main" + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/foo.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/foo.kt new file mode 100644 index 00000000000..5730b06eda6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/foo.kt @@ -0,0 +1 @@ +fun foo(): Int = 42 \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/main.kt new file mode 100644 index 00000000000..cb8713b56f4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-multifile/src/hostMain/kotlin/main.kt @@ -0,0 +1,4 @@ + +fun main() { + println(foo()) +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/build.gradle.kts new file mode 100644 index 00000000000..bdcd64b735e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + ("host") { + binaries { + executable { + entryPoint = "main" + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/src/hostMain/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/src/hostMain/kotlin/main.kt new file mode 100644 index 00000000000..f6e36b84c3e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/native-incremental-simple/src/hostMain/kotlin/main.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2022 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. + */ + +fun main() { + println("Hello, Kotlin/Native!") +}