A Day with an Elephant in the Room: Configuring Gradle Plugins.

Harun Wangereka
6 min readAug 13, 2020

--

You would think that one would suffocate after spending full day with an elephant in the room. But the good news is, at the end of the day you still get to breath. How? You may ask me, but by the end of this article you’ll see how you will be able to breath fine with an elephant in the room!

Many are the times that Android Developers dread Gradle and the errors that you encounter while using minimal Gradle setup, I myself included. But this changed recently as I was working on the droidconKE2020 app where after setting the Gradle Kotlin DSL there was a task to setup plugins like Detekt, Ktlint and other plugins.

The droidconKE2020 app is a mutli-module project and as such the requirement was to setup this plugins to apply to all modules and sub-projects. From an overview it seemed as quite a straight forward issue of just adding the plugins on your gradle.kts files but I realized that that’s not the case and made a couple of new realizations which am going to share all through this article.

Configuring Gradle Plugins

You maybe used to the old way of setting up plugins by first adding the classpath url at the project build.gradle.kts file and the declaring the plugin at the app/module level gradle file and any additional setup that may be required. This was the first problem I encountered as setting up this plugins cause it was bringing some weir-dish errors using the classpath definitions which are normally under the buildscript block.

This made me dig into the issue and some of the solutions I was seeing is people using the plugins approach. Going to Gradle Release notes, I found that there is a new way of declaring plugins using the plugins {} block eliminating the old way.

If you want to quickly jump to the full project implementing this here’s the GitHub Repo :

Too much writing now, lets see how to navigate through this. Will using the multi -module approach but the settings are the same for single module projects.

Using the Plugins Block

This is how a normal build.gradle.kts file looks like:

In the above snippet realize it uses the buildscript block. However, you can declare plugins using the plugins{} block.

Using the plugins {} block you change the file to be similar to the following snippet:

We no longer have the buildscript block, instead we have the plugins block. One thing to note is that this are plugins that are set to apply to all sub-project/ modules and the ones that are not supposed to be applied to all project like the Kotlin Android and Kotlin Android Extensions we set apply to false.

The reason we have the androidLibrary, androidLibrary, kotlinAndroid and kotlinAndroidExtensions defined on root project gradle file and also in other module gradle files as well is to avoid this error:

This is due to the design and architecture of the Gradle’s plugins system imposes this by loading plugins hierarchy of class loaders which in turn affects the visibility of plugins. More about this error can be found in this issue where the discussion helps explain why that happens.

Using Plugin Management

pluginManagement has a way of managing all versions by using pluginManagement.plugins in a settings script. Build Script can now apply plugins using plugins {} block without specifying the versions. This explains why in the build.gradle.kts file that is above does not have versions for the androidApplication, androidLibrary etc defined as it uses the settings script to manage add versions centrally.

From the docs :

One benefit of managing plugin versions in this way is that the pluginManagement.plugins {} block does not have the same constrained syntax as a build script plugins {} block. Plugin versions may be loaded from gradle.properties, or defined programmatically.

One disadvantage though is that accessing the buildSrc directory and its dependencies in settings scripts has been deprecated in Gradle v 6.0 since it runs before the buildSrc, meaning you cannot access the versions, libraries and plugins that you’ve defined in the Dependencies.kt file which is a huge blow but there is work arounds for that. You can use composite builds or declare them on the gradle.properties file.

For plugins from other sources other the Gradle Plugin Portal to be declared via the plugins {} block they must be published with their plugin marker artifacts. And currently all versions of Android Plugin for Gradle up to 3.2.0 present in the google() repository lack plugin marker artifacts. Since they’re missing you can use the plugins {} block. To solve this you must fallback to using buildscript in your settings script.

With this you cannot apply this plugins at the root gradle file. And since you have already shifted away from the buildscript you are going to use the settings script to instruct how to map the plugins to resolvable artifacts.

To do this you:
- Add the plugin repository.
- Map the plugin id to corresponding artifacts.

pluginManagement comes to our rescue. As you can see there is google() in the repositories block where the Android Gradle Plugin is published and we have others like jcenter() and maven() to help also fetch other plugins.

There’s the resolutionStrategy block to map “com.android.application” and “com.android.library” plugin ID to the com.android.tools.build:gradle:<version> artifact available in the google() repository. Also we have the ones from JetBrains and Firebase being mapped to their respective artifacts.

With these configurations, you’re ready with the new plugin {} configuration and good thing is that now you have plugins like ktLint, Detekt and dokka running for all the modules and you can use the following commands to execute them in this project : ./gradlew ktlintFormat, ./gradlew ktlintCheck, ./gradlew detekt and ./gradlew dokka on the terminal or using the Gradle tasks pane on Android Studio to do formatting and detect code smells and generate documentation using Dokka.

One thing to also explain, there are some plugin configurations that can be done on the allProjects {} block that will apply to all modules. However there are some that cannot be configured here as they require the build file to have either the Android Gradle Plugin Enabled or Kotlin Gradle Plugin enabled so they have to be done at the module gradle file level.

A good example of this is the Dokka plugin where you add this code to the app module gradle file:

Adding this on project level build script that do not have the Kotlin and Android plugins added will bring the following error when you try to run the ./gradlew dokka command.

The dokka plugin requires the Kotlin Gradle Plugin for it to generate the docs on the project. Be sure to check on plugin documentation while adding this plugins.

Surviving a day with an Elephant in the Room

With this exploration I have realized that, you really cannot suffocate with this big elephant in the room, actually its quite the contrary , there is pretty of air for both of you to breath!

Pun aside, Gradle has really improved especially if you are using the Kotlin Gradle DSL which has a ton of features.

And the Gradle Documentation is so detailed and with all the information that you need to know about Gradle, and that’s all you need to survive a day with an Elephant in the Room :-) Reading the Gradle Documentation and Release Notes.

With my new found love with Gradle, will continue writing this findings and even much more how to optimize configurations and all that!

--

--

Harun Wangereka

Google Developer Expert for Android | Android Engineer | Co-organizer droidconKE, Android254 & Kotlin Kenya | Android Author @raywenderlich.com