diff --git a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt index afdac00ddf3..5e073dc6395 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt @@ -109,15 +109,17 @@ abstract class AbstractIncrementalCache( override fun markDirty(removedAndCompiledSources: Collection) { for (sourceFile in removedAndCompiledSources) { - val classes = sourceToClassesMap[sourceFile] - classes.forEach { - dirtyOutputClassesMap.markDirty(it) + sourceToClassesMap[sourceFile].forEach { className -> + markDirty(className) } - sourceToClassesMap.clearOutputsForSource(sourceFile) } } + fun markDirty(className: ClassName) { + dirtyOutputClassesMap.markDirty(className) + } + /** * Updates class storage based on the given class proto. * diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt index 8dcfcf0d5c8..7121387fb56 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt @@ -8,16 +8,114 @@ package org.jetbrains.kotlin.gradle.incremental import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.storage.FileToCanonicalPathConverter +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import java.io.File import java.util.* import kotlin.collections.LinkedHashMap /** Computes [ClasspathChanges] between two [ClasspathSnapshot]s .*/ object ClasspathChangesComputer { - fun compute(currentClasspathSnapshot: ClasspathSnapshot, previousClasspathSnapshot: ClasspathSnapshot): ClasspathChanges { - val currentClassSnapshots = currentClasspathSnapshot.getClassSnapshots() - val previousClassSnapshots = previousClasspathSnapshot.getClassSnapshots() + fun compute( + currentClasspathEntrySnapshotFiles: List, + previousClasspathEntrySnapshotFiles: List, + unchangedCurrentClasspathEntrySnapshotFiles: List + ): ClasspathChanges { + // To improve performance, we will compute changes for the changed snapshot files only, ignoring unchanged ones. (Duplicate classes + // will make this a bit tricky, but it will be dealt with below.) + // First, align unchanged snapshot files in the current classpath with unchanged snapshot files in the previous classpath. Gradle + // has this information, but doesn't expose it, so we have to reconstruct it here. + val unchangedCurrentToPreviousAlignment: Map = + alignUnchangedSnapshotFiles(unchangedCurrentClasspathEntrySnapshotFiles, previousClasspathEntrySnapshotFiles) + // Use sets to make presence checks faster + val unchangedCurrentFiles: Set = unchangedCurrentToPreviousAlignment.keys + val unchangedPreviousFiles: Set = unchangedCurrentToPreviousAlignment.values.toSet() + + // We will split the current files into 2 groups: + // 1a) Unchanged current files + // 1b) Added files + // We will split the previous files into 2 groups: + // 2a) Unchanged previous files + // 2b) Removed files + // If the classpath doesn't contain duplicate classes, comparing (1b) with (2b) would be enough. + // However, if the classpath contains duplicate classes, comparing (1b) with (2b) would not be enough. + // Therefore, to deal with duplicate classes while still being able to compare (1b) with (2b), we will find snapshot files in groups + // (1a) and (2a) that have duplicate classes with groups (1b) or (2b) and add them to groups (1b) and (2b). Duplicate classes in + // groups (1b) and (2b) will then be handled in a separate step (see ClasspathChangesComputer.getNonDuplicateClassSnapshots). + val addedFiles: List = currentClasspathEntrySnapshotFiles.filter { it !in unchangedCurrentFiles } + val removedFiles: List = previousClasspathEntrySnapshotFiles.filter { it !in unchangedPreviousFiles } + + val adjustedAddedFiles = addedFiles.toMutableSet() + val adjustedRemovedFiles = removedFiles.toMutableSet() + unchangedCurrentToPreviousAlignment.forEach { (unchangedCurrentFile, unchangedPreviousFile) -> + if (unchangedCurrentFile.containsDuplicatesWith(addedFiles) || unchangedPreviousFile.containsDuplicatesWith(removedFiles)) { + adjustedAddedFiles.add(unchangedCurrentFile) + adjustedRemovedFiles.add(unchangedPreviousFile) + } + } + + // Keep the original order of added/removed files as it is important for the handling of duplicate classes. + val finalAddedFiles: List = currentClasspathEntrySnapshotFiles.filter { it in adjustedAddedFiles } + val finalRemovedFiles: List = previousClasspathEntrySnapshotFiles.filter { it in adjustedRemovedFiles } + + val changedCurrentSnapshot = ClasspathSnapshotSerializer.load(finalAddedFiles) + val changedPreviousSnapshot = ClasspathSnapshotSerializer.load(finalRemovedFiles) + + return compute(changedCurrentSnapshot, changedPreviousSnapshot) + } + + /** + * Maps the unchanged snapshot files of the current build to the unchanged snapshot files of the previous build (selected from all the + * snapshot files of the previous build). + */ + private fun alignUnchangedSnapshotFiles(unchangedCurrentSnapshotFiles: List, previousSnapshotFiles: List): Map { + var index = 0 + return unchangedCurrentSnapshotFiles.associateWith { unchangedCurrentFile -> + var candidates = previousSnapshotFiles.subList(index, previousSnapshotFiles.size).filter { previousFile -> + unchangedCurrentFile.lastModified() == previousFile.lastModified() && unchangedCurrentFile.length() == previousFile.length() + } + if (candidates.size > 1) { + candidates = candidates.filter { candidate -> + unchangedCurrentFile.readBytes().contentEquals(candidate.readBytes()) + } + } + check(candidates.isNotEmpty()) { + "Can't find previous snapshot file of unchanged current snapshot file '${unchangedCurrentFile.path}'" + } + // If there are multiple candidates, select the first one. (It doesn't have to match Gradle's alignment as long as it's still a + // correct alignment.) + val unchangedPreviousFile = candidates.first() + while (previousSnapshotFiles[index] != unchangedPreviousFile) { + index++ + } + index++ + unchangedPreviousFile + } + } + + /** Returns `true` if this snapshot file contains a duplicate class with another snapshot file in the given set. */ + @Suppress("unused", "UNUSED_PARAMETER") + private fun File.containsDuplicatesWith(otherSnapshotFiles: List): Boolean { + // TODO: Implement and optimize this method + // Existing approach (with `kotlin.incremental.useClasspathSnapshot=false`) doesn't seem to handle duplicate classes, so it is + // probably not a regression that we are not handling duplicate classes here yet. + return false + } + + fun compute(currentClasspathSnapshot: ClasspathSnapshot, previousClasspathSnapshot: ClasspathSnapshot): ClasspathChanges { + val currentClassSnapshots = currentClasspathSnapshot.getNonDuplicateClassSnapshots() + val previousClassSnapshots = previousClasspathSnapshot.getNonDuplicateClassSnapshots() + + return compute(currentClassSnapshots, previousClassSnapshots) + } + + /** + * Computes changes between two lists of [ClassSnapshot]s. + * + * Each list must not contain duplicate classes. + */ + fun compute(currentClassSnapshots: List, previousClassSnapshots: List): ClasspathChanges { if (currentClassSnapshots.any { it is ContentHashJavaClassSnapshot } || previousClassSnapshots.any { it is ContentHashJavaClassSnapshot }) { return ClasspathChanges.NotAvailable.UnableToCompute @@ -27,56 +125,83 @@ object ClasspathChangesComputer { FileUtil.createTempDirectory(this::class.java.simpleName, "_WorkingDir_${UUID.randomUUID()}", /* deleteOnExit */ true) val incrementalJvmCache = IncrementalJvmCache(workingDir, /* targetOutputDir */ null, FileToCanonicalPathConverter) - // Store previous class snapshots in incrementalJvmCache, the returned ChangesCollector result is not used. + // Step 1: + // - Add previous class snapshots to incrementalJvmCache. + // - Internally, incrementalJvmCache maintains a set of dirty classes to detect removed classes. Add previous classes to this set + // to detect removed classes later (see step 2). + // - The final ChangesCollector result will contain all symbols in the previous classes (we actually don't need them, but it's + // part of the API's effects). val unusedChangesCollector = ChangesCollector() for (previousSnapshot in previousClassSnapshots) { when (previousSnapshot) { - is KotlinClassSnapshot -> incrementalJvmCache.saveClassToCache( - kotlinClassInfo = previousSnapshot.classInfo, - sourceFiles = null, - changesCollector = unusedChangesCollector - ) - is RegularJavaClassSnapshot -> incrementalJvmCache.saveJavaClassProto( - source = null, - serializedJavaClass = previousSnapshot.serializedJavaClass, - collector = unusedChangesCollector - ) + is KotlinClassSnapshot -> { + incrementalJvmCache.saveClassToCache( + kotlinClassInfo = previousSnapshot.classInfo, + sourceFiles = null, + changesCollector = unusedChangesCollector + ) + incrementalJvmCache.markDirty(previousSnapshot.classInfo.className) + } + is RegularJavaClassSnapshot -> { + incrementalJvmCache.saveJavaClassProto( + source = null, + serializedJavaClass = previousSnapshot.serializedJavaClass, + collector = unusedChangesCollector + ) + incrementalJvmCache.markDirty(JvmClassName.byClassId(previousSnapshot.serializedJavaClass.classId)) + } is EmptyJavaClassSnapshot -> { - // Nothing to process + // Nothing to process as these classes don't impact the result. } is ContentHashJavaClassSnapshot -> { error("Unexpected type (it should have been handled earlier): ${previousSnapshot.javaClass.name}") } } } - // Call the following method even though there are no removed classes, just in case the method updates the state of - // incrementalJvmCache. - incrementalJvmCache.clearCacheForRemovedClasses(unusedChangesCollector) - // Compute changes between the current class snapshots and the previously stored snapshots, and save the result in changesCollector. + // Step 2: + // - Add current class snapshots to incrementalJvmCache. This will overwrite any previous class snapshots that have the same + // `JvmClassName`. The previous class snapshots that are not overwritten will be detected as removed class snapshots and will be + // removed from incrementalJvmCache in step 3. + // - Internally, incrementalJvmCache will mark these classes as non-dirty. That means unchanged/modified classes that were marked + // as dirty in step 1 will now be non-dirty (added classes will also be marked as non-dirty even though they were not marked as + // dirty before). After this, the remaining dirty classes will be removed classes. + // - The intermediate ChangesCollector result will contain all symbols in added classes and changed (added/modified/removed) + // symbols in modified classes. We will collect all symbols in removed classes in step 3. val changesCollector = ChangesCollector() for (currentSnapshot in currentClassSnapshots) { when (currentSnapshot) { - is KotlinClassSnapshot -> incrementalJvmCache.saveClassToCache( - kotlinClassInfo = currentSnapshot.classInfo, - sourceFiles = null, - changesCollector = changesCollector - ) - is RegularJavaClassSnapshot -> incrementalJvmCache.saveJavaClassProto( - source = null, - serializedJavaClass = currentSnapshot.serializedJavaClass, - collector = changesCollector - ) + is KotlinClassSnapshot -> { + incrementalJvmCache.saveClassToCache( + kotlinClassInfo = currentSnapshot.classInfo, + sourceFiles = null, + changesCollector = changesCollector + ) + } + is RegularJavaClassSnapshot -> { + incrementalJvmCache.saveJavaClassProto( + source = null, + serializedJavaClass = currentSnapshot.serializedJavaClass, + collector = changesCollector + ) + } is EmptyJavaClassSnapshot -> { - // Nothing to process + // Nothing to process as these classes don't impact the result. } is ContentHashJavaClassSnapshot -> { error("Unexpected type (it should have been handled earlier): ${currentSnapshot.javaClass.name}") } } } + + // Step 3: + // - Detect removed classes: they are the remaining dirty classes. + // - Remove previous class snapshots of removed classes from incrementalJvmCache. + // - The final ChangesCollector result will contain all symbols in added classes, changed (added/modified/removed) symbols in + // modified classes, and all symbols in removed classes. incrementalJvmCache.clearCacheForRemovedClasses(changesCollector) + // Normalize the changes and clean up val dirtyData = changesCollector.getDirtyData(listOf(incrementalJvmCache), EmptyICReporter) workingDir.deleteRecursively() @@ -86,18 +211,12 @@ object ClasspathChangesComputer { ) } - private fun ClasspathSnapshot.getClassSnapshots(): List { - // If there are duplicate classes on the classpath, retain only the first one to match the compiler's behavior. - // We still need to consider whether to remove duplicate classes based on the class file name or the `ClassId`, as two different - // `ClassId`s can have the same class file name (e.g., nested class `B$C` of top-level class `A` in `first.jar` and nested class `C` - // of top-level class `A$B` in `second.jar` both have the same class file name `A$B$C`). - // - If we use class file name, only `A$B$C` in `first.jar` will be retained. This matches the compiler's behavior because when - // resolving either of those classes, the compiler will only look for `A$B$C` in the first jar, even when the actual class is - // located in the second jar. - // - If we use `ClassId`, both `A$B$C` in `first.jar` and `A$B$C` in `second.jar` will be retained. That means that the snapshot - // of the class in `second.jar` will be considered when computing classpath changes, which is not expected as it doesn't match - // the compiler's behavior. - // Therefore, we will remove duplicate classes based on the class file name, not the `ClassId`. + /** + * Returns all [ClassSnapshot]s in this [ClasspathSnapshot]. + * + * If there are duplicate classes on the classpath, retain only the first one to match the compiler's behavior. + */ + private fun ClasspathSnapshot.getNonDuplicateClassSnapshots(): List { val classSnapshots = LinkedHashMap(classpathEntrySnapshots.sumOf { it.classSnapshots.size }) for (classpathEntrySnapshot in classpathEntrySnapshots) { for ((unixStyleRelativePath, classSnapshot) in classpathEntrySnapshot.classSnapshots) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt index 131779903da..09b663a96ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt @@ -239,8 +239,7 @@ private object DirectoryOrJarContentsReader { * * The map entries are sorted based on their Unix-style relative paths (to ensure deterministic results across filesystems). * - * Note: If a jar has duplicate entries, only one of them will be used (there is no guarantee which one will be used, but the selection - * will be deterministic). + * Note: If a jar has duplicate entries after filtering, only the first one is retained. */ fun read( directoryOrJar: File, @@ -258,31 +257,31 @@ private object DirectoryOrJarContentsReader { directory: File, entryFilter: ((unixStyleRelativePath: String, isDirectory: Boolean) -> Boolean)? = null ): LinkedHashMap { - val relativePathsToContents: MutableList> = mutableListOf() + val relativePathsToContents = mutableMapOf() directory.walk().forEach { file -> val unixStyleRelativePath = file.relativeTo(directory).invariantSeparatorsPath if (entryFilter == null || entryFilter(unixStyleRelativePath, file.isDirectory)) { - relativePathsToContents.add(unixStyleRelativePath to file.readBytes()) + relativePathsToContents[unixStyleRelativePath] = file.readBytes() } } - return relativePathsToContents.sortedBy { it.first }.toMap(LinkedHashMap()) + return relativePathsToContents.toSortedMap().toMap(LinkedHashMap()) } private fun readJar( jarFile: File, entryFilter: ((unixStyleRelativePath: String, isDirectory: Boolean) -> Boolean)? = null ): LinkedHashMap { - val relativePathsToContents: MutableList> = mutableListOf() + val relativePathsToContents = mutableMapOf() ZipInputStream(jarFile.inputStream().buffered()).use { zipInputStream -> while (true) { val entry = zipInputStream.nextEntry ?: break val unixStyleRelativePath = entry.name if (entryFilter == null || entryFilter(unixStyleRelativePath, entry.isDirectory)) { - relativePathsToContents.add(unixStyleRelativePath to zipInputStream.readBytes()) + relativePathsToContents.computeIfAbsent(unixStyleRelativePath) { zipInputStream.readBytes() } } } } - return relativePathsToContents.sortedBy { it.first }.toMap(LinkedHashMap()) + return relativePathsToContents.toSortedMap().toMap(LinkedHashMap()) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/transforms/ClasspathEntrySnapshotTransform.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/transforms/ClasspathEntrySnapshotTransform.kt index 01718f5edb5..672c9270b38 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/transforms/ClasspathEntrySnapshotTransform.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/transforms/ClasspathEntrySnapshotTransform.kt @@ -22,11 +22,9 @@ abstract class ClasspathEntrySnapshotTransform : TransformAction, classpathSnapshotDir: File) { - classpathSnapshotDir.deleteRecursively() - classpathSnapshotDir.mkdirs() - for ((index, file) in classpathSnapshotFiles.withIndex()) { - file.copyTo(File("$classpathSnapshotDir/$index/$CLASSPATH_ENTRY_SNAPSHOT_FILE_NAME"), overwrite = true) + private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges { + val fileChanges = inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).toList() + return if (fileChanges.isEmpty()) { + ClasspathChanges.Available(LinkedHashSet(), LinkedHashSet()) + } else { + val currentClasspathEntrySnapshotFiles = classpathSnapshotProperties.classpathSnapshot.files.toList() + val changedCurrentFiles = fileChanges + .filter { it.changeType == ChangeType.ADDED || it.changeType == ChangeType.MODIFIED } + .map { it.file }.toSet() + ClasspathChangesComputer.compute( + currentClasspathEntrySnapshotFiles = currentClasspathEntrySnapshotFiles, + previousClasspathEntrySnapshotFiles = getPreviousClasspathEntrySnapshotFiles(), + unchangedCurrentClasspathEntrySnapshotFiles = currentClasspathEntrySnapshotFiles.filter { it !in changedCurrentFiles } + ) } } /** - * Returns all classpath snapshot files in the given directory, sorted by their original indices (the subdirectories' names). + * Copies classpath entry snapshot files of the current build to `classpathSnapshotDir`. They will be used in the next build (see + * [getPreviousClasspathEntrySnapshotFiles]). * - * See [copyClasspathSnapshotFilesToDir] for the structure of the classpath snapshot directory. + * To preserve their order, we put them in subdirectories with names being their indices, as shown below: + * classpathSnapshotDir/0/a-snapshot.bin + * classpathSnapshotDir/1/b-snapshot.bin + * ... + * classpathSnapshotDir/N-1/z-snapshot.bin */ - private fun getClasspathSnapshotFilesInDir(classpathSnapshotDir: File): List { - val subDirs = classpathSnapshotDir.listFiles() ?: return emptyList() - return subDirs.toList().sortedBy { it.name.toInt() }.map { File(it, CLASSPATH_ENTRY_SNAPSHOT_FILE_NAME) } + private fun copyCurrentClasspathEntrySnapshotFiles() { + val snapshotFiles = classpathSnapshotProperties.classpathSnapshot.files.toList() + val classpathSnapshotDir = classpathSnapshotProperties.classpathSnapshotDir.get().asFile + classpathSnapshotDir.deleteRecursively() + classpathSnapshotDir.mkdirs() + snapshotFiles.forEachIndexed { index, snapshotFile -> + val targetFile = File("$classpathSnapshotDir/$index/${snapshotFile.name}") + snapshotFile.copyTo(targetFile, overwrite = true) + // Preserve timestamp to find out unchanged files later (see `ClasspathChangesComputer.alignUnchangedSnapshotFiles`) + targetFile.setLastModified(snapshotFile.lastModified()) + } + } + + /** + * Returns classpath entry snapshot files of the previous build, stored in `classpathSnapshotDir` (see + * [copyCurrentClasspathEntrySnapshotFiles]). + */ + private fun getPreviousClasspathEntrySnapshotFiles(): List { + val classpathSnapshotDir = classpathSnapshotProperties.classpathSnapshotDir.get().asFile + val subDirs = classpathSnapshotDir.listFiles()!!.sortedBy { it.name.toInt() } + return subDirs.map { it.listFiles()!!.single() } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt index 0ef5c296d92..0fa7b911ef4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt @@ -5,65 +5,19 @@ package org.jetbrains.kotlin.gradle.incremental +import org.jetbrains.kotlin.gradle.incremental.ClasspathSnapshotTestCommon.* +import org.jetbrains.kotlin.gradle.incremental.ClasspathSnapshotTestCommon.Util.snapshot import org.jetbrains.kotlin.incremental.ClasspathChanges import org.jetbrains.kotlin.incremental.LookupSymbol -import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.sam.SAM_LOOKUP_NAME import org.junit.Assert.assertEquals -import org.junit.Before import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File abstract class ClasspathChangesComputerTest : ClasspathSnapshotTestCommon() { - protected abstract val testSourceFile: ChangeableTestSourceFile - - private lateinit var originalSnapshot: ClassSnapshot - - @Before - fun setUp() { - originalSnapshot = testSourceFile.compileAndSnapshot() - } - - /** Adapted version of [ClasspathChanges.Available] for readability in this test. */ - private data class Changes(private val lookupSymbols: Set, private val fqNames: Set) - - private fun computeClassChanges(current: ClassSnapshot, previous: ClassSnapshot): Changes { - val classChanges = - ClasspathChangesComputer.compute(current.toClasspathSnapshot(), previous.toClasspathSnapshot()) as ClasspathChanges.Available - return Changes(HashSet(classChanges.lookupSymbols), HashSet(classChanges.fqNames)) - } - - private fun ClassSnapshot.toClasspathSnapshot(): ClasspathSnapshot { - return ClasspathSnapshot( - classpathEntrySnapshots = listOf( - ClasspathEntrySnapshot( - LinkedHashMap(1).also { - it[getClassId()!!.getUnixStyleRelativePath()] = this - }) - ) - ) - } - - private fun ClassSnapshot.getClassId(): ClassId? { - return when (this) { - is KotlinClassSnapshot -> classInfo.classId - is RegularJavaClassSnapshot -> serializedJavaClass.classId - is EmptyJavaClassSnapshot, is ContentHashJavaClassSnapshot -> null - } - } - - private fun ClassId.getUnixStyleRelativePath() = asString().replace('.', '$') + ".class" - - /** - * Returns the [FqName] of the class in this source file (e.g., "com/example/Foo$Bar.kt" or - * "com/example/Foo$Bar.java" has [FqName] "com.example.Foo.Bar"). - * - * This source file must contain only 1 class. - */ - private fun SourceFile.getClassFqName() = - FqName(unixStyleRelativePath.substringBeforeLast('.').replace('/', '.').replace('$', '.')) - // TODO Add more test cases: // - private/non-private fields // - inline functions @@ -71,37 +25,173 @@ abstract class ClasspathChangesComputerTest : ClasspathSnapshotTestCommon() { // - adding an annotation @Test - fun testComputeClassChanges_changedPublicMethodSignature() { - val updatedSnapshot = testSourceFile.changePublicMethodSignature().compileAndSnapshot() - val classChanges = computeClassChanges(updatedSnapshot, originalSnapshot) + abstract fun testSingleClass_changePublicMethodSignature() + + @Test + abstract fun testSingleClass_changeMethodImplementation() + + @Test + abstract fun testMultipleClasses() +} + +class KotlinClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { + + @Test + override fun testSingleClass_changePublicMethodSignature() { + val sourceFile = SimpleKotlinClass(tmpDir) + val previousSnapshot = sourceFile.compileAndSnapshot() + val currentSnapshot = sourceFile.changePublicMethodSignature().compileAndSnapshot() + val changes = ClasspathChangesComputer.compute(listOf(currentSnapshot), listOf(previousSnapshot)).normalize() - val testClassFqName = testSourceFile.sourceFile.getClassFqName() assertEquals( Changes( lookupSymbols = setOf( - LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = testClassFqName.asString()), - LookupSymbol(name = "changedPublicMethod", scope = testClassFqName.asString()), - LookupSymbol(name = "publicMethod", scope = testClassFqName.asString()) + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SimpleKotlinClass"), + LookupSymbol(name = "changedPublicMethod", scope = "com.example.SimpleKotlinClass"), + LookupSymbol(name = "publicMethod", scope = "com.example.SimpleKotlinClass") + ), + fqNames = setOf( + FqName("com.example.SimpleKotlinClass") ), - fqNames = setOf(testClassFqName), ), - classChanges + changes ) } @Test - fun testComputeClassChanges_changedMethodImplementation() { - val updatedSnapshot = testSourceFile.changeMethodImplementation().compileAndSnapshot() - val classChanges = computeClassChanges(updatedSnapshot, originalSnapshot) + override fun testSingleClass_changeMethodImplementation() { + val sourceFile = SimpleKotlinClass(tmpDir) + val previousSnapshot = sourceFile.compileAndSnapshot() + val currentSnapshot = sourceFile.changeMethodImplementation().compileAndSnapshot() + val changes = ClasspathChangesComputer.compute(listOf(currentSnapshot), listOf(previousSnapshot)).normalize() - assertEquals(Changes(emptySet(), emptySet()), classChanges) + assertEquals(Changes(emptySet(), emptySet()), changes) + } + + @Test + override fun testMultipleClasses() { + val classpathSourceDir = File(testDataDir, "../ClasspathChangesComputerTest/testMultipleClasses/src/kotlin").canonicalFile + val currentSnapshot = snapshotClasspath(File(classpathSourceDir, "current-classpath"), tmpDir) + val previousSnapshot = snapshotClasspath(File(classpathSourceDir, "previous-classpath"), tmpDir) + val changes = ClasspathChangesComputer.compute(currentSnapshot, previousSnapshot).normalize() + + assertEquals( + Changes( + lookupSymbols = setOf( + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.B"), + LookupSymbol(name = "b2", scope = "com.example.B"), + LookupSymbol(name = "b3", scope = "com.example.B"), + LookupSymbol(name = "b4", scope = "com.example.B"), + LookupSymbol(name = "C", scope = "com.example"), + LookupSymbol(name = "D", scope = "com.example") + ), + fqNames = setOf( + FqName("com.example.B"), + FqName("com.example.C"), + FqName("com.example.D") + ) + ), + changes + ) } } -class KotlinClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { - override val testSourceFile = SimpleKotlinClass(tmpDir) +class JavaClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { + + @Test + override fun testSingleClass_changePublicMethodSignature() { + val sourceFile = SimpleJavaClass(tmpDir) + val previousSnapshot = sourceFile.compileAndSnapshot() + val currentSnapshot = sourceFile.changePublicMethodSignature().compileAndSnapshot() + val changes = ClasspathChangesComputer.compute(listOf(currentSnapshot), listOf(previousSnapshot)).normalize() + + assertEquals( + Changes( + lookupSymbols = setOf( + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SimpleJavaClass"), + LookupSymbol(name = "changedPublicMethod", scope = "com.example.SimpleJavaClass"), + LookupSymbol(name = "publicMethod", scope = "com.example.SimpleJavaClass") + ), + fqNames = setOf( + FqName("com.example.SimpleJavaClass") + ), + ), + changes + ) + } + + @Test + override fun testSingleClass_changeMethodImplementation() { + val sourceFile = SimpleJavaClass(tmpDir) + val previousSnapshot = sourceFile.compileAndSnapshot() + val currentSnapshot = sourceFile.changeMethodImplementation().compileAndSnapshot() + val changes = ClasspathChangesComputer.compute(listOf(currentSnapshot), listOf(previousSnapshot)).normalize() + + assertEquals(Changes(emptySet(), emptySet()), changes) + } + + @Test + override fun testMultipleClasses() { + val classpathSourceDir = File(testDataDir, "../ClasspathChangesComputerTest/testMultipleClasses/src/java").canonicalFile + val currentSnapshot = snapshotClasspath(File(classpathSourceDir, "current-classpath"), tmpDir) + val previousSnapshot = snapshotClasspath(File(classpathSourceDir, "previous-classpath"), tmpDir) + val changes = ClasspathChangesComputer.compute(currentSnapshot, previousSnapshot).normalize() + + assertEquals( + Changes( + lookupSymbols = setOf( + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.B"), + LookupSymbol(name = "b2", scope = "com.example.B"), + LookupSymbol(name = "b3", scope = "com.example.B"), + LookupSymbol(name = "b4", scope = "com.example.B"), + LookupSymbol(name = "C", scope = "com.example"), + LookupSymbol(name = "D", scope = "com.example"), + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.D"), + LookupSymbol(name = "", scope = "com.example.D"), + LookupSymbol(name = "d", scope = "com.example.D") + ), + fqNames = setOf( + FqName("com.example.B"), + FqName("com.example.C"), + FqName("com.example.D") + ) + ), + changes + ) + } } -class JavaClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { - override val testSourceFile = SimpleJavaClass(tmpDir) +private fun snapshotClasspath(classpathSourceDir: File, tmpDir: TemporaryFolder): ClasspathSnapshot { + val classpathEntrySnapshots = classpathSourceDir.listFiles()!!.map { classpathEntrySourceDir -> + val relativePathsInDir = classpathEntrySourceDir.walk() + .filter { it.extension == "kt" || it.extension == "java" } + .map { file -> file.toRelativeString(classpathEntrySourceDir) } + .sortedBy { it } + val sourceFiles = relativePathsInDir.map { relativePath -> + if (relativePath.endsWith(".kt")) { + KotlinSourceFile( + classpathEntrySourceDir, relativePath, + preCompiledClassFile = ClassFile( + File(classpathEntrySourceDir.path.replace("src/kotlin", "classes/kotlin")), + relativePath.replace(".kt", ".class") + ) + ) + } else { + SourceFile(classpathEntrySourceDir, relativePath) + } + } + val classFiles = sourceFiles.map { TestSourceFile(it, tmpDir).compile() } + ClasspathEntrySnapshot( + classSnapshots = classFiles.map { it.unixStyleRelativePath to it.snapshot() }.toMap(LinkedHashMap()) + ) + } + return ClasspathSnapshot(classpathEntrySnapshots) +} + +/** Adapted version of [ClasspathChanges.Available] for readability in this test. */ +private data class Changes(private val lookupSymbols: Set, private val fqNames: Set) + +private fun ClasspathChanges.normalize(): Changes { + this as ClasspathChanges.Available + return Changes(lookupSymbols, fqNames) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt index 0edf1a19c95..269258f9526 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt @@ -26,7 +26,7 @@ abstract class ClasspathSnapshotTestCommon { private val gson by lazy { GsonBuilder().setPrettyPrinting().create() } protected fun Any.toGson(): String = gson.toJson(this) - class SourceFile(val baseDir: File, relativePath: String) { + open class SourceFile(val baseDir: File, relativePath: String) { val unixStyleRelativePath: String init { @@ -36,14 +36,18 @@ abstract class ClasspathSnapshotTestCommon { fun asFile() = File(baseDir, unixStyleRelativePath) } - /** Same as [SourceFile] but with a [TemporaryFolder] to store the results of operations on the [SourceFile]. */ - open class TestSourceFile(val sourceFile: SourceFile, protected val tmpDir: TemporaryFolder) { + class KotlinSourceFile(baseDir: File, relativePath: String, val preCompiledClassFile: ClassFile) : SourceFile(baseDir, relativePath) - fun replace(oldValue: String, newValue: String, newBaseDir: File? = null): TestSourceFile { + /** Same as [SourceFile] but with a [TemporaryFolder] to store the results of operations on the [SourceFile]. */ + open class TestSourceFile(val sourceFile: SourceFile, private val tmpDir: TemporaryFolder) { + + fun replace(oldValue: String, newValue: String, preCompiledKotlinClassFile: ClassFile? = null): TestSourceFile { val fileContents = sourceFile.asFile().readText() check(fileContents.contains(oldValue)) { "String '$oldValue' not found in file '${sourceFile.asFile().path}'" } - val newSourceFile = SourceFile(newBaseDir ?: tmpDir.newFolder(), sourceFile.unixStyleRelativePath) + val newSourceFile = + preCompiledKotlinClassFile?.let { KotlinSourceFile(tmpDir.newFolder(), sourceFile.unixStyleRelativePath, it) } + ?: SourceFile(tmpDir.newFolder(), sourceFile.unixStyleRelativePath) newSourceFile.asFile().parentFile.mkdirs() newSourceFile.asFile().writeText(fileContents.replace(oldValue, newValue)) return TestSourceFile(newSourceFile, tmpDir) @@ -69,14 +73,12 @@ abstract class ClasspathSnapshotTestCommon { } private fun compileKotlin(): List { - // TODO: Call Kotlin compiler to generate classes (see https://github.com/JetBrains/kotlin/pull/4512#discussion_r679432232) - // Currently, we use the precompiled classes in the test data. - return listOf( - ClassFile( - File(testDataDir.path + "/classes/" + sourceFile.baseDir.name), - sourceFile.unixStyleRelativePath.replace(".kt", ".class") - ) - ) + // TODO: Call Kotlin compiler to generate classes, for example: + // org.jetbrains.kotlin.test.MockLibraryUtil.compileKotlin(sourceFile.asFile().path, sourceFile.preCompiledClassFile.classRoot) + // However, currently it is not possible (see https://github.com/JetBrains/kotlin/pull/4512#discussion_r679432232), so we use + // the precompiled classes in the test data instead. + sourceFile as KotlinSourceFile + return listOf(sourceFile.preCompiledClassFile) } private fun compileJava(): List { @@ -125,17 +127,25 @@ abstract class ClasspathSnapshotTestCommon { } class SimpleKotlinClass(tmpDir: TemporaryFolder) : ChangeableTestSourceFile( - SourceFile(File(testDataDir, "src/original"), "com/example/SimpleKotlinClass.kt"), tmpDir + KotlinSourceFile( + baseDir = File(testDataDir, "src/original"), + relativePath = "com/example/SimpleKotlinClass.kt", + preCompiledClassFile = ClassFile(File(testDataDir, "classes/original"), "com/example/SimpleKotlinClass.class") + ), tmpDir ) { override fun changePublicMethodSignature() = replace( "publicMethod()", "changedPublicMethod()", - newBaseDir = File(tmpDir.newFolder(), "changedPublicMethodSignature") + preCompiledKotlinClassFile = ClassFile( + File(testDataDir, "classes/changedPublicMethodSignature"), "com/example/SimpleKotlinClass.class" + ) ) override fun changeMethodImplementation() = replace( "I'm in a public method", "This method implementation has changed!", - newBaseDir = File(tmpDir.newFolder(), "changedMethodImplementation") + preCompiledKotlinClassFile = ClassFile( + File(testDataDir, "classes/changedMethodImplementation"), "com/example/SimpleKotlinClass.class" + ) ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/A.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/A.class new file mode 100644 index 00000000000..95329953495 Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/A.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/B.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/B.class new file mode 100644 index 00000000000..a9b4aa8885c Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/B.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/D.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/D.class new file mode 100644 index 00000000000..9e8224b146d Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/D.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/A.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/A.class new file mode 100644 index 00000000000..134b7762aa1 Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/A.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/B.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/B.class new file mode 100644 index 00000000000..30d8764d71a Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/B.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/C.class b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/C.class new file mode 100644 index 00000000000..3aa0fa67439 Binary files /dev/null and b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/C.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/A.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/A.java new file mode 100644 index 00000000000..40564fe58a0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/A.java @@ -0,0 +1,6 @@ +package com.example; + +// Unchanged class +public class A { + public int a = 0; +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/B.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/B.java new file mode 100644 index 00000000000..574d6af1c2d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/B.java @@ -0,0 +1,9 @@ +package com.example; + +// Modified class +public class B { + public int b1 = 0; // Unchanged field + public String b2 = ""; // Modified field + //public int b3 = 0; // Removed field + public int b4 = 0; // Added field +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/D.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/D.java new file mode 100644 index 00000000000..1a8dbcf65dd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/D.java @@ -0,0 +1,6 @@ +package com.example; + +// Added class +public class D { + public int d = 0; +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/A.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/A.java new file mode 100644 index 00000000000..31d2c687b77 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/A.java @@ -0,0 +1,5 @@ +package com.example; + +public class A { + public int a = 0; +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/B.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/B.java new file mode 100644 index 00000000000..8453bca0bac --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/B.java @@ -0,0 +1,7 @@ +package com.example; + +public class B { + public int b1 = 0; + public int b2 = 0; + public int b3 = 0; +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/C.java b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/C.java new file mode 100644 index 00000000000..cfcbdb5df26 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/C.java @@ -0,0 +1,6 @@ +package com.example; + +// Will be removed +public class C { + public int c = 0; +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/A.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/A.kt new file mode 100644 index 00000000000..904b2c39229 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/A.kt @@ -0,0 +1,6 @@ +package com.example + +// Unchanged class +class A { + val a: Int = 0 +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/B.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/B.kt new file mode 100644 index 00000000000..e7ae7bdbdda --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/B.kt @@ -0,0 +1,9 @@ +package com.example + +// Modified class +public class B { + val b1: Int = 0 // Unchanged field + val b2: String = "" // Modified field + //val b3: Int = 0 // Removed field + val b4: Int = 0 // Added field +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/D.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/D.kt new file mode 100644 index 00000000000..e0b6460e1e7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/D.kt @@ -0,0 +1,6 @@ +package com.example + +// Added class +public class D { + val d: Int = 0 +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/A.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/A.kt new file mode 100644 index 00000000000..409c5326b3c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/A.kt @@ -0,0 +1,5 @@ +package com.example + +class A { + val a: Int = 0 +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/B.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/B.kt new file mode 100644 index 00000000000..2bcee31626a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/B.kt @@ -0,0 +1,7 @@ +package com.example + +public class B { + val b1: Int = 0 + val b2: Int = 0 + val b3: Int = 0 +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/C.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/C.kt new file mode 100644 index 00000000000..6193de47875 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/C.kt @@ -0,0 +1,6 @@ +package com.example + +// Will be removed +public class C { + val c: Int = 0 +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedMethodImplementation/com/example/SimpleKotlinClass.class b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedMethodImplementation/com/example/SimpleKotlinClass.class index 578ee200b77..502740f501a 100644 Binary files a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedMethodImplementation/com/example/SimpleKotlinClass.class and b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedMethodImplementation/com/example/SimpleKotlinClass.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedPublicMethodSignature/com/example/SimpleKotlinClass.class b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedPublicMethodSignature/com/example/SimpleKotlinClass.class index 67f6678f836..ec49c752f93 100644 Binary files a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedPublicMethodSignature/com/example/SimpleKotlinClass.class and b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/changedPublicMethodSignature/com/example/SimpleKotlinClass.class differ diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/original/com/example/SimpleKotlinClass.class b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/original/com/example/SimpleKotlinClass.class index ecfbae10fbf..83b86e6a5e0 100644 Binary files a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/original/com/example/SimpleKotlinClass.class and b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/classes/original/com/example/SimpleKotlinClass.class differ