KT-36113: Make KAPT classpath snapshot deterministic
When snapshotting a classpath entry, sort information about types so there is no dependency on the order of entries in jar. Test: ClasspathAnalyzerTest
This commit is contained in:
committed by
Yan Zhulanow
parent
9b68e4fe56
commit
6d4eb9ae52
+20
-14
@@ -105,32 +105,38 @@ class ClasspathEntryData : Serializable {
|
|||||||
var classDependencies = mutableMapOf<String, ClassDependencies>()
|
var classDependencies = mutableMapOf<String, ClassDependencies>()
|
||||||
|
|
||||||
private fun writeObject(output: ObjectOutputStream) {
|
private fun writeObject(output: ObjectOutputStream) {
|
||||||
val names = mutableMapOf<String, Int>()
|
// Sort only classDependencies, as all keys in this map are keys of classAbiHash map.
|
||||||
classAbiHash.keys.forEach { names[it] = names.size }
|
val sortedClassDependencies =
|
||||||
classDependencies.values.forEach {
|
classDependencies.toSortedMap().mapValues { ClassDependencies(it.value.abiTypes.sorted(), it.value.privateTypes.sorted()) }
|
||||||
it.abiTypes.forEach {
|
|
||||||
if (!names.containsKey(it)) names[it] = names.size
|
val names = LinkedHashMap<String, Int>()
|
||||||
|
sortedClassDependencies.forEach {
|
||||||
|
names[it.key] = names.size
|
||||||
|
it.value.abiTypes.forEach { type ->
|
||||||
|
if (type !in names) names[type] = names.size
|
||||||
}
|
}
|
||||||
it.privateTypes.forEach {
|
it.value.privateTypes.forEach { type ->
|
||||||
if (!names.containsKey(it)) names[it] = names.size
|
if (type !in names) names[type] = names.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output.writeInt(names.size)
|
output.writeInt(names.size)
|
||||||
names.forEach { key, value ->
|
names.forEach { (key, value) ->
|
||||||
output.writeInt(value)
|
output.writeInt(value)
|
||||||
output.writeUTF(key)
|
output.writeUTF(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
output.writeInt(classAbiHash.size)
|
output.writeInt(classAbiHash.size)
|
||||||
classAbiHash.forEach {
|
sortedClassDependencies.forEach { (key, _) ->
|
||||||
output.writeInt(names[it.key]!!)
|
output.writeInt(names[key]!!)
|
||||||
output.writeInt(it.value.size)
|
classAbiHash[key]!!.let {
|
||||||
output.write(it.value)
|
output.writeInt(it.size)
|
||||||
|
output.write(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output.writeInt(classDependencies.size)
|
output.writeInt(sortedClassDependencies.size)
|
||||||
classDependencies.forEach {
|
sortedClassDependencies.forEach {
|
||||||
output.writeInt(names[it.key]!!)
|
output.writeInt(names[it.key]!!)
|
||||||
|
|
||||||
output.writeInt(it.value.abiTypes.size)
|
output.writeInt(it.value.abiTypes.size)
|
||||||
|
|||||||
+32
-2
@@ -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.ClassWriter
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.*
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@@ -82,6 +81,37 @@ class ClasspathAnalyzerTest {
|
|||||||
assertEquals(emptySet<String>(), data.classDependencies["test/B"]!!.privateTypes)
|
assertEquals(emptySet<String>(), 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
|
@Test
|
||||||
fun emptyInput() {
|
fun emptyInput() {
|
||||||
val inputDir = tmp.newFolder("input")
|
val inputDir = tmp.newFolder("input")
|
||||||
|
|||||||
Reference in New Issue
Block a user