From 7d969a01daee165a9f9e3d1cf20eed930a65119c Mon Sep 17 00:00:00 2001 From: Leon Linhart Date: Mon, 2 Jun 2025 17:37:42 +0200 Subject: [PATCH 1/9] Check the Gradle distribution checksum (#289) See https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:verification --- gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e6aba251..a7a990ab 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionSha256Sum=c16d517b50dd28b3f5838f0e844b7520b8f1eb610f2f29de7e4e04a1b7c9c79b distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip networkTimeout=10000 validateDistributionUrl=true From 8ba41765d1dce0fc25b4347077bd042c1ff9a089 Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Mon, 2 Jun 2025 17:46:45 +0200 Subject: [PATCH 2/9] Added a warning about the end of development of a separate plugin (#296) Co-authored-by: Filipp Zhinkin --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 6e173157..2b096b7c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,18 @@ [![License](https://img.shields.io/github/license/Kotlin/binary-compatibility-validator)](LICENSE.TXT) [![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/binary-compatibility-validator/) +# Support for this plugin has been discontinued +The development of a separate binary compatibility validator Gradle plugin has been discontinued, +and all its functionality will be moved to Kotlin Gradle Plugin starting from the `2.2.0` release. + +As part of the migration, the code of the current plugin has been migrated to [the Kotlin repository](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/abi-validation), +as well as issues migrated to [the Kotlin project in YouTrack](https://youtrack.jetbrains.com/issues/KT?q=subsystems:%20%7BTools.%20BCV%7D,%20%7BTools.%20Gradle.%20BCV%7D). + +This plugin is frozen from changes, no new features or minor bugfixes will be added to it. + +The functionality of working with the ABI in Kotlin Gradle Plugin is in an experimental state now, +so it is recommended to continue using this plugin in production projects until KGP API stabilization. + # Binary compatibility validator The tool allows dumping binary API of a JVM part of a Kotlin library that is public in the sense of Kotlin visibilities and ensures that the public binary API wasn't changed in a way that makes this change binary incompatible. From 05bb751c261ebe6852245d00994b9a7262bd8090 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Mon, 2 Jun 2025 17:57:26 +0200 Subject: [PATCH 3/9] Use findProperty instead of getProperties (#287) --- src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt b/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt index 7cd2af31..de70e96f 100644 --- a/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt +++ b/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt @@ -464,7 +464,7 @@ private class KlibValidationPipelineBuilder( } fun Project.bannedTargets(): Set { - val prop = project.properties[BANNED_TARGETS_PROPERTY_NAME] as String? + val prop = project.findProperty(BANNED_TARGETS_PROPERTY_NAME)?.toString() prop ?: return emptySet() return prop.split(",").map { it.trim() }.toSet().also { if (it.isNotEmpty()) { From fa1a82e8e18476f4124dfe2d9a0758071191b630 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 11 Jun 2025 03:29:45 +0800 Subject: [PATCH 4/9] Highlight the warning in README (#297) --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2b096b7c..66355e40 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,18 @@ [![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/binary-compatibility-validator/) # Support for this plugin has been discontinued -The development of a separate binary compatibility validator Gradle plugin has been discontinued, -and all its functionality will be moved to Kotlin Gradle Plugin starting from the `2.2.0` release. -As part of the migration, the code of the current plugin has been migrated to [the Kotlin repository](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/abi-validation), -as well as issues migrated to [the Kotlin project in YouTrack](https://youtrack.jetbrains.com/issues/KT?q=subsystems:%20%7BTools.%20BCV%7D,%20%7BTools.%20Gradle.%20BCV%7D). - -This plugin is frozen from changes, no new features or minor bugfixes will be added to it. - -The functionality of working with the ABI in Kotlin Gradle Plugin is in an experimental state now, -so it is recommended to continue using this plugin in production projects until KGP API stabilization. +> [!WARNING] +> The development of a separate binary compatibility validator Gradle plugin has been discontinued, +> and all its functionality will be moved to Kotlin Gradle Plugin starting from the `2.2.0` release. +> +> As part of the migration, the code of the current plugin has been migrated to [the Kotlin repository](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/abi-validation), +> as well as issues migrated to [the Kotlin project in YouTrack](https://youtrack.jetbrains.com/issues/KT?q=subsystems:%20%7BTools.%20BCV%7D,%20%7BTools.%20Gradle.%20BCV%7D). +> +> This plugin is frozen from changes, no new features or minor bugfixes will be added to it. +> +> The functionality of working with the ABI in Kotlin Gradle Plugin is in an experimental state now, +> so it is recommended to continue using this plugin in production projects until KGP API stabilization. # Binary compatibility validator From a742b297044d36b7786310113bf62fd868f0ca6a Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 12 Jun 2025 16:45:39 -0400 Subject: [PATCH 5/9] Kotlin 2.1.0 cross compilation support (#299) * Kotlin 2.1.0 cross compilation support Closes #291 --- .../validation/test/KlibVerificationTests.kt | 21 ++++++++++ ...NativePluginAndCrossCompilation.gradle.kts | 38 +++++++++++++++++++ .../BinaryCompatibilityValidatorPlugin.kt | 20 ++++++++-- 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/functionalTest/resources/examples/gradle/base/withNativePluginAndCrossCompilation.gradle.kts diff --git a/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt b/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt index b47fe4e4..64d1df78 100644 --- a/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt +++ b/src/functionalTest/kotlin/kotlinx/validation/test/KlibVerificationTests.kt @@ -850,4 +850,25 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() { .contains("+// Targets: [linuxArm64]") } } + + @Test + fun `check cross compilation support`() { + Assume.assumeFalse(HostManager().isEnabled(KonanTarget.MACOS_ARM64)) + + val runner = test { + settingsGradleKts { + resolve("/examples/gradle/settings/settings-name-testproject.gradle.kts") + } + buildGradleKts { + resolve("/examples/gradle/base/withNativePluginAndCrossCompilation.gradle.kts") + } + additionalBuildConfig("/examples/gradle/configuration/appleTargets/targets.gradle.kts") + addToSrcSet("/examples/classes/TopLevelDeclarations.kt") + runner { + arguments.addAll(listOf(":apiDump", "-Pkotlin.native.enableKlibsCrossCompilation=true")) + } + } + + checkKlibDump(runner.build(), "/examples/classes/TopLevelDeclarations.klib.all.dump") + } } diff --git a/src/functionalTest/resources/examples/gradle/base/withNativePluginAndCrossCompilation.gradle.kts b/src/functionalTest/resources/examples/gradle/base/withNativePluginAndCrossCompilation.gradle.kts new file mode 100644 index 00000000..5ad94c2a --- /dev/null +++ b/src/functionalTest/resources/examples/gradle/base/withNativePluginAndCrossCompilation.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +plugins { + kotlin("multiplatform") version "2.1.0" + id("org.jetbrains.kotlinx.binary-compatibility-validator") +} + +repositories { + mavenCentral() +} + +kotlin { + linuxX64() + linuxArm64() + mingwX64() + androidNativeArm32() + androidNativeArm64() + androidNativeX64() + androidNativeX86() + + sourceSets { + val commonMain by getting + val commonTest by getting { + dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + } +} + +apiValidation { + klib.enabled = true +} diff --git a/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt b/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt index de70e96f..c117b3ea 100644 --- a/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt +++ b/src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader -import org.jetbrains.kotlin.library.abi.LibraryAbiReader import java.io.* import java.util.* @@ -320,6 +319,7 @@ private inline fun Project.task( private const val BANNED_TARGETS_PROPERTY_NAME = "binary.compatibility.validator.klib.targets.disabled.for.testing" private const val KLIB_DUMPS_DIRECTORY = "klib" private const val KLIB_INFERRED_DUMPS_DIRECTORY = "klib-all" +private const val ENABLE_CROSS_COMPILATION_PROPERTY_NAME = "kotlin.native.enableKlibsCrossCompilation" /** * KLib ABI dump validation and dump extraction consists of multiple steps that extracts and transforms dumps for klibs. @@ -544,10 +544,14 @@ private class KlibValidationPipelineBuilder( private fun Project.targetIsSupported(target: KotlinTarget): Boolean { if (bannedTargets().contains(target.targetName)) return false - return when (target) { - is KotlinNativeTarget -> HostManager().isEnabled(target.konanTarget) - else -> true + if (target !is KotlinNativeTarget || HostManager().isEnabled(target.konanTarget)) { + return true } + + // Starting from Kotlin 2.1.0, cross compilation could be enabled via property + if (!isKgpVersionAtLeast2_1(getKotlinPluginVersion())) return false + + return (project.findProperty(ENABLE_CROSS_COMPILATION_PROPERTY_NAME) as String?).toBoolean() } // Compilable targets not supported by the host compiler @@ -770,3 +774,11 @@ private var Configuration.isCanBeDeclaredCompat: Boolean isCanBeDeclared = value } } + +private fun isKgpVersionAtLeast2_1(kgpVersion: String): Boolean { + val parts = kgpVersion.split('.') + if (parts.size < 2) return false + val major = parts[0].toIntOrNull() ?: return false + val minor = parts[1].toIntOrNull() ?: return false + return major > 2 || (major == 2 && minor >= 1) +} From 0403f922ea9930bc0d4f722a46d7114b4444c93b Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Fri, 13 Jun 2025 14:25:58 +0200 Subject: [PATCH 6/9] Excluding local classes created by Parcelize (#300) In some rare cases, JVM accepts not to specify method name (passing to `outerMethod`) in `EnclosingMethod` attribute, only class name. It's relevant for cases if local class is enclosed in an instance initializer, static initializer, instance variable initializer, or class variable initializer (see https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.7) Normally Kotlin compiler always fills method name but kotlin-parcelize can generate different instructions because it is a compiler plugin. Fixes #112 Co-authored-by: Filipp Zhinkin --- src/main/kotlin/api/AsmMetadataLoading.kt | 2 +- src/test/kotlin/tests/PrecompiledCasesTest.kt | 40 ++++++++++++++++++ .../parcelable/Country$Creator.class | Bin 0 -> 1395 bytes .../precompiled/parcelable/parcelable.txt | 0 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test/kotlin/tests/PrecompiledCasesTest.kt create mode 100644 src/test/resources/precompiled/parcelable/Country$Creator.class create mode 100644 src/test/resources/precompiled/parcelable/parcelable.txt diff --git a/src/main/kotlin/api/AsmMetadataLoading.kt b/src/main/kotlin/api/AsmMetadataLoading.kt index b7a4d303..f106c1de 100644 --- a/src/main/kotlin/api/AsmMetadataLoading.kt +++ b/src/main/kotlin/api/AsmMetadataLoading.kt @@ -39,7 +39,7 @@ internal fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) = private val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name } -private fun ClassNode.isLocal() = outerMethod != null +private fun ClassNode.isLocal() = outerClass != null // using outerMethod is unreliable, because even for local classes outerMethod can sometimes be null private fun ClassNode.isInner() = innerClassNode != null private fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings") private fun ClassNode.isSyntheticAnnotationClass() = isSynthetic(access) && name.contains("\$annotationImpl\$") diff --git a/src/test/kotlin/tests/PrecompiledCasesTest.kt b/src/test/kotlin/tests/PrecompiledCasesTest.kt new file mode 100644 index 00000000..c1cec4f7 --- /dev/null +++ b/src/test/kotlin/tests/PrecompiledCasesTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.validation.api.tests + +import kotlinx.validation.api.* +import org.junit.* +import org.junit.rules.TestName +import java.io.File +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.walk + +class PrecompiledCasesTest { + + companion object { + val baseOutputPath = File("src/test/resources/precompiled") + } + + @Rule + @JvmField + val testName = TestName() + + @Test fun parcelable() { snapshotAPIAndCompare(testName.methodName) } + + @OptIn(ExperimentalPathApi::class) + private fun snapshotAPIAndCompare(testClassRelativePath: String, nonPublicMarkers: Set = emptySet()) { + val testClasses = baseOutputPath.toPath().walk().map(Path::toFile).toList() + check(testClasses.isNotEmpty()) { "No class files are found in path: $baseOutputPath" } + + val testClassStreams = testClasses.asSequence().filter { it.name.endsWith(".class") }.map { it.inputStream() } + val classes = testClassStreams.loadApiFromJvmClasses() + val additionalPackages = classes.extractAnnotatedPackages(nonPublicMarkers) + val api = classes.filterOutNonPublic(nonPublicPackages = additionalPackages).filterOutAnnotated(nonPublicMarkers) + val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt") + api.dumpAndCompareWith(target) + } +} diff --git a/src/test/resources/precompiled/parcelable/Country$Creator.class b/src/test/resources/precompiled/parcelable/Country$Creator.class new file mode 100644 index 0000000000000000000000000000000000000000..a2adb565ce73ca45ed65a96f72b7d9d7eae98259 GIT binary patch literal 1395 zcmaJ=e^1j;6g_VnE7U1$Km`#*K-oYj_zQ73oQot&1{yIWhQA(N1CMpDrtMJtRQeAX zjeY<>l<~f{fY}&H`|j)QJNMjs?z?~f{`d`G1+N$;E$-XC`H_2;?eIOvHY;vR2Hx>Z z#j|)-+Z%oXj?(C#%0}eMcs6L^X)nM47FD47PnX`G7Pkb(~x2q zzcQqTG(&n#ND-76lKH|;2EFLh(F2_!6&whkVX7Vw3|j}b)!1@_t(M~uEpOU%kYP4|Ny9EQ-&R5Pi%P*;7}GI|+YH$b z^)*Z|=u}oc0x%5a3;zX+4zIGYh zK);Rz3L#lX&ZO@pm>~=+%mcbWzT2X!PUY5OeK+lZ*LlFH*@@;+k_IE87EoIoYLg`Y zXQ6gWBzOm*ni&l%3|c%eBw0K{i;aM;NX4xaU9RR@+}YutP=NPpVaQaaw7rVMeVRdr z{@2oST%TG)IUTrlhQ4jFFL}`N=&-&WX1*a5a`YVysc70e!WR^(ETtRpK)BM!G8NiG z*=v-svNR*K7U^qR)ug<0r?~cwHVkXDW+Erq>-3dX0+UAp7RV;Z_Kf{P_A6~7fDt)k zfMUsEkp5*sI>*owQsF6MK6mpw#(yIFVg3Zu>OIB%_E^SwECU8+!3N5dMm~AA5J%(Y|F=h$?V2VsXW-+?9Sg{!cOikE76K}AsM8l}4mQ{uI>+tkau7 g5-&sh9A&Z-gf+>xw2NdF%i;47kE(c#CsjQC2iPJ&`~Uy| literal 0 HcmV?d00001 diff --git a/src/test/resources/precompiled/parcelable/parcelable.txt b/src/test/resources/precompiled/parcelable/parcelable.txt new file mode 100644 index 00000000..e69de29b From 9112e783abe838f0f3436e9b39888664dbda5b2c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 25 Jun 2025 02:00:16 +0800 Subject: [PATCH 7/9] Link the builtin BCV (#301) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66355e40..822ec544 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ > [!WARNING] > The development of a separate binary compatibility validator Gradle plugin has been discontinued, -> and all its functionality will be moved to Kotlin Gradle Plugin starting from the `2.2.0` release. +> and all its functionality will be moved to Kotlin Gradle Plugin starting from the [`2.2.0` release](https://kotlinlang.org/docs/whatsnew22.html#binary-compatibility-validation-included-in-kotlin-gradle-plugin). > > As part of the migration, the code of the current plugin has been migrated to [the Kotlin repository](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/abi-validation), > as well as issues migrated to [the Kotlin project in YouTrack](https://youtrack.jetbrains.com/issues/KT?q=subsystems:%20%7BTools.%20BCV%7D,%20%7BTools.%20Gradle.%20BCV%7D). From a89014fd993c38c4264e9d272c98cef86f853aca Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 24 Jun 2025 15:32:40 -0400 Subject: [PATCH 8/9] Update dependencies (#302) --- build.gradle.kts | 8 ++++---- gradle/libs.versions.toml | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ca84df73..2296343a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -import kotlinx.kover.gradle.plugin.dsl.MetricType +import kotlinx.kover.gradle.plugin.dsl.CoverageUnit import kotlinx.validation.build.mavenCentralMetadata import kotlinx.validation.build.mavenRepositoryPublishing import kotlinx.validation.build.signPublicationIfKeyPresent @@ -215,7 +215,7 @@ tasks.withType().configureEach { } kover { - koverReport { + reports { filters { excludes { packages("kotlinx.validation.test") @@ -223,8 +223,8 @@ kover { } verify { rule { - minBound(80, MetricType.BRANCH) - minBound(90, MetricType.LINE) + minBound(80, CoverageUnit.BRANCH) + minBound(90, CoverageUnit.LINE) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf4e6684..22bdfcbb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,9 +4,8 @@ kotlin = "1.9.22" javaDiffUtils = "4.12" junit = "5.9.2" -kotest = "5.5.5" -kotlinx-bcv = "0.13.1" -ow2Asm = "9.7.1" +kotlinx-bcv = "0.17.0" +ow2Asm = "9.8" dokka = "1.9.20" gradlePluginPublishPlugin = "1.1.0" @@ -44,5 +43,5 @@ gradlePlugin-android = { module = "com.android.tools.build:gradle", version.ref [plugins] -kover = { id = "org.jetbrains.kotlinx.kover", version = "0.7.5" } +kover = { id = "org.jetbrains.kotlinx.kover", version = "0.9.1" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } From 52529219f13caf22848fc349c9f882ab49e11aac Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 24 Jun 2025 15:53:06 -0400 Subject: [PATCH 9/9] Release 0.18.0 (#303) --- README.md | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 822ec544..cc26af4e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Binary compatibility validator is a Gradle plugin that can be added to your buil - in `build.gradle.kts` ```kotlin plugins { - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.17.0" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.18.0" } ``` @@ -59,7 +59,7 @@ plugins { ```groovy plugins { - id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.17.0' + id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.18.0' } ``` diff --git a/gradle.properties b/gradle.properties index 948cb450..54e40fdc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=0.17.1-SNAPSHOT +version=0.19.0-SNAPSHOT kotlin.stdlib.default.dependency=false