BCV-MU is a Gradle Plugin that validates the public JVM binary API of libraries, to make sure that breaking changes are tracked.
BCV-MU is based on Kotlin/binary-compatibility-validator, and contains improvements to better work with the Gradle API, especially in large projects.
Read more about the validation of the public API in the BCV project:
(The MU tag was chosen because I hope to banish this plugin to the Mirror Universe as soon the improvements here are merged upstream.)
Under-the-hood BCV-MU still uses the signature-generation code from BCV, but the Gradle plugin has been refactored to better follow the Gradle API, and generate API signatures more flexibly.
BCV-MU plugin can either be applied as to a Project in any build.gradle(.kts)
,
or (experimentally) as a Settings plugin in settings.gradle(.kts)
.
The minimal supported Gradle version is 7.6.
By default, BCV-MU uses BCV version 0.13.2
, which can be overridden, but may introduce runtime
errors.
BCV-MU can be applied to each subproject as a standard Gradle plugin.
// build.gradle.kts
plugins {
id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion"
}
To initialise the API declarations, run the Gradle task
./gradlew apiDump
This will produce API files into the ./api
directory of subprojects with the BCV-MU plugin.
These API declarations files must be committed to the repository.
To verify that the API declarations is up-to-date, run the Gradle task
./gradlew apiCheck
The apiCheck
task will also be run whenever the check
task is run.
BCV-MU can be configured in a similar manner to BCV:
// build.gradle.kts
plugins {
kotlin("jvm") version "1.8.10"
id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion"
}
binaryCompatibilityValidator {
// Explicitly include specific classes, markers, or packages.
// If any class, marker, or package is defined then all other declarations will be excluded.
// If no explicit public declarations are defined, then all declarations will be included by default.
publicClasses.add("com.company.api.FooPublicClass")
publicMarkers.add("com.company.api.ExplicitApiAnnotation")
publicPackages.add("com.company.api")
// Packages that are excluded from public API dumps even if they contain public API.
ignoredPackages.add("kotlinx.coroutines.internal")
// 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.
ignoredMarkers.add("my.package.MyInternalApiAnnotation")
// Disable or enable all BCV-MU tasks for this project
bcvEnabled.set(true)
// Override the default BCV version
kotlinxBinaryCompatibilityValidatorVersion.set("0.13.2")
}
BCV automatically generates 'targets' for each Kotlin/JVM source set that it finds.
these BCVTarget
s can be specifically modified, or manually defined, for fine-grained control.
// build.gradle.kts
plugins {
kotlin("jvm") version "1.8.10"
id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion"
`java-test-fixtures`
}
binaryCompatibilityValidator {
// these are the default values that will be used in all Targets
ignoredMarkers.add("my.package.MyFirstInternalApiAnnotation")
ignoredClasses.add("com.company.BuildConfig")
// BCV will automatically generate targets for each Kotlin/JVM target,
// and each can be configured manually for fine-grained control.
targets.configureEach {
// values can be appended to the default values
ignoredMarkers.add("my.package.MySecondInternalApiAnnotation")
// Or overridden
ignoredClasses.set(listOf())
}
// BCV will automatically register a target for testFixtures, but it must be enabled manually
targets.testFixtures {
enabled.set(true)
}
// BCV Targets can also be manually defined
targets.register("customTarget") {
inputJar.set(tasks.customTargetJar.flatMap { it.archiveFile })
}
}
To share common configuration it is best to set up a convention-plugin.
If you don't have any convention plugins already, then here's a quick guide.
First, set-up
buildSrc
by creating a file ./buildSrc/build.gradle.kts
In it, add the
kotlin-dsl
plugin,
and add the BCV-MU plugin as a dependency.
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
// add the *Maven coordinates* of the bcv-MU plugin, not the plugin ID, as a dependency
implementation("dev.adamko.kotlin.binary-compatibility-validator:bcv-gradle-plugin:$bcvMuVersion")
}
Next, create a convention plugin. This is where any shared configuration can be defined.
// buildSrc/src/main/kotlin/binary-compatibility-validator-convention.gradle.kts
plugins {
id("dev.adamko.kotlin.binary-compatibility-validator") // no version needed - it's defined in buildSrc/build.gradle.kts
}
binaryCompatibilityValidator {
ignoredClasses.add("com.company.BuildConfig")
}
Finally, apply the convention plugin to subprojects. This will automatically re-use the same configuration.
// ./some/subproject/build.gradle.kts
plugins {
id("binary-compatibility-validator-convention")
kotlin("jvm")
}
There is experimental support for applying BCV-MU as a Settings plugin.
This allows for applying BCV-MU to all subprojects in a Gradle-friendly way. All subprojects are included by default, and can be excluded using BCV-MU config.
// settings.gradle.kts
buildscript {
dependencies {
// BCV-MU requires the Kotlin Gradle Plugin classes are present
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.8.10")
}
}
plugins {
id("dev.adamko.kotlin.binary-compatibility-validator") version "$bcvMuVersion"
}
binaryCompatibilityValidator {
ignoredProjects.addAll(
":", // ignore root project
":some-subproject", // ignore subprojects explicitly
// or ignore using a glob pattern
":internal-dependencies:*",
":*-tasks:**",
)
// set the default values for all targets in all enabled-subprojects
defaultTargetValues {
enabled.convention(true)
ignoredClasses.set(listOf("com.package.MyIgnoredClass"))
ignoredMarkers.set(listOf("com.package.MyInternalApiAnnotationMarker"))
ignoredPackages.set(listOf("com.package.my_ignored_package"))
}
}
include(
// these projects will have BCV-MU automatically applied
":common",
":internal-dependencies:alpha:nested",
":internal-dependencies:alpha:nested",
":a-task",
// this subproject is explicitly excluded from BCV
":some-subproject",
// these subprojects will be excluded by glob pattern
":internal-dependencies",
":internal-dependencies:alpha",
":internal-dependencies:beta",
":x-tasks",
":x-tasks:sub1",
":x-tasks:sub1:sub2",
":z-tasks",
":z-tasks:sub1",
":z-tasks:sub1:sub2",
)
The code in this project is based on the code in the Kotlin/binary-compatibility-validator project.