From 8c5bb6420a4aa7c6a8038a1ac2e02bd909386aa4 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Tue, 27 Oct 2020 19:20:28 +0700 Subject: [PATCH] Introduce ${target}CheckAbiCompatibility task. Since Kotlin/Native is moving tovards stable ABI it is important to check between releases that there are no unexpected breaking changes. ${target}CheckAbiCompatibility task allows to compare `klib signatures` dumps of stdlib and platforms libs from current distribution and the given one. --- .../kotlin/CompareDistributionSignatures.kt | 126 ++++++++++++++++++ kotlin-native/build.gradle | 13 ++ 2 files changed, 139 insertions(+) create mode 100644 kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/CompareDistributionSignatures.kt diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/CompareDistributionSignatures.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/CompareDistributionSignatures.kt new file mode 100644 index 00000000000..1c71ebddd95 --- /dev/null +++ b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/CompareDistributionSignatures.kt @@ -0,0 +1,126 @@ +package org.jetbrains.kotlin + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import org.jetbrains.kotlin.konan.target.HostManager +import java.io.File + +/** + * Compares SignatureIds of the current distribution and the given older one. + * Can be used to validate that there are no unexpected breaking ABI changes. + */ +open class CompareDistributionSignatures : DefaultTask() { + + @Input + lateinit var target: String + + @Input + lateinit var oldDistribution: String + + private val newDistribution: String = + project.kotlinNativeDist.absolutePath + + enum class OnMismatchMode { + FAIL, + NOTIFY + } + + private val messageBuilder = StringBuilder() + + @Input + var onMismatchMode: OnMismatchMode = OnMismatchMode.NOTIFY + + @TaskAction + fun run() { + val klibs = getKlibs(oldDistribution, newDistribution) + if (klibs.missingLibs.isNotEmpty()) { + messageBuilder.apply { + appendln("Following libraries are missing in the new distro:") + klibs.missingLibs.forEach { appendln(it) } + } + } + if (klibs.newLibs.isNotEmpty()) { + messageBuilder.apply { + appendln("Following libraries were added:") + klibs.newLibs.forEach { appendln(it) } + } + } + for ((new, old) in klibs.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) { + messageBuilder.apply { + appendln("Mismatch for $klibName:") + if (compareDiff.oldKlibOnly.isNotEmpty()) { + appendln("Following signatures are missing in the new version:") + compareDiff.oldKlibOnly.forEach { appendln(it) } + } + if (compareDiff.newKlibOnly.isNotEmpty()) { + appendln("Following signatures were added:") + compareDiff.newKlibOnly.forEach { appendln(it) } + } + } + } + + private fun report(message: String) { + when (onMismatchMode) { + OnMismatchMode.FAIL -> error(message) + OnMismatchMode.NOTIFY -> println(message) + } + } + + private data class RemainingLibrary(val new: File, val old: File) + + private class KlibDiff( + val newLibs: Collection, + val missingLibs: Collection, + val remainingLibs: Collection + ) + + private fun String.stdlib(): File = + File("$this/klib/common/stdlib") + + private fun String.platformLibs(target: String): File = + File("$this/klib/platform/$target") + + private fun getKlibs(oldDistribution: String, newDistribution: String): KlibDiff { + val oldPlatformLibs = oldDistribution.platformLibs(target) + val oldPlatformLibsNames = oldPlatformLibs.list().toSet() + val newPlatformLibs = newDistribution.platformLibs(target) + val newPlatformLibsNames = newPlatformLibs.list().toSet() + return KlibDiff( + (newPlatformLibsNames - oldPlatformLibsNames).map(newPlatformLibs::resolve), + (oldPlatformLibsNames - newPlatformLibsNames).map(oldPlatformLibs::resolve), + oldPlatformLibsNames.intersect(newPlatformLibsNames).map { + RemainingLibrary(newPlatformLibs.resolve(it), oldPlatformLibs.resolve(it)) + } + RemainingLibrary(newDistribution.stdlib(), oldDistribution.stdlib()) + ) + } + + private fun getKlibSignatures(klib: File): List { + val tool = if (HostManager.hostIsMingw) "klib.bat" else "klib" + val klibTool = File("$newDistribution/bin/$tool").absolutePath + val args = listOf("signatures", klib.absolutePath) + return runProcess(localExecutor(project), klibTool, args).stdOut.lines() + } + + private class CompareDiff(val newKlibOnly: Set, val oldKlibOnly: Set) + + private fun compareSignatures(new: File, old: File): CompareDiff { + val newKlibSignatures = getKlibSignatures(new).toSet() + val oldKlibSignatures = getKlibSignatures(old).toSet() + return CompareDiff( + newKlibSignatures - oldKlibSignatures, + oldKlibSignatures - newKlibSignatures, + ) + } +} \ No newline at end of file diff --git a/kotlin-native/build.gradle b/kotlin-native/build.gradle index 4dfb940a352..6c60edb6fc6 100644 --- a/kotlin-native/build.gradle +++ b/kotlin-native/build.gradle @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.KotlinBuildPusher import org.jetbrains.kotlin.CollisionDetector import org.jetbrains.kotlin.CollisionTransformer import org.jetbrains.kotlin.CompilationDatabaseKt +import org.jetbrains.kotlin.CompareDistributionSignatures import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.apache.tools.ant.filters.ReplaceTokens import org.jetbrains.kotlin.UtilsKt @@ -778,3 +779,15 @@ task compdb(type: Copy) { from "$buildDir/${hostName}/compile_commands.json" into "$projectDir" } + +if (project.hasProperty("anotherDistro")) { + targetList.each { targetName -> + task "${targetName}CheckAbiCompatibility"(type: CompareDistributionSignatures) { + dependsOn "${targetName}PlatformLibs" + + target = targetName + oldDistribution = project.findProperty("anotherDistro") + onMismatchMode = CompareDistributionSignatures.OnMismatchMode.FAIL + } + } +}