Share
Embed
There is 1 Broken API Among Us
As a library user, you probably know that versions don't always tell the truth. Have you ever experienced a broken build after doing a minor bump of a library?
As a library author, maintaining a clean API is a challenge. There is always the risk that a breaking change sneaks in by accident.
Wouldn't it be nice to get notified if you're accidentally breaking your users' code? A tool to inspect the binary API of your Kotlin/Java code can help you exactly with that. In this talk, we will see some of those tools and how that simplifies the life of library & SDK developers.
Nicola Corti
November 29, 2020
Transcript
104 slides
-
NICOLA CORTI Kotlin GDE Android Infra Eng. @ Spotify twitter.com/cortinico
github.com/cortinico ncorti.com -
WHO IS THIS TALK FOR? • Library Devs • SDK
Devs • Open Source Devs • Mobile Devs in a Modularized world -
GOOD API CITIZENS @Deprecate @Deprecated( message = "This method is
affected by a bug" ) fun replace(with: String) -
GOOD API CITIZENS @Deprecate @Deprecated( message = "This method is
affected by a bug", replaceWith = ReplaceWith("newReplace(with)") ) fun replace(with: String) -
GOOD API CITIZENS @Deprecate @Deprecated( message = "This method is
affected by a bug", replaceWith = ReplaceWith("newReplace(with)"), level = DeprecationLevel.ERROR ) fun replace(with: String) -
GOOD API CITIZENS @OptIn @RequiresOptIn(message = "This API is experimental")
annotation class MyExperimentalApi @MyExperimentalApi fun getAnswer() = 42 -
GOOD API CITIZENS @OptIn @RequiresOptIn(message = "This API is experimental")
annotation class MyExperimentalApi @MyExperimentalApi fun getAnswer() = 42 fun main() { println(getAnswer()) } -
GOOD API CITIZENS @OptIn @RequiresOptIn(message = "This API is experimental")
annotation class MyExperimentalApi @MyExperimentalApi fun getAnswer() = 42 @MyExperimentalApi fun main() { println(getAnswer()) } -
GOOD API CITIZENS @OptIn @RequiresOptIn(message = "This API is experimental")
annotation class MyExperimentalApi @MyExperimentalApi fun getAnswer() = 42 @OptIn(MyExperimentalApi::class) fun main() { println(getAnswer()) } -
GOOD API CITIZENS Explicit API Mode kotlin { explicitApi() }
fun getAnswer() = 42 -
GOOD API CITIZENS Explicit API Mode kotlin { explicitApi() }
fun getAnswer() = 42.0 -
GOOD API CITIZENS Explicit API Mode kotlin { explicitApi() }
public fun getAnswer() : Int = 42 -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup apiValidation { } -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup apiValidation { ignoredProjects += ["sample"] } -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup apiValidation { ignoredProjects += ["sample"] ignoredPackages += ["com.chuckerteam.chucker.internal"] } -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup apiValidation { ignoredProjects += ["sample"] ignoredPackages += ["com.chuckerteam.chucker.internal"] nonPublicMarkers += ["com.chuckerteam.chucker.InternalApiAnnotation"] } -
BINARY COMPATIBILITY VALIDATOR buildscript { dependencies { classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4") } }
apply plugin: "binary-compatibility-validator" Setup apiValidation { ignoredProjects += ["sample"] ignoredPackages += ["com.chuckerteam.chucker.internal"] nonPublicMarkers += ["com.chuckerteam.chucker.InternalApiAnnotation"] } $ ./gradlew apiDump -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR .api File public final class com/chuckerteam/chucker/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String; public static final field DEBUG Z public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; public fun <init> ()V } public final class com/chuckerteam/chucker/api/Chucker { public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker; public static final fun dismissNotifications (Landroid/content/Context;)V public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent; public final fun isOp ()Z } public final class com/chuckerteam/chucker/api/ChuckerCollector { public fun <init> (Landroid/content/Context;)V public fun <init> (Landroid/content/Context;Z)V public fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V public synthetic fun <init> (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/ RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getShowNotification ()Z public final fun setShowNotification (Z)V } public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor { -
BINARY COMPATIBILITY VALIDATOR Normal Workflow $ ./gradlew apiCheck # OR
$ ./gradlew check -
BINARY COMPATIBILITY VALIDATOR Normal Workflow $ ./gradlew apiCheck # OR
$ ./gradlew check # Computed API has not changed BUILD SUCCESSFUL -
BINARY COMPATIBILITY VALIDATOR Normal Workflow $ ./gradlew apiCheck # OR
$ ./gradlew check # Computed API has not changed BUILD SUCCESSFUL # Computed API has changed BUILD FAILED -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] [--error-on-semantic-incompatibility] -
JAPICMP CLI $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h SYNOPSIS java -jar
japicmp.jar [-a <accessModifier>] [(-b | --only-incompatible)] [(-e <excludes> | --exclude <excludes>)] [--exclude-exclusively] [(-h | --help)] [--html-file <pathToHtmlOutputFile>] [--html-stylesheet <pathToHtmlStylesheet>] [(-i <includes> | --include <includes>)] [--ignore-missing-classes] [--ignore-missing-classes-by-regex <ignoreMissingClassesByRegEx>...] [--include-exclusively] [--include-synthetic] [(-m | --only-modified)] [(-n <pathToNewVersionJar> | --new <pathToNewVersionJar>)] [--new-classpath <newClassPath>] [--no-annotations] [(-o <pathToOldVersionJar> | --old <pathToOldVersionJar>)] [--old-classpath <oldClassPath>] [--report-only-filename] [(-s | --semantic-versioning)] [(-x <pathToXml> | --xml-file <pathToXml>)] [--error-on-binary-incompatibility] [--error-on-source-incompatibility] [--error-on-modifications] [--no-error-on-exclusion-incompatibility] -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ --include “com.chuckerteam.chucker.api” \ --include-exclusively -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ --include “com.chuckerteam.chucker.api” \ --include-exclusively java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new library-3.4.0/classes.jar \ --html-file index.html \ -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ --include “com.chuckerteam.chucker.api” \ --include-exclusively java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new library-3.4.0/classes.jar \ --html-file index.html \ --ignore-missing-classes \ -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ --include “com.chuckerteam.chucker.api” \ --include-exclusively java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new library-3.4.0/classes.jar \ --html-file index.html \ --ignore-missing-classes \ --no-annotations \ -
JAPICMP CLI java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new
library-3.4.0/classes.jar \ --html-file index.html \ --include “com.chuckerteam.chucker.api” \ --include-exclusively java -jar japicmp-0.14.4-jar-with-dependencies.jar \ --old library-3.3.0/classes.jar \ --new library-3.4.0/classes.jar \ --html-file index.html \ --ignore-missing-classes \ --no-annotations \ --exclude “com.chuckerteam.chucker.databinding; com.chuckerteam.chucker.internal" -
JAPICMP GRADLE SETUP apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline
} dependencies { baseline project(path: ":library", configuration: "default") } -
JAPICMP GRADLE SETUP apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline
} dependencies { baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") } -
JAPICMP GRADLE SETUP apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline
} dependencies { baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } -
apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline } dependencies {
baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = configurations.latest newClasspath = configurations.baseline } JAPICMP GRADLE SETUP -
apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline } dependencies {
baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = configurations.latest newClasspath = configurations.baseline htmlOutputFile = file("$buildDir/reports/japiCmp.html") } JAPICMP GRADLE SETUP -
apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline } dependencies {
baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = configurations.latest newClasspath = configurations.baseline htmlOutputFile = file("$buildDir/reports/japiCmp.html") failOnModification = true } JAPICMP GRADLE SETUP -
apply plugin: "me.champeau.gradle.japicmp" configurations { latest baseline } dependencies {
baseline project(path: ":library", configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = configurations.latest newClasspath = configurations.baseline htmlOutputFile = file("$buildDir/reports/japiCmp.html") failOnModification = true ignoreMissingClasses = true } JAPICMP GRADLE SETUP -
configurations { latest baseline } dependencies { baseline project(path: ":library",
configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = configurations.latest newClasspath = configurations.baseline htmlOutputFile = file("$buildDir/reports/japiCmp.html") failOnModification = true ignoreMissingClasses = true packageIncludes = ["com.chuckerteam.chucker.api"] } JAPICMP GRADLE SETUP -
configurations { latest baseline } dependencies { baseline project(path: ":library",
configuration: "default") latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } } tasks.register("japiCmp", JapicmpTask) { oldClasspath = getJarClasses(project, configurations.latest) newClasspath = getJarClasses(project, configurations.baseline) htmlOutputFile = file("$buildDir/reports/japiCmp.html") failOnModification = true ignoreMissingClasses = true packageIncludes = ["com.chuckerteam.chucker.api"] } JAPICMP GRADLE SETUP -
latest("com.github.chuckerteam.chucker:library:3.4.0") { transitive = false force = true } }
tasks.register("japiCmp", JapicmpTask) { oldClasspath = getJarClasses(project, configurations.latest) newClasspath = getJarClasses(project, configurations.baseline) htmlOutputFile = file("$buildDir/reports/japiCmp.html") failOnModification = true ignoreMissingClasses = true packageIncludes = ["com.chuckerteam.chucker.api"] } def getJarClasses(project, configuration) { return project.files( project.zipTree(configuration.files.first()) .matching {include "classes.jar"} .first() ) } JAPICMP GRADLE SETUP -
TO RECAP • binary-compatibility-validator • Safeguard your API during your
workflow • Easier to integrate for Kotlin/Gradle projects • japicmp • Fine grain control over your API surface • Useful when making a new release • Be a good API citizen