[K/N] Compare distributions as a whole

We might move declarations between platform libraries
preserving their fqname. To make diff simpler
let's collect signatures from all platform libraries
and compare them together instead of per-file basis.
This commit is contained in:
Sergey Bogolepov
2022-09-12 18:58:26 +03:00
parent 49f2f85927
commit e669ad95fa
@@ -27,8 +27,6 @@ open class CompareDistributionSignatures : DefaultTask() {
NOTIFY NOTIFY
} }
private val messageBuilder = StringBuilder()
@Input @Input
var onMismatchMode: OnMismatchMode = OnMismatchMode.NOTIFY var onMismatchMode: OnMismatchMode = OnMismatchMode.NOTIFY
@@ -47,6 +45,7 @@ open class CompareDistributionSignatures : DefaultTask() {
emptyList(), emptyList(),
listOf(RemainingLibrary(newDistribution.stdlib(), oldDistribution.stdlib())) listOf(RemainingLibrary(newDistribution.stdlib(), oldDistribution.stdlib()))
) )
is Libraries.Platform -> { is Libraries.Platform -> {
val oldPlatformLibs = oldDistribution.platformLibs(libraries.target) val oldPlatformLibs = oldDistribution.platformLibs(libraries.target)
val oldPlatformLibsNames = oldPlatformLibs.list().toSet() val oldPlatformLibsNames = oldPlatformLibs.list().toSet()
@@ -71,56 +70,62 @@ open class CompareDistributionSignatures : DefaultTask() {
""".trimIndent() """.trimIndent()
} }
val platformLibsDiff = computeDiff() val platformLibsDiff = computeDiff()
if (platformLibsDiff.missingLibs.isNotEmpty()) { report("libraries diff")
messageBuilder.apply { val librariesMismatch = platformLibsDiff.missingLibs.isNotEmpty() || platformLibsDiff.newLibs.isNotEmpty()
appendln("Following platform libraries are missing in the new distro:") platformLibsDiff.missingLibs.forEach { report("-: $it") }
platformLibsDiff.missingLibs.forEach { appendln(it) } platformLibsDiff.newLibs.forEach { report("+: $it") }
} val signaturesMismatch = cumulativeSignaturesComparison(platformLibsDiff)
} if ((librariesMismatch || signaturesMismatch) && onMismatchMode == OnMismatchMode.FAIL) {
if (platformLibsDiff.newLibs.isNotEmpty()) { error("Mismatch found, see stdout for details.")
messageBuilder.apply {
appendln("Following platform libraries were added:")
platformLibsDiff.newLibs.forEach { appendln(it) }
}
}
for ((new, old) in platformLibsDiff.remainingLibs) {
val result = compareSignatures(new, old)
if (result.run { newKlibOnly.isNotEmpty() || oldKlibOnly.isNotEmpty() }) {
reportMismatch(result, new.name)
}
}
if (messageBuilder.isNotEmpty()) {
report(messageBuilder.toString())
} }
} }
private fun reportMismatch(compareDiff: CompareDiff, klibName: String) { private data class Mark(var presentInOld: Boolean = false, var presentInNew: Boolean = false) {
messageBuilder.apply { val newOnly: Boolean
appendln("Mismatch for $klibName:") get() = presentInNew && !presentInOld
if (compareDiff.oldKlibOnly.isNotEmpty()) {
appendln("Following signatures are missing in the new version:") val oldOnly: Boolean
compareDiff.oldKlibOnly.forEach { appendln(it) } get() = presentInOld && !presentInNew
} }
if (compareDiff.newKlibOnly.isNotEmpty()) {
appendln("Following signatures were added:") private fun cumulativeSignaturesComparison(klibDiff: KlibDiff): Boolean {
compareDiff.newKlibOnly.forEach { appendln(it) } report("signatures diff")
// Boolean value signifies if value is present in new platform libraries.
val signaturesMap = mutableMapOf<String, Mark>()
val oldLibs = klibDiff.missingLibs + klibDiff.remainingLibs.map { it.old }
oldLibs.flatMap { getKlibSignatures(it) }.forEach { sig ->
signaturesMap.getOrPut(sig, ::Mark).presentInOld = true
}
val duplicates = mutableListOf<String>()
val newLibs = klibDiff.newLibs + klibDiff.remainingLibs.map { it.new }
newLibs.flatMap { getKlibSignatures(it) }.forEach { sig ->
val mark = signaturesMap.getOrPut(sig, ::Mark)
if (mark.presentInNew) {
duplicates += sig
} else {
mark.presentInNew = true
} }
} }
duplicates.forEach { report("dup: $it") }
val oldSigs = signaturesMap.filterValues { it.oldOnly }.keys
.sorted()
.onEach { report("-: $it") }
val newSigs = signaturesMap.filterValues { it.newOnly }.keys
.sorted()
.onEach { report("+: $it") }
return oldSigs.isNotEmpty() || newSigs.isNotEmpty()
} }
private fun report(message: String) { private fun report(message: String) {
when (onMismatchMode) { println(message)
OnMismatchMode.FAIL -> error(message)
OnMismatchMode.NOTIFY -> println(message)
}
} }
private data class RemainingLibrary(val new: File, val old: File) private data class RemainingLibrary(val new: File, val old: File)
private class KlibDiff( private class KlibDiff(
val newLibs: Collection<File>, val newLibs: Collection<File>,
val missingLibs: Collection<File>, val missingLibs: Collection<File>,
val remainingLibs: Collection<RemainingLibrary> val remainingLibs: Collection<RemainingLibrary>
) )
private fun String.stdlib(): File = private fun String.stdlib(): File =
@@ -148,18 +153,7 @@ open class CompareDistributionSignatures : DefaultTask() {
val tool = if (HostManager.hostIsMingw) "klib.bat" else "klib" val tool = if (HostManager.hostIsMingw) "klib.bat" else "klib"
val klibTool = File("$newDistribution/bin/$tool").absolutePath val klibTool = File("$newDistribution/bin/$tool").absolutePath
val args = listOf("signatures", klib.absolutePath) val args = listOf("signatures", klib.absolutePath)
return runProcess(localExecutor(project), klibTool, args).stdOut.lines() return runProcess(localExecutor(project), klibTool, args).stdOut.lines().filter { it.isNotBlank() }
}
private class CompareDiff(val newKlibOnly: Set<String>, val oldKlibOnly: Set<String>)
private fun compareSignatures(new: File, old: File): CompareDiff {
val newKlibSignatures = getKlibSignatures(new).toSet()
val oldKlibSignatures = getKlibSignatures(old).toSet()
return CompareDiff(
newKlibSignatures - oldKlibSignatures,
oldKlibSignatures - newKlibSignatures
)
} }
private fun looksLikeKotlinNativeDistribution(directory: Path): Boolean { private fun looksLikeKotlinNativeDistribution(directory: Path): Boolean {