diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzer.kt index e0c9133e319..958b4671091 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzer.kt @@ -105,32 +105,38 @@ class ClasspathEntryData : Serializable { var classDependencies = mutableMapOf() private fun writeObject(output: ObjectOutputStream) { - val names = mutableMapOf() - classAbiHash.keys.forEach { names[it] = names.size } - classDependencies.values.forEach { - it.abiTypes.forEach { - if (!names.containsKey(it)) names[it] = names.size + // Sort only classDependencies, as all keys in this map are keys of classAbiHash map. + val sortedClassDependencies = + classDependencies.toSortedMap().mapValues { ClassDependencies(it.value.abiTypes.sorted(), it.value.privateTypes.sorted()) } + + val names = LinkedHashMap() + sortedClassDependencies.forEach { + names[it.key] = names.size + it.value.abiTypes.forEach { type -> + if (type !in names) names[type] = names.size } - it.privateTypes.forEach { - if (!names.containsKey(it)) names[it] = names.size + it.value.privateTypes.forEach { type -> + if (type !in names) names[type] = names.size } } output.writeInt(names.size) - names.forEach { key, value -> + names.forEach { (key, value) -> output.writeInt(value) output.writeUTF(key) } output.writeInt(classAbiHash.size) - classAbiHash.forEach { - output.writeInt(names[it.key]!!) - output.writeInt(it.value.size) - output.write(it.value) + sortedClassDependencies.forEach { (key, _) -> + output.writeInt(names[key]!!) + classAbiHash[key]!!.let { + output.writeInt(it.size) + output.write(it) + } } - output.writeInt(classDependencies.size) - classDependencies.forEach { + output.writeInt(sortedClassDependencies.size) + sortedClassDependencies.forEach { output.writeInt(names[it.key]!!) output.writeInt(it.value.abiTypes.size) diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzerTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzerTest.kt index 7cda5e623d3..de9f0b6629f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzerTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/incremental/ClasspathAnalyzerTest.kt @@ -7,8 +7,7 @@ package org.jetbrains.kotlin.gradle.internal.kapt.incremental import org.jetbrains.org.objectweb.asm.ClassWriter import org.jetbrains.org.objectweb.asm.Opcodes -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue +import org.junit.Assert.* import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -82,6 +81,37 @@ class ClasspathAnalyzerTest { assertEquals(emptySet(), data.classDependencies["test/B"]!!.privateTypes) } + @Test + fun testJarWithEntriesShuffled() { + val jarA = tmp.newFile("inputA.jar").also { jar -> + ZipOutputStream(jar.outputStream()).use { + it.putNextEntry(ZipEntry("test/A.class")) + it.write(emptyClass("test/A")) + it.closeEntry() + + it.putNextEntry(ZipEntry("test/B.class")) + it.write(emptyClass("test/B")) + it.closeEntry() + } + } + val outputA = StructureArtifactTransform().also { it.outputDirectory = tmp.newFolder() }.transform(jarA).single() + + val jarB = tmp.newFile("inputB.jar").also { jar -> + ZipOutputStream(jar.outputStream()).use { + it.putNextEntry(ZipEntry("test/B.class")) + it.write(emptyClass("test/B")) + it.closeEntry() + + it.putNextEntry(ZipEntry("test/A.class")) + it.write(emptyClass("test/A")) + it.closeEntry() + } + } + val outputB = StructureArtifactTransform().also { it.outputDirectory = tmp.newFolder() }.transform(jarB).single() + + assertArrayEquals(outputA.readBytes(), outputB.readBytes()) + } + @Test fun emptyInput() { val inputDir = tmp.newFolder("input")