KT-45777: Clean up build cache tests for incremental compilation
- Clean up BuildCacheRelocationIT.testKotlinIncrementalCompilation - Delete BuildCacheIT.testKotlinCompileIncrementalBuildWithoutRelocation as it is already covered by the test in BuildCacheRelocationIT - Add BuildCacheRelocationIT.testKotlinIncrementalCompilation_withClasspathSnapshot - Updated BuildCacheRelocationIT.testKotlinIncrementalCompilation_withClasspathSnapshot
This commit is contained in:
committed by
nataliya.valtman
parent
200cc9f75f
commit
d018031cbf
@@ -16,6 +16,7 @@ enum class BuildAttributeKind : Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable {
|
enum class BuildAttribute(val kind: BuildAttributeKind, val readableString: String) : Serializable {
|
||||||
|
CACHE_DIRECTORY_NOT_POPULATED(BuildAttributeKind.REBUILD_REASON, "Cache directory not populated"),
|
||||||
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"),
|
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON, "Build history file not found"),
|
||||||
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"),
|
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON, "ABI snapshot not found"),
|
||||||
CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"),
|
CLASSPATH_SNAPSHOT_NOT_FOUND(BuildAttributeKind.REBUILD_REASON, "Classpath snapshot not found"),
|
||||||
|
|||||||
@@ -288,13 +288,13 @@ data class LookupSymbol(val name: String, val scope: String) : Comparable<Lookup
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper of a [LookupMap] which tracks changes to the map after the initialization of this [TrackedLookupMap] instance, (unless
|
* Wrapper of a [LookupMap] which tracks changes to the map after the initialization of this [TrackedLookupMap] instance (unless
|
||||||
* [trackChanges] is set to `false`).
|
* [trackChanges] is set to `false`).
|
||||||
*/
|
*/
|
||||||
private class TrackedLookupMap(private val lookupMap: LookupMap, private val trackChanges: Boolean) {
|
private class TrackedLookupMap(private val lookupMap: LookupMap, private val trackChanges: Boolean) {
|
||||||
|
|
||||||
// Note that there may be multiple operations on the same key, and the following sets contain the *latest* differences with the original
|
// Note that there may be multiple operations on the same key, and the following sets contain the *aggregated* differences with the
|
||||||
// set of keys in the map. For example, if a key is added then removed, or vice versa, it will not be present in either set.
|
// original set of keys in the map. For example, if a key is added then removed, or vice versa, it will not be present in either set.
|
||||||
val addedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
val addedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||||
val removedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
val removedKeys = if (trackChanges) mutableSetOf<LookupSymbolKey>() else null
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -129,7 +129,15 @@ abstract class IncrementalCompilerRunner<
|
|||||||
else -> providedChangedFiles
|
else -> providedChangedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot)
|
// Check whether the cache directory is populated (note that it may be deleted upon a Gradle build cache hit if the directory is
|
||||||
|
// marked as @LocalState in the Gradle task).
|
||||||
|
val cacheDirectoryNotPopulated = cacheDirectory.walk().none { it.isFile }
|
||||||
|
|
||||||
|
val compilationMode = if (cacheDirectoryNotPopulated) {
|
||||||
|
CompilationMode.Rebuild(BuildAttribute.CACHE_DIRECTORY_NOT_POPULATED)
|
||||||
|
} else {
|
||||||
|
sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot)
|
||||||
|
}
|
||||||
|
|
||||||
val exitCode = when (compilationMode) {
|
val exitCode = when (compilationMode) {
|
||||||
is CompilationMode.Incremental -> {
|
is CompilationMode.Incremental -> {
|
||||||
|
|||||||
+1
-1
@@ -256,7 +256,7 @@ class IncrementalJvmCompilerRunner(
|
|||||||
is NotAvailableDueToMissingClasspathSnapshot -> ChangesEither.Unknown(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND)
|
is NotAvailableDueToMissingClasspathSnapshot -> ChangesEither.Unknown(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND)
|
||||||
is NotAvailableForNonIncrementalRun -> ChangesEither.Unknown(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
|
is NotAvailableForNonIncrementalRun -> ChangesEither.Unknown(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
|
||||||
is ClasspathSnapshotDisabled -> reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
|
is ClasspathSnapshotDisabled -> reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
|
||||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild(BuildAttribute.IC_IS_NOT_ENABLED)
|
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
|
||||||
reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" }
|
reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||||
val scopes = caches.lookupCache.lookupSymbols.map { it.scope.ifBlank { it.name } }.distinct()
|
val scopes = caches.lookupCache.lookupSymbols.map { it.scope.ifBlank { it.name } }.distinct()
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1030,8 +1030,7 @@ fun getSomething() = 10
|
|||||||
.filter { it.contains("[KOTLIN] compile iteration:") }
|
.filter { it.contains("[KOTLIN] compile iteration:") }
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.joinToString(separator = "/n")
|
.joinToString(separator = "/n")
|
||||||
val actualSources = getCompiledKotlinSources(filteredOutput).projectRelativePaths(project)
|
assertCompiledKotlinSources(project.relativize(androidModuleKt), output = filteredOutput)
|
||||||
assertSameFiles(project.relativize(androidModuleKt), actualSources, "Compiled Kotlin files differ:\n ")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-47
@@ -11,26 +11,19 @@ import org.gradle.api.logging.configuration.WarningMode
|
|||||||
import org.gradle.tooling.GradleConnector
|
import org.gradle.tooling.GradleConnector
|
||||||
import org.gradle.util.GradleVersion
|
import org.gradle.util.GradleVersion
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
|
||||||
import org.jetbrains.kotlin.gradle.model.ModelContainer
|
import org.jetbrains.kotlin.gradle.model.ModelContainer
|
||||||
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
|
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||||
import org.jetbrains.kotlin.gradle.testbase.applyAndroidTestFixes
|
import org.jetbrains.kotlin.gradle.testbase.*
|
||||||
import org.jetbrains.kotlin.gradle.testbase.enableCacheRedirector
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.extractJavaCompiledSources
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.extractKotlinCompiledSources
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.prettyPrintXml
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.readAndCleanupTestResults
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.addPluginManagementToSettings
|
|
||||||
import org.jetbrains.kotlin.gradle.util.*
|
import org.jetbrains.kotlin.gradle.util.*
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.jetbrains.kotlin.konan.target.HostManager
|
import org.jetbrains.kotlin.konan.target.HostManager
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.test.RunnerWithMuteInDatabase
|
import org.jetbrains.kotlin.test.RunnerWithMuteInDatabase
|
||||||
import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.AfterClass
|
import org.junit.AfterClass
|
||||||
import org.junit.Assert
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -386,14 +379,7 @@ abstract class BaseGradleIT {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompiledProject(val project: Project, val output: String, val resultCode: Int) {
|
class CompiledProject(val project: Project, val output: String, val resultCode: Int)
|
||||||
fun getCompiledKotlinSources(output: String) =
|
|
||||||
extractKotlinCompiledSources(project.projectDir.toPath(), output).map { sourcePath -> sourcePath.toFile() }
|
|
||||||
|
|
||||||
val compiledJavaSources: Iterable<File> by lazy {
|
|
||||||
extractJavaCompiledSources(output).map { sourcePath -> sourcePath.toFile() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basically the same as `Project.build`, tells gradle to wait for debug on 5005 port
|
// Basically the same as `Project.build`, tells gradle to wait for debug on 5005 port
|
||||||
// Faster to type than `project.build("-Dorg.gradle.debug=true")` or `project.build(options = defaultBuildOptions().copy(debug = true))`
|
// Faster to type than `project.build("-Dorg.gradle.debug=true")` or `project.build(options = defaultBuildOptions().copy(debug = true))`
|
||||||
@@ -593,27 +579,6 @@ abstract class BaseGradleIT {
|
|||||||
return map { it.canonicalFile.toRelativeString(project.projectDir) }
|
return map { it.canonicalFile.toRelativeString(project.projectDir) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CompiledProject.assertSameFiles(expected: Iterable<String>, actual: Iterable<String>, messagePrefix: String): CompiledProject {
|
|
||||||
val expectedSet = expected.map { it.normalizePath() }.toSortedSet().joinToString("\n")
|
|
||||||
val actualSet = actual.map { it.normalizePath() }.toSortedSet().joinToString("\n")
|
|
||||||
Assert.assertEquals(messagePrefix, expectedSet, actualSet)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun CompiledProject.assertContainFiles(
|
|
||||||
expected: Iterable<String>,
|
|
||||||
actual: Iterable<String>,
|
|
||||||
messagePrefix: String = ""
|
|
||||||
): CompiledProject {
|
|
||||||
val expectedNormalized = expected.map(::normalizePath).toSortedSet()
|
|
||||||
val actualNormalized = actual.map(::normalizePath).toSortedSet()
|
|
||||||
assertTrue(
|
|
||||||
actualNormalized.containsAll(expectedNormalized),
|
|
||||||
messagePrefix + "expected files: ${expectedNormalized.joinToString()}\n !in actual files: ${actualNormalized.joinToString()}"
|
|
||||||
)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun CompiledProject.findTasksByPattern(pattern: String): Set<String> {
|
fun CompiledProject.findTasksByPattern(pattern: String): Set<String> {
|
||||||
return "task '($pattern)'".toRegex().findAll(output).mapTo(HashSet()) { it.groupValues[1] }
|
return "task '($pattern)'".toRegex().findAll(output).mapTo(HashSet()) { it.groupValues[1] }
|
||||||
}
|
}
|
||||||
@@ -778,13 +743,14 @@ Finished executing task ':$taskName'|
|
|||||||
output: String = this.output,
|
output: String = this.output,
|
||||||
suffix: String = ""
|
suffix: String = ""
|
||||||
): CompiledProject {
|
): CompiledProject {
|
||||||
val messagePrefix = "Compiled Kotlin files differ${suffix}:\n "
|
val messagePrefix = "Compiled Kotlin files differ${suffix}:\n"
|
||||||
val actualSources = getCompiledKotlinSources(output).projectRelativePaths(this.project)
|
val actualSources = extractCompiledKotlinFiles(output)
|
||||||
return if (weakTesting) {
|
if (weakTesting) {
|
||||||
assertContainFiles(expectedSourcesRelativePaths, actualSources, messagePrefix)
|
assertContainsFiles(expectedSourcesRelativePaths.toPaths(), actualSources, messagePrefix)
|
||||||
} else {
|
} else {
|
||||||
assertSameFiles(expectedSourcesRelativePaths, actualSources, messagePrefix)
|
assertSameFiles(expectedSourcesRelativePaths.toPaths(), actualSources, messagePrefix)
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CompiledProject.assertCompiledKotlinFiles(expectedFiles: Iterable<File>): CompiledProject =
|
fun CompiledProject.assertCompiledKotlinFiles(expectedFiles: Iterable<File>): CompiledProject =
|
||||||
@@ -799,11 +765,15 @@ Finished executing task ':$taskName'|
|
|||||||
fun CompiledProject.assertCompiledJavaSources(
|
fun CompiledProject.assertCompiledJavaSources(
|
||||||
sources: Iterable<String>,
|
sources: Iterable<String>,
|
||||||
weakTesting: Boolean = false
|
weakTesting: Boolean = false
|
||||||
): CompiledProject =
|
): CompiledProject {
|
||||||
|
val messagePrefix = "Compiled Java files differ:\n"
|
||||||
|
val actualSources = extractCompiledJavaFiles(project.projectDir, output)
|
||||||
if (weakTesting)
|
if (weakTesting)
|
||||||
assertContainFiles(sources, compiledJavaSources.projectRelativePaths(this.project), "Compiled Java files differ:\n ")
|
assertContainsFiles(sources.toPaths(), actualSources, messagePrefix)
|
||||||
else
|
else
|
||||||
assertSameFiles(sources, compiledJavaSources.projectRelativePaths(this.project), "Compiled Java files differ:\n ")
|
assertSameFiles(sources.toPaths(), actualSources, messagePrefix)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun Project.resourcesDir(subproject: String? = null, sourceSet: String = "main"): String =
|
fun Project.resourcesDir(subproject: String? = null, sourceSet: String = "main"): String =
|
||||||
(subproject?.plus("/") ?: "") + "build/resources/$sourceSet/"
|
(subproject?.plus("/") ?: "") + "build/resources/$sourceSet/"
|
||||||
@@ -904,7 +874,7 @@ Finished executing task ':$taskName'|
|
|||||||
options.incrementalJsKlib?.let { add("-Pkotlin.incremental.js.klib=$it") }
|
options.incrementalJsKlib?.let { add("-Pkotlin.incremental.js.klib=$it") }
|
||||||
options.jsIrBackend?.let { add("-Pkotlin.js.useIrBackend=$it") }
|
options.jsIrBackend?.let { add("-Pkotlin.js.useIrBackend=$it") }
|
||||||
options.usePreciseJavaTracking?.let { add("-Pkotlin.incremental.usePreciseJavaTracking=$it") }
|
options.usePreciseJavaTracking?.let { add("-Pkotlin.incremental.usePreciseJavaTracking=$it") }
|
||||||
options.useClasspathSnapshot?.let { add("-Pkotlin.incremental.useClasspathSnapshot=$it") }
|
options.useClasspathSnapshot?.let { add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") }
|
||||||
options.androidGradlePluginVersion?.let { add("-Pandroid_tools_version=$it") }
|
options.androidGradlePluginVersion?.let { add("-Pandroid_tools_version=$it") }
|
||||||
if (options.debug) {
|
if (options.debug) {
|
||||||
add("-Dorg.gradle.debug=true")
|
add("-Dorg.gradle.debug=true")
|
||||||
|
|||||||
-27
@@ -103,33 +103,6 @@ class BuildCacheIT : KGPBaseTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DisplayName("Incremental compilation works with cache")
|
|
||||||
@GradleTest
|
|
||||||
fun testKotlinCompileIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) {
|
|
||||||
project("buildCacheSimple", gradleVersion) {
|
|
||||||
enableLocalBuildCache(localBuildCacheDir)
|
|
||||||
|
|
||||||
build("assemble") {
|
|
||||||
assertTasksPackedToCache(":compileKotlin")
|
|
||||||
}
|
|
||||||
|
|
||||||
build("clean", "assemble") {
|
|
||||||
assertTasksFromCache(":compileKotlin")
|
|
||||||
}
|
|
||||||
|
|
||||||
val fooKtSourceFile = kotlinSourcesDir().resolve("foo.kt")
|
|
||||||
fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") }
|
|
||||||
build("assemble") {
|
|
||||||
assertIncrementalCompilation(modifiedFiles = setOf(fooKtSourceFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
fooKtSourceFile.modify { it.replace("String = \"abc\"", "Int = 1") }
|
|
||||||
build("clean", "assemble") {
|
|
||||||
assertTasksFromCache(":compileKotlin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DisplayName("Debug log level should not break build cache")
|
@DisplayName("Debug log level should not break build cache")
|
||||||
@GradleTest
|
@GradleTest
|
||||||
fun testDebugLogLevelCaching(gradleVersion: GradleVersion) {
|
fun testDebugLogLevelCaching(gradleVersion: GradleVersion) {
|
||||||
|
|||||||
+23
-20
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.logging.LogLevel
|
||||||
import org.gradle.api.logging.configuration.WarningMode
|
import org.gradle.api.logging.configuration.WarningMode
|
||||||
import org.gradle.util.GradleVersion
|
import org.gradle.util.GradleVersion
|
||||||
import org.jetbrains.kotlin.gradle.testbase.*
|
import org.jetbrains.kotlin.gradle.testbase.*
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import kotlin.io.path.createDirectory
|
import kotlin.io.path.createDirectory
|
||||||
|
import kotlin.io.path.pathString
|
||||||
|
|
||||||
@DisplayName("Build cache relocation")
|
@DisplayName("Build cache relocation")
|
||||||
@SimpleGradlePluginTests
|
@SimpleGradlePluginTests
|
||||||
@@ -260,20 +262,6 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@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)
|
|
||||||
}
|
|
||||||
|
|
||||||
@DisplayName("Kapt incremental compilation works with cache")
|
@DisplayName("Kapt incremental compilation works with cache")
|
||||||
@GradleTest
|
@GradleTest
|
||||||
fun testKaptCachingIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) {
|
fun testKaptCachingIncrementalBuildWithoutRelocation(gradleVersion: GradleVersion) {
|
||||||
@@ -332,10 +320,22 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkKotlinCompileCachingIncrementalBuild(
|
@DisplayName("Kotlin incremental compilation should work correctly")
|
||||||
firstProject: TestProject,
|
@GradleTest
|
||||||
secondProject: TestProject
|
fun testKotlinIncrementalCompilation(gradleVersion: GradleVersion) {
|
||||||
) {
|
checkKotlinIncrementalCompilation(gradleVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Kotlin incremental compilation with `kotlin.incremental.useClasspathSnapshot` feature should work correctly")
|
||||||
|
@GradleTest
|
||||||
|
fun testKotlinIncrementalCompilation_withClasspathSnapshot(gradleVersion: GradleVersion) {
|
||||||
|
checkKotlinIncrementalCompilation(gradleVersion, useClasspathSnapshot = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkKotlinIncrementalCompilation(gradleVersion: GradleVersion, useClasspathSnapshot: Boolean? = null) {
|
||||||
|
val buildOptions = defaultBuildOptions.copy(useClasspathSnapshot = useClasspathSnapshot)
|
||||||
|
val (firstProject, secondProject) = prepareTestProjects("buildCacheSimple", gradleVersion, buildOptions)
|
||||||
|
|
||||||
// First build, should be stored into the build cache:
|
// First build, should be stored into the build cache:
|
||||||
firstProject.build("assemble") {
|
firstProject.build("assemble") {
|
||||||
assertTasksPackedToCache(":compileKotlin")
|
assertTasksPackedToCache(":compileKotlin")
|
||||||
@@ -348,9 +348,12 @@ class BuildCacheRelocationIT : KGPBaseTest() {
|
|||||||
|
|
||||||
// Change the return type of foo() from Int to String in foo.kt, and check that fooUsage.kt is recompiled as well:
|
// 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.kotlinSourcesDir().resolve("foo.kt")
|
val fooKtSourceFile = secondProject.kotlinSourcesDir().resolve("foo.kt")
|
||||||
|
val fooUsageKtSourceFile = secondProject.kotlinSourcesDir().resolve("fooUsage.kt")
|
||||||
fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") }
|
fooKtSourceFile.modify { it.replace("Int = 1", "String = \"abc\"") }
|
||||||
secondProject.build("assemble") {
|
secondProject.build("assemble", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
|
||||||
assertIncrementalCompilation(modifiedFiles = setOf(fooKtSourceFile))
|
assertIncrementalCompilation(
|
||||||
|
listOf(fooKtSourceFile, fooUsageKtSourceFile).relativizeTo(secondProject.projectPath).map { it.pathString }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revert the change to the return type of foo(), and check if we get a cache hit
|
// Revert the change to the return type of foo(), and check if we get a cache hit
|
||||||
|
|||||||
+4
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.testbase
|
|||||||
import org.gradle.api.logging.LogLevel
|
import org.gradle.api.logging.LogLevel
|
||||||
import org.gradle.api.logging.configuration.WarningMode
|
import org.gradle.api.logging.configuration.WarningMode
|
||||||
import org.gradle.util.GradleVersion
|
import org.gradle.util.GradleVersion
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM
|
||||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||||
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
import org.jetbrains.kotlin.gradle.report.BuildReportType
|
||||||
@@ -21,6 +22,7 @@ data class BuildOptions(
|
|||||||
val configurationCacheProblems: BaseGradleIT.ConfigurationCacheProblems = BaseGradleIT.ConfigurationCacheProblems.FAIL,
|
val configurationCacheProblems: BaseGradleIT.ConfigurationCacheProblems = BaseGradleIT.ConfigurationCacheProblems.FAIL,
|
||||||
val parallel: Boolean = true,
|
val parallel: Boolean = true,
|
||||||
val incremental: Boolean? = null,
|
val incremental: Boolean? = null,
|
||||||
|
val useClasspathSnapshot: Boolean? = null,
|
||||||
val maxWorkers: Int = (Runtime.getRuntime().availableProcessors() / 4 - 1).coerceAtLeast(2),
|
val maxWorkers: Int = (Runtime.getRuntime().availableProcessors() / 4 - 1).coerceAtLeast(2),
|
||||||
val fileSystemWatchEnabled: Boolean = false,
|
val fileSystemWatchEnabled: Boolean = false,
|
||||||
val buildCacheEnabled: Boolean = false,
|
val buildCacheEnabled: Boolean = false,
|
||||||
@@ -81,6 +83,8 @@ data class BuildOptions(
|
|||||||
arguments.add("-Pkotlin.incremental=$incremental")
|
arguments.add("-Pkotlin.incremental=$incremental")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useClasspathSnapshot?.let { arguments.add("-P${COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM.property}=$it") }
|
||||||
|
|
||||||
if (gradleVersion >= GradleVersion.version("6.5")) {
|
if (gradleVersion >= GradleVersion.version("6.5")) {
|
||||||
if (fileSystemWatchEnabled) {
|
if (fileSystemWatchEnabled) {
|
||||||
arguments.add("--watch-fs")
|
arguments.add("--watch-fs")
|
||||||
|
|||||||
+42
-29
@@ -5,14 +5,16 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.testbase
|
package org.jetbrains.kotlin.gradle.testbase
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import kotlin.io.path.relativeTo
|
|
||||||
import org.gradle.api.logging.LogLevel
|
import org.gradle.api.logging.LogLevel
|
||||||
import org.gradle.testkit.runner.BuildResult
|
import org.gradle.testkit.runner.BuildResult
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
private val kotlinSrcRegex by lazy { Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)") }
|
private val kotlinSrcRegex by lazy { Regex("\\[KOTLIN] compile iteration: ([^\\r\\n]*)") }
|
||||||
|
|
||||||
|
private val javaSrcRegex by lazy { Regex("\\[DEBUG] \\[[^]]*JavaCompiler] Compiler arguments: ([^\\r\\n]*)") }
|
||||||
|
|
||||||
@Language("RegExp")
|
@Language("RegExp")
|
||||||
private fun taskOutputRegex(
|
private fun taskOutputRegex(
|
||||||
@@ -42,45 +44,56 @@ fun BuildResult.getOutputForTask(taskName: String): String = taskOutputRegex(tas
|
|||||||
?: error("Could not find output for task $taskName")
|
?: error("Could not find output for task $taskName")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts list of compiled .kt files from task output
|
* Extracts the list of compiled .kt files from the build output.
|
||||||
*
|
*
|
||||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
* The returned paths are relative to the project directory.
|
||||||
|
*
|
||||||
|
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||||
*/
|
*/
|
||||||
fun extractKotlinCompiledSources(projectDirectory: Path, output: String) =
|
fun extractCompiledKotlinFiles(output: String): List<Path> {
|
||||||
kotlinSrcRegex.findAll(output)
|
return kotlinSrcRegex.findAll(output).asIterable()
|
||||||
.asIterable()
|
.flatMap { matchResult -> matchResult.groups[1]!!.value.split(", ") }
|
||||||
.flatMap { result ->
|
.toPaths()
|
||||||
result.groups[1]!!.value.split(", ")
|
}
|
||||||
.map { source -> projectDirectory.resolve(source).normalize() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val javaSrcRegex by lazy { Regex("\\[DEBUG\\] \\[[^\\]]*JavaCompiler\\] Compiler arguments: ([^\\r\\n]*)") }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts list of compiled .java files from task output
|
* Extracts the list of compiled .java files from the build output.
|
||||||
*
|
*
|
||||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
* The returned paths are relative to the project directory.
|
||||||
|
*
|
||||||
|
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||||
*/
|
*/
|
||||||
fun extractJavaCompiledSources(output: String): List<Path> =
|
fun extractCompiledJavaFiles(projectDir: File, output: String): List<Path> {
|
||||||
javaSrcRegex.findAll(output).asIterable().flatMap {
|
return javaSrcRegex.findAll(output).asIterable()
|
||||||
it.groups[1]!!.value
|
.flatMap { matchResult -> matchResult.groups[1]!!.value.split(" ") }
|
||||||
.split(" ")
|
.filter { filePath -> filePath.endsWith(".java", ignoreCase = true) }
|
||||||
.filter { source -> source.endsWith(".java", ignoreCase = true) }
|
.map { javaFilePath -> projectDir.toPath().relativize(Paths.get(javaFilePath)) }
|
||||||
.map { source -> Paths.get(source).normalize() }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts all the .kt files from [expectedSources] and only they are compiled
|
* Asserts all the .kt files from [expectedSources] and only they are compiled
|
||||||
*
|
*
|
||||||
* Note: log level of output should be set to [LogLevel.DEBUG]
|
* Note: log level of output should be set to [LogLevel.DEBUG]
|
||||||
*/
|
*/
|
||||||
fun GradleProject.assertCompiledKotlinSources(
|
fun assertCompiledKotlinSources(
|
||||||
expectedSources: Iterable<Path>,
|
expectedSources: Iterable<Path>,
|
||||||
output: String,
|
output: String,
|
||||||
errorMessageSuffix: String = ""
|
errorMessageSuffix: String = ""
|
||||||
) {
|
) {
|
||||||
val actualSources = extractKotlinCompiledSources(projectPath, output).map {
|
val actualSources = extractCompiledKotlinFiles(output)
|
||||||
it.relativeTo(projectPath)
|
|
||||||
}
|
|
||||||
assertSameFiles(expectedSources, actualSources, "Compiled Kotlin files differ${errorMessageSuffix}:\n")
|
assertSameFiles(expectedSources, actualSources, "Compiled Kotlin files differ${errorMessageSuffix}:\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that compilation was incremental and the set of compiled .kt files exactly match [expectedCompiledKotlinFiles].
|
||||||
|
*
|
||||||
|
* [expectedCompiledKotlinFiles] are relative to the project directory.
|
||||||
|
*
|
||||||
|
* Note: Log level of output must be set to [LogLevel.DEBUG].
|
||||||
|
*/
|
||||||
|
fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable<String>) {
|
||||||
|
assertOutputDoesNotContain("Non-incremental compilation will be performed")
|
||||||
|
|
||||||
|
val actualCompiledKotlinFiles = extractCompiledKotlinFiles(output)
|
||||||
|
assertSameFiles(expectedCompiledKotlinFiles.toPaths(), actualCompiledKotlinFiles, "Compiled Kotlin files differ:\n")
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ fun TestProject.assertSimpleConfigurationCacheScenarioWorks(
|
|||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalPathApi::class)
|
@OptIn(ExperimentalPathApi::class)
|
||||||
private fun copyReportToTempDir(htmlReportFile: Path): Path =
|
private fun copyReportToTempDir(htmlReportFile: Path): Path =
|
||||||
createTempDir("report").let { tempDir ->
|
createTempDirDeleteOnExit("report").let { tempDir ->
|
||||||
htmlReportFile.parent.toFile().copyRecursively(tempDir.toFile())
|
htmlReportFile.parent.toFile().copyRecursively(tempDir.toFile())
|
||||||
tempDir.resolve(htmlReportFile.name)
|
tempDir.resolve(htmlReportFile.name)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-6
@@ -10,7 +10,7 @@ import java.nio.file.Path
|
|||||||
import kotlin.io.path.exists
|
import kotlin.io.path.exists
|
||||||
import kotlin.io.path.isDirectory
|
import kotlin.io.path.isDirectory
|
||||||
import kotlin.io.path.readText
|
import kotlin.io.path.readText
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.asserter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts file under [file] path exists and is a regular file.
|
* Asserts file under [file] path exists and is a regular file.
|
||||||
@@ -189,8 +189,24 @@ fun GradleProject.assertFileDoesNotContain(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun GradleProject.assertSameFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
fun assertSameFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
||||||
val expectedSet = expected.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n")
|
val expectedSet = expected.map { it.toString().normalizePath() }.toSet()
|
||||||
val actualSet = actual.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n")
|
val actualSet = actual.map { it.toString().normalizePath() }.toSet()
|
||||||
assertEquals(expectedSet, actualSet, messagePrefix)
|
asserter.assertTrue(lazyMessage = {
|
||||||
}
|
messagePrefix +
|
||||||
|
"Actual set does not exactly match expected set.\n" +
|
||||||
|
"Expected set: ${expectedSet.sorted().joinToString(", ")}\n" +
|
||||||
|
"Actual set: ${actualSet.sorted().joinToString(", ")}\n"
|
||||||
|
}, actualSet.size == expectedSet.size && actualSet.containsAll(expectedSet))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertContainsFiles(expected: Iterable<Path>, actual: Iterable<Path>, messagePrefix: String) {
|
||||||
|
val expectedSet = expected.map { it.toString().normalizePath() }.toSet()
|
||||||
|
val actualSet = actual.map { it.toString().normalizePath() }.toSet()
|
||||||
|
asserter.assertTrue(lazyMessage = {
|
||||||
|
messagePrefix +
|
||||||
|
"Actual set does not contain all of expected set.\n" +
|
||||||
|
"Expected set: ${expectedSet.sorted().joinToString(", ")}\n" +
|
||||||
|
"Actual set: ${actualSet.sorted().joinToString(", ")}\n"
|
||||||
|
}, actualSet.containsAll(expectedSet))
|
||||||
|
}
|
||||||
|
|||||||
-65
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.gradle.testbase
|
package org.jetbrains.kotlin.gradle.testbase
|
||||||
|
|
||||||
import org.gradle.testkit.runner.BuildResult
|
import org.gradle.testkit.runner.BuildResult
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts Gradle output contains [expectedSubString] string.
|
* Asserts Gradle output contains [expectedSubString] string.
|
||||||
@@ -127,70 +126,6 @@ fun BuildResult.assertNoBuildWarnings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BuildResult.assertIncrementalCompilation(
|
|
||||||
modifiedFiles: Set<Path> = emptySet(),
|
|
||||||
deletedFiles: Set<Path> = emptySet()
|
|
||||||
) {
|
|
||||||
val incrementalCompilationOptions = output
|
|
||||||
.lineSequence()
|
|
||||||
.filter { it.trim().startsWith("Options for KOTLIN DAEMON: IncrementalCompilationOptions(") }
|
|
||||||
.map {
|
|
||||||
it
|
|
||||||
.removePrefix("Options for KOTLIN DAEMON: IncrementalCompilationOptions(")
|
|
||||||
.removeSuffix(")")
|
|
||||||
}
|
|
||||||
.toList()
|
|
||||||
|
|
||||||
assert(incrementalCompilationOptions.isNotEmpty()) {
|
|
||||||
printBuildOutput()
|
|
||||||
"No incremental compilation options were found in the build"
|
|
||||||
}
|
|
||||||
|
|
||||||
val modifiedFilesPath = modifiedFiles.map { it.toAbsolutePath().toString() }
|
|
||||||
val deletedFilesPath = deletedFiles.map { it.toAbsolutePath().toString() }
|
|
||||||
val hasMatch = incrementalCompilationOptions
|
|
||||||
.firstOrNull {
|
|
||||||
val optionModifiedFiles = it
|
|
||||||
.substringAfter("modifiedFiles=[")
|
|
||||||
.substringBefore("]")
|
|
||||||
.split(",")
|
|
||||||
.filter(String::isNotEmpty)
|
|
||||||
|
|
||||||
val modifiedFilesFound = if (modifiedFilesPath.isEmpty()) {
|
|
||||||
optionModifiedFiles.isEmpty()
|
|
||||||
} else {
|
|
||||||
modifiedFilesPath.subtract(optionModifiedFiles).isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
val optionDeletedFiles = it
|
|
||||||
.substringAfter("deletedFiles=[")
|
|
||||||
.substringBefore("]")
|
|
||||||
.split(",")
|
|
||||||
.filter(String::isNotEmpty)
|
|
||||||
|
|
||||||
val deletedFilesFound = if (deletedFilesPath.isEmpty()) {
|
|
||||||
optionDeletedFiles.isEmpty()
|
|
||||||
} else {
|
|
||||||
deletedFilesPath.subtract(optionDeletedFiles).isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiedFilesFound && deletedFilesFound
|
|
||||||
} != null
|
|
||||||
|
|
||||||
assert(hasMatch) {
|
|
||||||
printBuildOutput()
|
|
||||||
|
|
||||||
"""
|
|
||||||
|Expected incremental compilation options with:
|
|
||||||
|- modified files: ${modifiedFilesPath.joinToString()}
|
|
||||||
|- deleted files: ${deletedFilesPath.joinToString()}
|
|
||||||
|
|
|
||||||
|but none of following compilation options match:
|
|
||||||
|${incrementalCompilationOptions.joinToString(separator = "\n")}
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts compilation is running via Kotlin daemon with given jvm arguments.
|
* Asserts compilation is running via Kotlin daemon with given jvm arguments.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+4
-5
@@ -6,10 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.gradle.testbase
|
package org.jetbrains.kotlin.gradle.testbase
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.FileVisitResult
|
import java.nio.file.*
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.SimpleFileVisitor
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.*
|
||||||
import kotlin.streams.asSequence
|
import kotlin.streams.asSequence
|
||||||
@@ -50,7 +47,7 @@ internal val Path.allJavaSources: List<Path>
|
|||||||
*
|
*
|
||||||
* Prefer using JUnit5 `@TempDir` over this method when possible.
|
* Prefer using JUnit5 `@TempDir` over this method when possible.
|
||||||
*/
|
*/
|
||||||
internal fun createTempDir(prefix: String): Path = Files
|
internal fun createTempDirDeleteOnExit(prefix: String): Path = Files
|
||||||
.createTempDirectory(prefix)
|
.createTempDirectory(prefix)
|
||||||
.apply { toFile().deleteOnExit() }
|
.apply { toFile().deleteOnExit() }
|
||||||
|
|
||||||
@@ -108,3 +105,5 @@ internal fun Path.deleteRecursively() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Iterable<String>.toPaths(): List<Path> = map { Paths.get(it) }
|
||||||
|
|||||||
+2
-7
@@ -5,10 +5,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.util
|
package org.jetbrains.kotlin.gradle.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.gradle.testbase.createTempDirDeleteOnExit
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.relativeTo
|
|
||||||
|
|
||||||
fun File.getFileByName(name: String): File =
|
fun File.getFileByName(name: String): File =
|
||||||
findFileByName(name) ?: throw AssertionError("Could not find file with name '$name' in $this")
|
findFileByName(name) ?: throw AssertionError("Could not find file with name '$name' in $this")
|
||||||
@@ -36,10 +34,7 @@ fun File.addNewLine() {
|
|||||||
modify { "$it\n" }
|
modify { "$it\n" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTempDir(prefix: String): File =
|
fun createTempDir(prefix: String): File = createTempDirDeleteOnExit(prefix).toFile()
|
||||||
Files.createTempDirectory(prefix).toFile().apply {
|
|
||||||
deleteOnExit()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* converts back slashes to forward slashes
|
* converts back slashes to forward slashes
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// This class is used so that the set of compiled source files in an incremental compilation is a proper subset of the set of compiled
|
||||||
|
// source files in a non-incremental compilation, so that the two cases are not ambiguous when we check the set of compiled source files.
|
||||||
|
class StandAloneClass
|
||||||
Reference in New Issue
Block a user