[!IMPORTANT] This plugin is in maintenance mode.
Fixes for critical bugs and support for current versions of Kotlin continue to be delivered.
The implementation of new features has been stopped in favor of adding them to the experimental binary compatibility validation in the Kotlin Gradle plugin
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.
Binary compatibility validator plugin requires Gradle 6.1.1 or newer.
Kotlin version 1.6.20 or newer.
Binary compatibility validator is a Gradle plugin that can be added to your build in the following way:
build.gradle.ktsplugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.18.1"
}
build.gradleplugins {
id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.18.1'
}
It is enough to apply the plugin only to the root project build file; all sub-projects will be configured automatically.
The plugin provides two tasks:
apiDump — builds the project and dumps its public API in project api subfolder.
API is dumped in a human-readable format. If API dump already exists, it will be overwritten.apiCheck — builds the project and checks that project's public API is the same as golden value
in project api subfolder. This task is automatically inserted into check pipeline, so both build and check
tasks will start checking public API upon their execution.For projects with multiple JVM targets, multiple subfolders will be created, e.g.
api/jvmandapi/android
Binary compatibility validator can be additionally configured with the following DSL:
Groovy
apiValidation {
/**
* Packages that are excluded from public API dumps even if they
* contain public API.
*/
ignoredPackages += ["kotlinx.coroutines.internal"]
/**
* Sub-projects that are excluded from API validation
*/
ignoredProjects += ["benchmarks", "examples"]
/**
* Classes (fully qualified) that are excluded from public API dumps even if they
* contain public API.
*/
ignoredClasses += ["com.company.BuildConfig"]
/**
* Set of annotations that exclude API from being public.
* Typically, it is all kinds of `@InternalApi` annotations that mark
* effectively private API that cannot be actually private for technical reasons.
*/
nonPublicMarkers += ["my.package.MyInternalApiAnnotation"]
/**
* Flag to programmatically disable compatibility validator
*/
validationDisabled = true
/**
* A path to a subdirectory inside the project root directory where dumps should be stored.
*/
apiDumpDirectory = "api"
}
Kotlin
apiValidation {
/**
* Packages that are excluded from public API dumps even if they
* contain public API.
*/
ignoredPackages.add("kotlinx.coroutines.internal")
/**
* Sub-projects that are excluded from API validation
*/
ignoredProjects.addAll(listOf("benchmarks", "examples"))
/**
* Classes (fully qualified) that are excluded from public API dumps even if they
* contain public API.
*/
ignoredClasses.add("com.company.BuildConfig")
/**
* Set of annotations that exclude API from being public.
* Typically, it is all kinds of `@InternalApi` annotations that mark
* effectively private API that cannot be actually private for technical reasons.
*/
nonPublicMarkers.add("my.package.MyInternalApiAnnotation")
/**
* Flag to programmatically disable compatibility validator
*/
validationDisabled = false
/**
* A path to a subdirectory inside the project root directory where dumps should be stored.
*/
apiDumpDirectory = "aux/validation"
}
By default, binary compatibility validator analyzes project output class files from build/classes directory when building an API dump.
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,
the API dump built from the original class files may no longer reflect the resulting jar contents accurately.
In that case, it makes sense to use the resulting jar as an input of the apiBuild task:
Kotlin
tasks {
apiBuild {
// "jar" here is the name of the default Jar task producing the resulting jar file
// in a multiplatform project it can be named "jvmJar"
// if you applied the shadow plugin, it creates the "shadowJar" task that produces the transformed jar
inputJar.value(jar.flatMap { it.archiveFile })
}
}
When starting to validate your library public API, we recommend the following workflow:
Preparation phase (one-time action):
apiDump..api files to your VCS.check task will validate public API along with test run and will fail
the build if API differs.Regular workflow
check task on your CI will validate everything.check task will start to fail. apiDump should be executed manually,
the resulting diff in .api file should be verified: only signatures you expected to change should be changed..api diff along with code changes.The KLib validation support is experimental and is a subject to change (applies to both an API and the ABI dump format). A project has to use Kotlin 1.9.20 or newer to use this feature.
To validate public ABI of a Kotlin library (KLib) corresponding option should be enabled explicitly:
apiValidation {
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
klib {
enabled = true
}
}
When enabled, KLib support adds additional dependencies to existing apiDump and apiCheck tasks.
Generate KLib ABI dumps are places alongside JVM dumps (in api subfolder, by default)
in files named <project name>.klib.api.
The dump file combines all dumps generated for individual targets with declarations specific to some targets being
annotated with corresponding target names.
During the validation phase, that file is compared to the dump extracted from the latest version of the library,
and any differences between these two files are reported as errors.
Generated ABI dumps include a library name,
so it's recommended
to set Gradle's rootProject.name for your library.
Without declaring the root project's name, Gradle defaults to using the project's directory name, which can lead to
unstable validation behavior due to potential mismatches in naming.
Currently, all options described in Optional parameters section are supported for klibs too.
The only caveat here is that all class names should be specified in the JVM-format,
like package.name.ClassName$SubclassName.
Please refer to a design document for details on the format and rationale behind the current implementation.
Currently, compilation to Apple-specific targets (like iosArm64 or watchosX86) supported only on Apple hosts.
To ease the development on Windows and Linux hosts, binary compatibility validator does not validate ABI for targets
not supported on the current host, even if .klib.api file contains declarations for these targets.
This behavior could be altered to force an error when klibs for some targets could not be compiled:
apiValidation {
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
klib {
enabled = true
// treat a target being unsupported on a host as an error
strictValidation = true
}
}
When it comes to dump generation (apiDump task) on non-Apple hosts, binary compatibility validator attempts
to infer an ABI from dumps generated for supported targets and an old dump from project's api folder (if any).
Inferred dump may not match an actual dump,
and it is recommended to update a dump on hosts supporting all required targets, if possible.
A class is considered to be effectively public if all the following conditions are met:
ACC_PUBLIC or ACC_PROTECTED)PublishedApiwhen tableswitches ($WhenMappings)A member of the class (i.e. a field or a method) is considered to be effectively public if all the following conditions are met:
it has public or protected JVM access (ACC_PUBLIC or ACC_PROTECTED)
it has one of the following visibilities in Kotlin:
PublishedApiNote that Kotlin visibility of a field exposed by
lateinitproperty is the visibility of its setter.
in case if the member is protected, it is contained in non-final class
it isn't a synthetic access method for a private field
For a class a binary incompatible change is:
ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the class visibilityACC_FINAL — making non-final class finalACC_ABSTRACT — making non-abstract class abstractACC_INTERFACE — changing class to interface and vice versaACC_ANNOTATION — changing annotation to interface and vice versaFor a class member a binary incompatible change is:
ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the member visibilityACC_FINAL — making non-final field or method finalACC_ABSTRACT — making non-abstract method abstractACC_STATIC — changing instance member to static and vice versaIn order to build and run tests in the project in IDE, two prerequisites are required:
Read the Contributing Guidelines.
Kotlin
99.6%
[!IMPORTANT] This plugin is in maintenance mode.
Fixes for critical bugs and support for current versions of Kotlin continue to be delivered.
The implementation of new features has been stopped in favor of adding them to the experimental binary compatibility validation in the Kotlin Gradle plugin
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.
Binary compatibility validator plugin requires Gradle 6.1.1 or newer.
Kotlin version 1.6.20 or newer.
Binary compatibility validator is a Gradle plugin that can be added to your build in the following way:
build.gradle.ktsplugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.18.1"
}
build.gradleplugins {
id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.18.1'
}
It is enough to apply the plugin only to the root project build file; all sub-projects will be configured automatically.
The plugin provides two tasks:
apiDump — builds the project and dumps its public API in project api subfolder.
API is dumped in a human-readable format. If API dump already exists, it will be overwritten.apiCheck — builds the project and checks that project's public API is the same as golden value
in project api subfolder. This task is automatically inserted into check pipeline, so both build and check
tasks will start checking public API upon their execution.For projects with multiple JVM targets, multiple subfolders will be created, e.g.
api/jvmandapi/android
Binary compatibility validator can be additionally configured with the following DSL:
Groovy
apiValidation {
/**
* Packages that are excluded from public API dumps even if they
* contain public API.
*/
ignoredPackages += ["kotlinx.coroutines.internal"]
/**
* Sub-projects that are excluded from API validation
*/
ignoredProjects += ["benchmarks", "examples"]
/**
* Classes (fully qualified) that are excluded from public API dumps even if they
* contain public API.
*/
ignoredClasses += ["com.company.BuildConfig"]
/**
* Set of annotations that exclude API from being public.
* Typically, it is all kinds of `@InternalApi` annotations that mark
* effectively private API that cannot be actually private for technical reasons.
*/
nonPublicMarkers += ["my.package.MyInternalApiAnnotation"]
/**
* Flag to programmatically disable compatibility validator
*/
validationDisabled = true
/**
* A path to a subdirectory inside the project root directory where dumps should be stored.
*/
apiDumpDirectory = "api"
}
Kotlin
apiValidation {
/**
* Packages that are excluded from public API dumps even if they
* contain public API.
*/
ignoredPackages.add("kotlinx.coroutines.internal")
/**
* Sub-projects that are excluded from API validation
*/
ignoredProjects.addAll(listOf("benchmarks", "examples"))
/**
* Classes (fully qualified) that are excluded from public API dumps even if they
* contain public API.
*/
ignoredClasses.add("com.company.BuildConfig")
/**
* Set of annotations that exclude API from being public.
* Typically, it is all kinds of `@InternalApi` annotations that mark
* effectively private API that cannot be actually private for technical reasons.
*/
nonPublicMarkers.add("my.package.MyInternalApiAnnotation")
/**
* Flag to programmatically disable compatibility validator
*/
validationDisabled = false
/**
* A path to a subdirectory inside the project root directory where dumps should be stored.
*/
apiDumpDirectory = "aux/validation"
}
By default, binary compatibility validator analyzes project output class files from build/classes directory when building an API dump.
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,
the API dump built from the original class files may no longer reflect the resulting jar contents accurately.
In that case, it makes sense to use the resulting jar as an input of the apiBuild task:
Kotlin
tasks {
apiBuild {
// "jar" here is the name of the default Jar task producing the resulting jar file
// in a multiplatform project it can be named "jvmJar"
// if you applied the shadow plugin, it creates the "shadowJar" task that produces the transformed jar
inputJar.value(jar.flatMap { it.archiveFile })
}
}
When starting to validate your library public API, we recommend the following workflow:
Preparation phase (one-time action):
apiDump..api files to your VCS.check task will validate public API along with test run and will fail
the build if API differs.Regular workflow
check task on your CI will validate everything.check task will start to fail. apiDump should be executed manually,
the resulting diff in .api file should be verified: only signatures you expected to change should be changed..api diff along with code changes.The KLib validation support is experimental and is a subject to change (applies to both an API and the ABI dump format). A project has to use Kotlin 1.9.20 or newer to use this feature.
To validate public ABI of a Kotlin library (KLib) corresponding option should be enabled explicitly:
apiValidation {
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
klib {
enabled = true
}
}
When enabled, KLib support adds additional dependencies to existing apiDump and apiCheck tasks.
Generate KLib ABI dumps are places alongside JVM dumps (in api subfolder, by default)
in files named <project name>.klib.api.
The dump file combines all dumps generated for individual targets with declarations specific to some targets being
annotated with corresponding target names.
During the validation phase, that file is compared to the dump extracted from the latest version of the library,
and any differences between these two files are reported as errors.
Generated ABI dumps include a library name,
so it's recommended
to set Gradle's rootProject.name for your library.
Without declaring the root project's name, Gradle defaults to using the project's directory name, which can lead to
unstable validation behavior due to potential mismatches in naming.
Currently, all options described in Optional parameters section are supported for klibs too.
The only caveat here is that all class names should be specified in the JVM-format,
like package.name.ClassName$SubclassName.
Please refer to a design document for details on the format and rationale behind the current implementation.
Currently, compilation to Apple-specific targets (like iosArm64 or watchosX86) supported only on Apple hosts.
To ease the development on Windows and Linux hosts, binary compatibility validator does not validate ABI for targets
not supported on the current host, even if .klib.api file contains declarations for these targets.
This behavior could be altered to force an error when klibs for some targets could not be compiled:
apiValidation {
@OptIn(kotlinx.validation.ExperimentalBCVApi::class)
klib {
enabled = true
// treat a target being unsupported on a host as an error
strictValidation = true
}
}
When it comes to dump generation (apiDump task) on non-Apple hosts, binary compatibility validator attempts
to infer an ABI from dumps generated for supported targets and an old dump from project's api folder (if any).
Inferred dump may not match an actual dump,
and it is recommended to update a dump on hosts supporting all required targets, if possible.
A class is considered to be effectively public if all the following conditions are met:
ACC_PUBLIC or ACC_PROTECTED)PublishedApiwhen tableswitches ($WhenMappings)A member of the class (i.e. a field or a method) is considered to be effectively public if all the following conditions are met:
it has public or protected JVM access (ACC_PUBLIC or ACC_PROTECTED)
it has one of the following visibilities in Kotlin:
PublishedApiNote that Kotlin visibility of a field exposed by
lateinitproperty is the visibility of its setter.
in case if the member is protected, it is contained in non-final class
it isn't a synthetic access method for a private field
For a class a binary incompatible change is:
ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the class visibilityACC_FINAL — making non-final class finalACC_ABSTRACT — making non-abstract class abstractACC_INTERFACE — changing class to interface and vice versaACC_ANNOTATION — changing annotation to interface and vice versaFor a class member a binary incompatible change is:
ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the member visibilityACC_FINAL — making non-final field or method finalACC_ABSTRACT — making non-abstract method abstractACC_STATIC — changing instance member to static and vice versaIn order to build and run tests in the project in IDE, two prerequisites are required:
Read the Contributing Guidelines.
Kotlin
99.6%