Skip to content

Commit 218728f

Browse files
committed
Merge branch 'develop'
2 parents 7124ff9 + 10f8e7e commit 218728f

File tree

113 files changed

+6819
-173
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+6819
-173
lines changed

Module.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Module binary-compatibility-validator
2+
3+
Binary compatibility validator allows dumping Kotlin library ABI (both for JVM libraries and KLibs)
4+
that is public in the sense of Kotlin visibilities and ensures that the public ABI
5+
wasn't changed in a way that makes this change binary incompatible.
6+
7+
# Package kotlinx.validation
8+
9+
Provides common declarations, Gradle plugin tasks and extensions.
10+
11+
# Package kotlinx.validation.api
12+
13+
Provides an API for dumping Kotlin Java libraries public ABI.
14+
15+
# Package kotlinx.validation.api.klib
16+
17+
Provides an API for dumping Kotlin libraries (KLibs) public ABI and managing resulting dumps.
18+
19+
**This package is experimental, both API and behaviour may change in the future. There are also no guarantees on preserving the behavior of the API until its
20+
stabilization.**

README.md

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
33
[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/binary-compatibility-validator)](https://central.sonatype.com/search?q=org.jetbrains.kotlinx.binary-compatibility-validator)
44
[![License](https://img.shields.io/github/license/Kotlin/binary-compatibility-validator)](LICENSE.TXT)
5+
[![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/binary-compatibility-validator/)
56

67
# Binary compatibility validator
78

@@ -14,6 +15,7 @@ The tool allows dumping binary API of a JVM part of a Kotlin library that is pub
1415
* [Tasks](#tasks)
1516
* [Optional parameters](#optional-parameters)
1617
* [Workflow](#workflow)
18+
* [Experimental KLib ABI validation support](#experimental-klib-abi-validation-support)
1719
* [What constitutes the public API](#what-constitutes-the-public-api)
1820
* [Classes](#classes)
1921
* [Members](#members)
@@ -33,15 +35,15 @@ Binary compatibility validator is a Gradle plugin that can be added to your buil
3335
- in `build.gradle.kts`
3436
```kotlin
3537
plugins {
36-
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.14.0"
38+
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.15.0-Beta.1"
3739
}
3840
```
3941

4042
- in `build.gradle`
4143

4244
```groovy
4345
plugins {
44-
id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.14.0'
46+
id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.15.0-Beta.1'
4547
}
4648
```
4749

@@ -146,7 +148,7 @@ apiValidation {
146148
By default, binary compatibility validator analyzes project output class files from `build/classes` directory when building an API dump.
147149
If you pack these classes into an output jar not in a regular way, for example, by excluding certain classes, applying `shadow` plugin, and so on,
148150
the API dump built from the original class files may no longer reflect the resulting jar contents accurately.
149-
In that case, it makes sense to use the resulting jar as an input of the `apuBuild` task:
151+
In that case, it makes sense to use the resulting jar as an input of the `apiBuild` task:
150152

151153
Kotlin
152154
```kotlin
@@ -180,6 +182,59 @@ When starting to validate your library public API, we recommend the following wo
180182
the resulting diff in `.api` file should be verified: only signatures you expected to change should be changed.
181183
* Commit the resulting `.api` diff along with code changes.
182184

185+
### Experimental KLib ABI validation support
186+
187+
The KLib validation support is experimental and is a subject to change (applies to both an API and the ABI dump format).
188+
A project has to use Kotlin 1.9.20 or newer to use this feature.
189+
190+
To validate public ABI of a Kotlin library (KLib) corresponding option should be enabled explicitly:
191+
```kotlin
192+
apiValidation {
193+
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
194+
klib {
195+
enabled = true
196+
}
197+
}
198+
```
199+
200+
When enabled, KLib support adds additional dependencies to existing `apiDump` and `apiCheck` tasks.
201+
Generate KLib ABI dumps are places alongside JVM dumps (in `api` subfolder, by default)
202+
in files named `<project name>.klib.api`.
203+
The dump file combines all dumps generated for individual targets with declarations specific to some targets being
204+
annotated with corresponding target names.
205+
During the validation phase, that file is compared to the dump extracted from the latest version of the library,
206+
and any differences between these two files are reported as errors.
207+
208+
Currently, all options described in [Optional parameters](#optional-parameters) section are supported for klibs too.
209+
The only caveat here is that all class names should be specified in the JVM-format,
210+
like `package.name.ClassName$SubclassName`.
211+
212+
Please refer to a [design document](docs/design/KLibSupport.md) for details on the format and rationale behind the
213+
current implementation.
214+
215+
#### KLib ABI dump generation and validation on Linux and Windows hosts
216+
217+
Currently, compilation to Apple-specific targets (like `iosArm64` or `watchosX86`) supported only on Apple hosts.
218+
To ease the development on Windows and Linux hosts, binary compatibility validator does not validate ABI for targets
219+
not supported on the current host, even if `.klib.api` file contains declarations for these targets.
220+
221+
This behavior could be altered to force an error when klibs for some targets could not be compiled:
222+
```kotlin
223+
apiValidation {
224+
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
225+
klib {
226+
enabled = true
227+
// treat a target being unsupported on a host as an error
228+
strictValidation = true
229+
}
230+
}
231+
```
232+
233+
When it comes to dump generation (`apiDump` task) on non-Apple hosts, binary compatibility validator attempts
234+
to infer an ABI from dumps generated for supported targets and an old dump from project's `api` folder (if any).
235+
Inferred dump may not match an actual dump,
236+
and it is recommended to update a dump on hosts supporting all required targets, if possible.
237+
183238
# What constitutes the public API
184239

185240
### Classes

api/binary-compatibility-validator.api

Lines changed: 120 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ public class kotlinx/validation/ApiValidationExtension {
55
public final fun getIgnoredClasses ()Ljava/util/Set;
66
public final fun getIgnoredPackages ()Ljava/util/Set;
77
public final fun getIgnoredProjects ()Ljava/util/Set;
8+
public final fun getKlib ()Lkotlinx/validation/KlibValidationSettings;
89
public final fun getNonPublicMarkers ()Ljava/util/Set;
910
public final fun getPublicClasses ()Ljava/util/Set;
1011
public final fun getPublicMarkers ()Ljava/util/Set;
1112
public final fun getPublicPackages ()Ljava/util/Set;
1213
public final fun getValidationDisabled ()Z
14+
public final fun klib (Lkotlin/jvm/functions/Function1;)V
1315
public final fun setAdditionalSourceSets (Ljava/util/Set;)V
1416
public final fun setApiDumpDirectory (Ljava/lang/String;)V
1517
public final fun setIgnoredClasses (Ljava/util/Set;)V
@@ -28,32 +30,59 @@ public final class kotlinx/validation/BinaryCompatibilityValidatorPlugin : org/g
2830
public fun apply (Lorg/gradle/api/Project;)V
2931
}
3032

33+
public abstract class kotlinx/validation/BuildTaskBase : org/gradle/api/DefaultTask {
34+
public field outputApiFile Ljava/io/File;
35+
public fun <init> ()V
36+
public final fun getIgnoredClasses ()Ljava/util/Set;
37+
public final fun getIgnoredPackages ()Ljava/util/Set;
38+
public final fun getNonPublicMarkers ()Ljava/util/Set;
39+
public final fun getOutputApiFile ()Ljava/io/File;
40+
public final fun getPublicClasses ()Ljava/util/Set;
41+
public final fun getPublicMarkers ()Ljava/util/Set;
42+
public final fun getPublicPackages ()Ljava/util/Set;
43+
public final fun setIgnoredClasses (Ljava/util/Set;)V
44+
public final fun setIgnoredPackages (Ljava/util/Set;)V
45+
public final fun setNonPublicMarkers (Ljava/util/Set;)V
46+
public final fun setOutputApiFile (Ljava/io/File;)V
47+
public final fun setPublicClasses (Ljava/util/Set;)V
48+
public final fun setPublicMarkers (Ljava/util/Set;)V
49+
public final fun setPublicPackages (Ljava/util/Set;)V
50+
}
51+
52+
public abstract interface annotation class kotlinx/validation/ExperimentalBCVApi : java/lang/annotation/Annotation {
53+
}
54+
3155
public abstract interface annotation class kotlinx/validation/ExternalApi : java/lang/annotation/Annotation {
3256
}
3357

34-
public class kotlinx/validation/KotlinApiBuildTask : org/gradle/api/DefaultTask {
58+
public class kotlinx/validation/KlibValidationSettings {
59+
public fun <init> ()V
60+
public final fun getEnabled ()Z
61+
public final fun getSignatureVersion ()Lkotlinx/validation/api/klib/KlibSignatureVersion;
62+
public final fun getStrictValidation ()Z
63+
public final fun setEnabled (Z)V
64+
public final fun setSignatureVersion (Lkotlinx/validation/api/klib/KlibSignatureVersion;)V
65+
public final fun setStrictValidation (Z)V
66+
}
67+
68+
public class kotlinx/validation/KotlinApiBuildTask : kotlinx/validation/BuildTaskBase {
3569
public field inputDependencies Lorg/gradle/api/file/FileCollection;
36-
public field outputApiDir Ljava/io/File;
3770
public fun <init> ()V
3871
public final fun getInputClassesDirs ()Lorg/gradle/api/file/FileCollection;
3972
public final fun getInputDependencies ()Lorg/gradle/api/file/FileCollection;
4073
public final fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty;
41-
public final fun getOutputApiDir ()Ljava/io/File;
4274
public final fun setInputClassesDirs (Lorg/gradle/api/file/FileCollection;)V
4375
public final fun setInputDependencies (Lorg/gradle/api/file/FileCollection;)V
44-
public final fun setOutputApiDir (Ljava/io/File;)V
4576
}
4677

4778
public class kotlinx/validation/KotlinApiCompareTask : org/gradle/api/DefaultTask {
48-
public field apiBuildDir Ljava/io/File;
79+
public field generatedApiFile Ljava/io/File;
80+
public field projectApiFile Ljava/io/File;
4981
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
50-
public final fun getApiBuildDir ()Ljava/io/File;
51-
public final fun getDummyOutputFile ()Ljava/io/File;
52-
public final fun getNonExistingProjectApiDir ()Ljava/lang/String;
53-
public final fun getProjectApiDir ()Ljava/io/File;
54-
public final fun setApiBuildDir (Ljava/io/File;)V
55-
public final fun setNonExistingProjectApiDir (Ljava/lang/String;)V
56-
public final fun setProjectApiDir (Ljava/io/File;)V
82+
public final fun getGeneratedApiFile ()Ljava/io/File;
83+
public final fun getProjectApiFile ()Ljava/io/File;
84+
public final fun setGeneratedApiFile (Ljava/io/File;)V
85+
public final fun setProjectApiFile (Ljava/io/File;)V
5786
}
5887

5988
public final class kotlinx/validation/api/ClassBinarySignature {
@@ -79,3 +108,82 @@ public final class kotlinx/validation/api/KotlinSignaturesLoadingKt {
79108
public static synthetic fun retainExplicitlyIncludedIfDeclared$default (Ljava/util/List;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;ILjava/lang/Object;)Ljava/util/List;
80109
}
81110

111+
public final class kotlinx/validation/api/klib/KlibDump {
112+
public static final field Companion Lkotlinx/validation/api/klib/KlibDump$Companion;
113+
public fun <init> ()V
114+
public final fun copy ()Lkotlinx/validation/api/klib/KlibDump;
115+
public final fun getTargets ()Ljava/util/Set;
116+
public final fun merge (Ljava/io/File;Ljava/lang/String;)V
117+
public final fun merge (Lkotlinx/validation/api/klib/KlibDump;)V
118+
public static synthetic fun merge$default (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;ILjava/lang/Object;)V
119+
public final fun remove (Ljava/lang/Iterable;)V
120+
public final fun retain (Ljava/lang/Iterable;)V
121+
public final fun saveTo (Ljava/lang/Appendable;)V
122+
}
123+
124+
public final class kotlinx/validation/api/klib/KlibDump$Companion {
125+
public final fun from (Ljava/io/File;Ljava/lang/String;)Lkotlinx/validation/api/klib/KlibDump;
126+
public static synthetic fun from$default (Lkotlinx/validation/api/klib/KlibDump$Companion;Ljava/io/File;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
127+
public final fun fromKlib (Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;)Lkotlinx/validation/api/klib/KlibDump;
128+
public static synthetic fun fromKlib$default (Lkotlinx/validation/api/klib/KlibDump$Companion;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
129+
}
130+
131+
public final class kotlinx/validation/api/klib/KlibDumpFilters {
132+
public static final field Companion Lkotlinx/validation/api/klib/KlibDumpFilters$Companion;
133+
public final fun getIgnoredClasses ()Ljava/util/Set;
134+
public final fun getIgnoredPackages ()Ljava/util/Set;
135+
public final fun getNonPublicMarkers ()Ljava/util/Set;
136+
public final fun getSignatureVersion ()Lkotlinx/validation/api/klib/KlibSignatureVersion;
137+
}
138+
139+
public final class kotlinx/validation/api/klib/KlibDumpFilters$Builder {
140+
public fun <init> ()V
141+
public final fun build ()Lkotlinx/validation/api/klib/KlibDumpFilters;
142+
public final fun getIgnoredClasses ()Ljava/util/Set;
143+
public final fun getIgnoredPackages ()Ljava/util/Set;
144+
public final fun getNonPublicMarkers ()Ljava/util/Set;
145+
public final fun getSignatureVersion ()Lkotlinx/validation/api/klib/KlibSignatureVersion;
146+
public final fun setSignatureVersion (Lkotlinx/validation/api/klib/KlibSignatureVersion;)V
147+
}
148+
149+
public final class kotlinx/validation/api/klib/KlibDumpFilters$Companion {
150+
public final fun getDEFAULT ()Lkotlinx/validation/api/klib/KlibDumpFilters;
151+
}
152+
153+
public final class kotlinx/validation/api/klib/KlibDumpFiltersKt {
154+
public static final fun KLibDumpFilters (Lkotlin/jvm/functions/Function1;)Lkotlinx/validation/api/klib/KlibDumpFilters;
155+
}
156+
157+
public final class kotlinx/validation/api/klib/KlibDumpKt {
158+
public static final fun inferAbi (Lkotlinx/validation/api/klib/KlibTarget;Ljava/lang/Iterable;Lkotlinx/validation/api/klib/KlibDump;)Lkotlinx/validation/api/klib/KlibDump;
159+
public static synthetic fun inferAbi$default (Lkotlinx/validation/api/klib/KlibTarget;Ljava/lang/Iterable;Lkotlinx/validation/api/klib/KlibDump;ILjava/lang/Object;)Lkotlinx/validation/api/klib/KlibDump;
160+
public static final fun mergeFromKlib (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;)V
161+
public static synthetic fun mergeFromKlib$default (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;Ljava/lang/String;Lkotlinx/validation/api/klib/KlibDumpFilters;ILjava/lang/Object;)V
162+
public static final fun saveTo (Lkotlinx/validation/api/klib/KlibDump;Ljava/io/File;)V
163+
}
164+
165+
public final class kotlinx/validation/api/klib/KlibSignatureVersion {
166+
public static final field Companion Lkotlinx/validation/api/klib/KlibSignatureVersion$Companion;
167+
public fun equals (Ljava/lang/Object;)Z
168+
public fun hashCode ()I
169+
public fun toString ()Ljava/lang/String;
170+
}
171+
172+
public final class kotlinx/validation/api/klib/KlibSignatureVersion$Companion {
173+
public final fun getLATEST ()Lkotlinx/validation/api/klib/KlibSignatureVersion;
174+
public final fun of (I)Lkotlinx/validation/api/klib/KlibSignatureVersion;
175+
}
176+
177+
public final class kotlinx/validation/api/klib/KlibTarget {
178+
public static final field Companion Lkotlinx/validation/api/klib/KlibTarget$Companion;
179+
public fun equals (Ljava/lang/Object;)Z
180+
public final fun getConfigurableName ()Ljava/lang/String;
181+
public final fun getTargetName ()Ljava/lang/String;
182+
public fun hashCode ()I
183+
public fun toString ()Ljava/lang/String;
184+
}
185+
186+
public final class kotlinx/validation/api/klib/KlibTarget$Companion {
187+
public final fun parse (Ljava/lang/String;)Lkotlinx/validation/api/klib/KlibTarget;
188+
}
189+

build.gradle.kts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import kotlinx.kover.gradle.plugin.dsl.MetricType
12
import kotlinx.validation.build.mavenCentralMetadata
23
import kotlinx.validation.build.mavenRepositoryPublishing
34
import kotlinx.validation.build.signPublicationIfKeyPresent
45
import org.gradle.api.attributes.TestSuiteType.FUNCTIONAL_TEST
6+
import org.jetbrains.dokka.gradle.DokkaTask
7+
import org.jetbrains.dokka.gradle.DokkaTaskPartial
58
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
69
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
10+
import java.net.URL
711

812
plugins {
913
kotlin("jvm")
@@ -14,6 +18,8 @@ plugins {
1418
`maven-publish`
1519
`jvm-test-suite`
1620
id("org.jetbrains.kotlinx.binary-compatibility-validator")
21+
alias(libs.plugins.kover)
22+
alias(libs.plugins.dokka)
1723
}
1824

1925
group = "org.jetbrains.kotlinx"
@@ -60,6 +66,7 @@ val createClasspathManifest = tasks.register("createClasspathManifest") {
6066
dependencies {
6167
implementation(gradleApi())
6268
implementation(libs.kotlinx.metadata)
69+
compileOnly(libs.kotlin.compiler.embeddable)
6370
implementation(libs.ow2.asm)
6471
implementation(libs.ow2.asmTree)
6572
implementation(libs.javaDiffUtils)
@@ -76,7 +83,6 @@ dependencies {
7683

7784
tasks.compileKotlin {
7885
compilerOptions {
79-
freeCompilerArgs.add("-Xexplicit-api=strict")
8086
allWarningsAsErrors.set(true)
8187
@Suppress("DEPRECATION") // Compatibility with Gradle 7 requires Kotlin 1.4
8288
languageVersion.set(KotlinVersion.KOTLIN_1_4)
@@ -85,7 +91,9 @@ tasks.compileKotlin {
8591
// Suppressing "w: Language version 1.4 is deprecated and its support will be removed" message
8692
// because LV=1.4 in practice is mandatory as it is a default language version in Gradle 7.0+ for users' kts scripts.
8793
freeCompilerArgs.addAll(
88-
"-Xsuppress-version-warnings"
94+
"-Xexplicit-api=strict",
95+
"-Xsuppress-version-warnings",
96+
"-Xopt-in=kotlin.RequiresOptIn"
8997
)
9098
}
9199
}
@@ -107,6 +115,7 @@ tasks.compileTestKotlin {
107115
tasks.withType<Test>().configureEach {
108116
systemProperty("overwrite.output", System.getProperty("overwrite.output", "false"))
109117
systemProperty("testCasesClassesDirs", sourceSets.test.get().output.classesDirs.asPath)
118+
systemProperty("kover.enabled", project.findProperty("kover.enabled")?.toString().toBoolean())
110119
jvmArgs("-ea")
111120
}
112121

@@ -159,6 +168,7 @@ testing {
159168
implementation(project())
160169
implementation(libs.assertJ.core)
161170
implementation(libs.kotlin.test)
171+
implementation(libs.kotlin.compiler.embeddable)
162172
}
163173
}
164174

@@ -195,3 +205,39 @@ testing {
195205
tasks.withType<Sign>().configureEach {
196206
onlyIf("only sign if signatory is present") { signatory?.keyId != null }
197207
}
208+
209+
kover {
210+
koverReport {
211+
filters {
212+
excludes {
213+
packages("kotlinx.validation.test")
214+
}
215+
}
216+
verify {
217+
rule {
218+
minBound(80, MetricType.BRANCH)
219+
minBound(90, MetricType.LINE)
220+
}
221+
}
222+
}
223+
// Unfortunately, we can't test both configuration cache use and the test coverage
224+
// simultaneously, so the coverage collection should be enabled explicitly (and that
225+
// will disable configuration cache).
226+
if (!project.findProperty("kover.enabled")?.toString().toBoolean()) {
227+
disable()
228+
}
229+
}
230+
231+
232+
tasks.withType<DokkaTask>().configureEach {
233+
dokkaSourceSets.configureEach {
234+
includes.from("Module.md")
235+
236+
sourceLink {
237+
localDirectory.set(rootDir)
238+
remoteUrl.set(URL("https://github.com/Kotlin/binary-compatibility-validator/tree/master"))
239+
remoteLineSuffix.set("#L")
240+
}
241+
samples.from("src/test/kotlin/samples/KlibDumpSamples.kt")
242+
}
243+
}

0 commit comments

Comments
 (0)