Dependency Analysis Gradle Plugin: What's an ABI?
Tags: gradle, java, kotlin, android
Published: 2020-08-27 18:07:36

---
title: Dependency Analysis Gradle Plugin: What's an ABI?
published: true
description: Using the Dependency Analysis Gradle Plugin to determine your library's binary API
tags: gradle, java, kotlin, android
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/l0slkafp4u3qf02dklzq.jpg
---

<small><em>(With thanks to [Jack Douglass](https://unsplash.com/photos/OIfFRthAASc) on Unsplash for the cover image)</em></small>

Welcome to my series on dependency analysis with the [Dependency Analysis Gradle Plugin](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin). This post is intended as a direct follow-up to the [prior post](https://dev.to/autonomousapps/dependency-analysis-gradle-plugin-using-bytecode-analysis-to-find-unused-dependencies-509n) in this series, which discussed detecting unused dependencies.

# What is an ABI?

A library’s ABI, or [application binary interface](https://en.wikipedia.org/wiki/Application_binary_interface), is what consumers _compile against_ (not what you code against!). I like to think of it as a library’s _binary API_. Knowledge of the ABI is what enables Gradle features such as incremental compilation and compilation avoidance. You can think of a library’s ABI as the collection of its public interfaces and the dependencies required to compile against them: public methods and their return and parameter types, and public fields / properties. To understand how this relates to dependency analysis, consider the following:

```kotlin
fun newOkHttpClient(gson: Gson): OkHttpClient { … }
```
This is a public function that takes a `Gson` instance as a parameter and returns an instance of an `OkHttpClient`. This means that _both_ Gson and OkHttp are part of your ABI! In Gradle terms, that means you need the following in your build script:

```groovy
dependencies {
  api "com.google.code.gson:gson:2.8.6"
  api "com.squareup.okhttp3:okhttp:4.8.0"
}
```
If instead of `api` you used `implementation`, your project would still compile, but you’d be creating trouble for downstream consumers. Without those libraries on their compile classpath, they will not be able to compile. It is *critically important* that library authors get this right. Please see the Gradle [documentation](https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation) for an in-depth discussion of this separation.

The ABI of a library is determined with the help of the [AbiAnalysisTask](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/blob/main/src/main/kotlin/com/autonomousapps/tasks/AbiAnalysisTask.kt). It delegates to [abiDependencies](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/blob/main/src/main/kotlin/com/autonomousapps/internal/kotlin/abiDependencies.kt) and [PublicApiDump](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/blob/main/src/main/kotlin/com/autonomousapps/internal/kotlin/PublicApiDump.kt), the latter of which was pulled directly from a JetBrains [repository](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/binary-compatibility-validator). It produces two outputs: one is JSON and is meant for use by other tasks involved in dependency analysis, and the second is a more human-readable plaintext output. Excerpts of each follow (where `<variant>` would be `debug` (etc.) for an Android project and `main` for a JVM project):

**json: build/reports/dependency-analysis/\<variant\>/intermediates/abi.json**
```json
[
  {
    "identifier": ":db",
    "configurationName": "implementation"
  },
  {
    "identifier": "androidx.appcompat:appcompat",
    "resolvedVersion": "1.1.0-rc01",
    "configurationName": "implementation"
  }
]
```
(The above indicates that the project `:db` and the external dependency `androidx.appcompat:appcompat` are both part of _this_ project's ABI, and are currently declared &mdash; incorrectly &mdash; on "implementation".)

**plaintext: build/reports/dependency-analysis/\<variant\>/intermediates/abi-dump.txt**
```
public abstract class com/seattleshelter/core/base/BaseActivity : androidx/appcompat/app/AppCompatActivity {
  public fun <init> ()V
  protected fun onCreate (Landroid/os/Bundle;)V
}
```
(The above indicates that the public `BaseActivity` class of the project-under-analysis extends `AppCompatActivity` and has a function, `onCreate`, that takes a `Bundle` as a parameter.)

The plugin uses this information to advise users on how best to declare their project’s dependencies.

# Case study

I was recently updating some libraries my team publishes for internal consumption. I ran `buildHealth` over them and followed the advice. In particular, I did this:

![a git diff showing a change from implementation to api for RxJava and Dagger2](https://dev-to-uploads.s3.amazonaws.com/i/5w98g1oe5mhsaco945t3.png)

(Note: no version numbers because I use the [Java Platform plugin](https://docs.gradle.org/current/userguide/java_platform_plugin.html).)

I filed a PR. A reviewer asked, entirely reasonably, why I was changing Dagger and RxJava from `implementation` to `api`. Where were they being exposed? Here's what I told him (with some paths and class names changed for Reasons):

If you run `./gradlew buildHealth` on this project, and then open up `hello/build/reports/dependency-analysis/debug/intermediates/abi-dump.txt`, you'll find **24** usages of `io/reactivex` (from RxJava2). Here's one example:

```
public final class com/hello/MyService {
	public final fun disableService (Landroid/content/Context;)Lio/reactivex/Completable;
```

Here we have a public class with a public function that returns a `Completable`. Therefore RxJava2 is part of this project's ABI.

I see **74** results for dagger. Here's one of them:
```
public final class com/hello/dao/DbOp {
	public fun <init> (Landroid/content/Context;Ldagger/Lazy;)V
```
And here is a public class whose public constructor takes a `Dagger.Lazy` as a parameter. So, Dagger is also part of the ABI.

I've had to do this enough &mdash; that is, get surprised at my own plugin's results, tediously verify them manually, finally follow the advice &mdash; that now I just skip the first two steps and follow the advice. This isn't to say the plugin is always right &mdash; there are annoying [corner](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/227) [cases](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/252) that I haven't resolved yet &mdash; but it's right more than 99% of the time.<sup>[verification needed]</sup> That said, I'm glad it's possible to verify the results manually, albeit tediously.

I'm hoping the tedious part disappears soon, with an exciting new feature I'm working on called "[provenance](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/251)" that will automate this explanation process. Here's sample output from using this draft feature on another project

```
$ ./gradlew db:reasonDebug --id io.reactivex.rxjava2:rxjava
```

```
You asked about the dependency io.reactivex.rxjava2:rxjava. You have been advised to add this dependency.

Shortest path to io.reactivex.rxjava2:rxjava from the current project:
:db
\--- androidx.room:room-rxjava2
     \--- io.reactivex.rxjava2:rxjava

Dependency io.reactivex.rxjava2:rxjava provides the following:
- 1651 classes
- 141 public constants

And this project exposes the following classes provided by this dependency:
- io.reactivex.Flowable

Please see abi-dump.txt for more information.
```
:tada:

# Conclusion

In this post, we've learned what an ABI is, how it relates to dependency analysis, and how to verify whether the [Dependency Analysis Gradle Plugin](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin) is emitting accurate advice on the subject. Please join me next time, when I hope to talk about the basics of using [ANTLR](https://www.antlr.org/) for source-code parsing.