From e26dc4d57401b65f71655133b3f28f68d74fa760 Mon Sep 17 00:00:00 2001 From: Hung Nguyen Date: Tue, 21 Sep 2021 09:49:04 +0100 Subject: [PATCH] KT-45777: Compute classpath changes based on changed snapshots only to avoid unnecessarily loading unchanged ones. Duplicate classes will make this a bit tricky. This commit outlines the algorithm to handle them, the full implementation will follow later. Also handle removed classes when computing classpath changes. Test: New tests in ClasspathChangesComputerTest --- .../incremental/AbstractIncrementalCache.kt | 10 +- .../incremental/ClasspathChangesComputer.kt | 203 ++++++++++++---- .../incremental/ClasspathSnapshotter.kt | 15 +- .../ClasspathEntrySnapshotTransform.kt | 4 +- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 81 ++++--- .../ClasspathChangesComputerTest.kt | 224 ++++++++++++------ .../ClasspathSnapshotTestCommon.kt | 42 ++-- .../current-classpath/0/com/example/A.class | Bin 0 -> 582 bytes .../current-classpath/0/com/example/B.class | Bin 0 -> 997 bytes .../current-classpath/0/com/example/D.class | Bin 0 -> 582 bytes .../previous-classpath/0/com/example/A.class | Bin 0 -> 582 bytes .../previous-classpath/0/com/example/B.class | Bin 0 -> 830 bytes .../previous-classpath/0/com/example/C.class | Bin 0 -> 582 bytes .../current-classpath/0/com/example/A.java | 6 + .../current-classpath/0/com/example/B.java | 9 + .../current-classpath/0/com/example/D.java | 6 + .../previous-classpath/0/com/example/A.java | 5 + .../previous-classpath/0/com/example/B.java | 7 + .../previous-classpath/0/com/example/C.java | 6 + .../current-classpath/0/com/example/A.kt | 6 + .../current-classpath/0/com/example/B.kt | 9 + .../current-classpath/0/com/example/D.kt | 6 + .../previous-classpath/0/com/example/A.kt | 5 + .../previous-classpath/0/com/example/B.kt | 7 + .../previous-classpath/0/com/example/C.kt | 6 + .../com/example/SimpleKotlinClass.class | Bin 901 -> 893 bytes .../com/example/SimpleKotlinClass.class | Bin 891 -> 883 bytes .../com/example/SimpleKotlinClass.class | Bin 884 -> 876 bytes 28 files changed, 482 insertions(+), 175 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/A.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/B.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/current-classpath/0/com/example/D.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/A.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/B.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/classes/kotlin/previous-classpath/0/com/example/C.class create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/A.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/B.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/current-classpath/0/com/example/D.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/A.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/B.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/java/previous-classpath/0/com/example/C.java create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/A.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/B.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/current-classpath/0/com/example/D.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/A.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/B.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/testData/ClasspathChangesComputerTest/testMultipleClasses/src/kotlin/previous-classpath/0/com/example/C.kt 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 0000000000000000000000000000000000000000..9532995349518a65c4260226076f412fa084dd97 GIT binary patch literal 582 zcmZutOG{fp7(Fxhk?S?7H)*sneb-IfMQ;>$ikP+(E~G8k5`?TK$)IEIqhu!HGOPZO zuH6WN;6j4S{;1M#ZVN@|V$SzE=bJh6^VjtafNg9r)J8=X%QKOEOJ&@xfH6cT;#9<` z$dBUB!xK3&6*vt3uF93!V{l%+>Q~{x58hNSbcRPZ*e>lT=Z| zy(Mt0Fw}dK!lWvXKT0FU!U&S^?9`!`F$=y0kZMBxGi9p|W#dhT{l&Z)@)i$yw3vrH zut{KJYcb#A9ro6{{o(V7A2cJU%{%Vl?f3W83Btv^?FL@t+dW&%Zg;zHCWgYhVl1r* zmCH}FY$&H+#W1B#qgRYX+80x0>%YzLOEH^{j$&ks7&^iRz&IMd0~W6MXs?) zPTkT(!y2cI)0Omfgiol7tv!m~>nj8o<@hO~9fO3=2&;bxSVBlu9m{3suJ{tsISo1f ziM{`bIi8oS$9O?;pBC5YEsX)31osm(6Rcv5Qanh|!ukMAqm73HR5U`3M;g9HtxT3R Gs=oox6ka$0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a9b4aa8885cec837b1cd808cdeb3c2134af9e3ee GIT binary patch literal 997 zcmZuvTTc@~6#iy*yWMU}*%p*i@QN3x$^zjHHL)6#O)4f}hzU>AvZ({xUDKHo;!~ge zAwK({iN?eS)9~nzGM*__E+w1H`OfA0&e=2b`_Io`0M@a>FwsgoL3AWK@8c+_XTcc! zeQ_v)SS0P>b#p&z=`1V;=dnzre#&4iF6|VMf#bnOmH{qu47|ComQZY<6&h^}2$h0F}UmZ$^Mv5Q{3z)*RVO|*(P=o2krSTRF3yrkf4)!D6 z>eb)&ytz}$)UJVU8*km^__!K+A%J)s4a^3I_}8)D{g z%mJF{fZ|8ec(gaZM?-!X^yfWZ;U4z~eUG~axMVG|MY48f&|l*#te!dkkax<+s*h6C9;%o&nI%eM~VD6qu@Jc0$PY&Up~+bb6dSc%T5#~hf>LA z96dYT02Mt?_ad`*nM9ktPBZGh5maODD{YB*M|7p}M?r5p?R8txOKB=*N5VT}vRI|7 zv1u6=%I2mBmCaq#rfP&8va`b&ZJMl2KD`+_ACUKixb$+6ul;~qD|_Ej{7NpzLpqBj zX$Urzn>Y4|JR?ixn1oNZL|=I%6_7Q#Ozj)4ekO1tVmeXq3_0_kJc9~VnK{mqi(f8~ zolD4Pf31Y-rBhtHIE8zO5XbVc-4e|JY|*sWY1$jRutMAnaVx}aED*$<5O;BJ7fj(k e7I!hB;49=59EGVNC@GW`@7(Fxhk=xtE+@w*{_-wmryXZ}eJ4LDn;X(+Vq?Tr_%IFe!{>ebdoA+ zxU&L|IzyvBEljHN_?(4%q_;D+8I=t(iTz~3OCkU5|jvIK9Z}%K9yWQ>WO$~)N#Y9>Y zDwjv|Y$RtN#VDmtvtNuwIuJ8u>%YzLLouI?&Nrls7wx&Rz!J~=Y3MoPFK>`5k96Ww)QA`yWbIfEyqs??HD9{N?5%iUsnQI!-n~t4X>cMQFY`X4qZKn*nd}fQO5D zzNI-!NF;qRRkprs24C{obR<73%UC;{Wk#j4ue2gcH_LJ(jLI{O zZF1@sCK}c_Wt^_0uOr-~Dzf${mR|ja|FalBA+%$_!BfJ@EdfgisH$VR=-e0oLimG* z9RI}bf5aTm3f2amQ{1J+b$Ux<04K(S7|j?D@rY8a#%N(}0H)E#`T%8(K;yB7r%^4E HWsS-W&JkWV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..30d8764d71adfaf199350e556ea2ae25e60bf794 GIT binary patch literal 830 zcmZuuO>fgc5PiG;h~qlc328~g_jg+)oTl7BNCbprDS%W^gp|`JvC1}yjk24n$DH^f zoVg$&A#p%akNhaatdj$6bD4SbW}au~`On{9zX3eKBZg8x8%N2B7=IomQL6yP5FUwR z5sgH87`^KqC4F6h#o#`ZsnpLItnHmn00*uQ8wCbif0Bw}y?y3OPZ^xUM7Q>GLCnB= z1X&!QgiA$mtS<+erqv8kShl`+c{rc>+%v$V?AGS9CU3BobNbcgAwQ^wR-HHOkEdT= z5##yuS>5)W(4|J0 ztfOqx6X<7Ad_YxX45IAp{)G3P0LMd$rfoj5O)*#upa`F;GS+hDtoR4QZ`Ax>Y_AlT zSBk6W#m-9c#!7MRyx3hK=Ge*KcOScy+jP7(Fxhk=xtE-o#YX__iB$;Y~|-6=@q0E<}YYp^(+&W@x9m56MhQms$Bk zT)Pnjp$iFJ`lG~eu7y%?G3R@o^Ua+3{paErfE{cyG}1DU<+;c|XEN^Bz!;)aaVFwS z6vy$0@u^Hr4Gx3 zTPxsLV`vVhrO8wgzmrBxgb^g+`I$p6V-|c1Ak~!m=gL++$|hS3d&@;T%X{w8<(D_q3Bu)~>jqxr+dbRNZg=}Hr^beIBCQEk z$oKPnEN7p@IHOK$P^KaqikY(YRWtlp&S$B7t1M&Va9$Xd%c0VWDEmcG8evpfXl#*F zw=&VP#wp`;C4C*?L#kqHkD~YDCxY+Q_z|HUgM^O>>;DK?K}b~-t5xTw_y?kIG~~Dz zd;bx0JgHa@@RZ^nEpF0VS|d0KZYO9bSjQboaW_E+8zV4{F7Ay`(+D;0Yxo+CDp}R2 F{{`UwUP1r> literal 0 HcmV?d00001 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 578ee200b77f2af76234c2b6cf8e2d89b9bada56..502740f501ac1d8ce57f08ab74e9e296fe1aea16 100644 GIT binary patch delta 162 zcmZo=|I0RE+Qh@cj64&sD{$~J@N+WoG6+nZqOpdB1||kZ1_lPsO$>rU!rK@m zHv&0K4B9}F3rI6FurY8jurufY`8*JX44gn21_mJpDWDu9gBFmDVu%bDL%4v3aAPrq JX|ffwHvkQE5?=rS delta 179 zcmey%*2+F%+Qchb;yjED0$GV=iTXK-dFlGWmBl5gx$F#l6IUs)2`~t9GVpKwyN$8l znt_RdnSp_USBTe#*>)3yu=X|v$&Ek(CI)RF$pxet7}ywi7}yzffGi#cMj*+{zzLLL fU=U)E0?IKmXaU&>gJf_U#04~f8`+@ATFk)!(=`~) 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 67f6678f836a1a4ecf5519163e5bbc5d4f213c51..ec49c752f9320821f4c8462b83b73438245b940d 100644 GIT binary patch delta 178 zcmey(_L*(MToHk+#Ii*FoW#6z{ou;tlGI#w2A+v$6*%}9_&FJP83ZQImfrYfD`UMm z0}}%y0|Nu+CI&$v;cX0(8-W}q25lh81*Dl7*cdn%*co(yd>)8G22P+11A`EQ6i|+l ZK?}%6F+>K7AzVO1xUm?*G}(aJ8vvkN7n1-0 delta 179 zcmey&_M2_O+=;g(#d#PR1hNv#67_Qu^V0Q$D~n4~bJ-dACT>(<6JQYJWZ>WUc^hNB zH3Jg^GXnzyuMn>fv+X7ZVeM@Uk{f{nObpsUk_$*PFt9Q3Ft9V|09iZ?j6jl?ffFdh fz#zmR1(aiC&;qg%2Fc(yhzn={H?l#K6_|qo8QK{r 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 ecfbae10fbf133c1409c385a65a4142edc34d219..83b86e6a5e00fd86d2d001e889012564dfc6d332 100644 GIT binary patch delta 198 zcmeyu_J(c3w27O9xp^2FnDa|Z7#T!;CVmuFwdP^qW8mjx;AIeCWDxUI&sE6GQ%F=O zC{4=AOjgKEEy>7F;n?_Q8)LmW0}}%y0|Nu+CI&$v;cX0(8-W}q25lh81*Dl7*cdn% t*co(yd>)8G22P+11A`EQ6i|+lK?}%6F+>K7AzVO1xUm?*G+C3`8vs#*8zle$ delta 201 zcmaFE_JwW2Gy^eD^<0I_JcUGsg3_d%%w&b!)RK(+6b=R+Mh1bb#Ii*FoW#6z{ou;t zlGI#w2EK`_6xak91UVV_H@?}%SZ~e1#K6qJz`!fS>%(lji9uL<8-wIVpa2ttHjv~3 z(hLl23_J|%3_3s-4+A5R