Introduction of new Dependency Management (Version Control) and Kotlin DSL migration

Mirsaidov Nurulloh
3 min readJul 24, 2024

--

Overview

This document outlines migrating and optimizing dependency management in our Android project. It includes details on converting Gradle files from Groovy DSL to Kotlin DSL, introducing versions.libs.toml, converting dependencies, and optimizing build performance by transitioning from api() to implementation().

Migration Steps

1. Converted Gradle Files from Groovy DSL to Kotlin DSL

The first step was to convert all build.gradle files from Groovy DSL to Kotlin DSL (build.gradle.kts). This transition improves type safety, offers better IDE support, and leverages Kotlin's language features.

Example conversion:

Groovy DSL (build.gradle):

plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
compileSdkVersion 30
defaultConfig {
applicationId "com.example.app"
minSdkVersion 21
targetSdkVersion 30
}
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.21"
}

Kotlin DSL (build.gradle.kts):

plugins {
id("com.android.application")
id("kotlin-android")
}

android {
compileSdk = 30
defaultConfig {
applicationId = "com.example.app"
minSdk = 21
targetSdk = 30
}
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.21")
}

For more details, refer to the official Android documentation.

2. Introduced versions.libs.toml

To manage dependencies more efficiently, a versions.libs.toml file was introduced. This file separates versions, libraries, and plugins, making it easier to maintain and update dependencies.

  • Versions: Define version numbers for all dependencies.
  • Libraries: List of library dependencies.
  • Plugins: List of plugin dependencies.

Example versions.libs.toml:

[versions]
kotlin = "1.5.21"
coroutines = "1.5.0"

[libraries]
kotlinStdLib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
coroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }

[plugins]
androidApplication = { id = "com.android.application", version = "7.0.2" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

3. Converted Dependencies

All dependencies in the project were converted to use the versions.libs.toml file. This involved updating the build.gradle.kts files to reference the new format.

Example conversion:

dependencies {
implementation(libs.kotlinStdLib)
implementation(libs.coroutinesCore)
}

4. Removed classpath() Calls

The classpath() calls were removed and converted into plugin implementations. This simplifies the build scripts and improves clarity.

Example conversion:

Old code :

dependencies {
classpath "com.android.tools.build:gradle:8.5.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
}

New code :

plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid)
}

5. Optimized api() to implementation()

For better build performance, all api() calls in the shared module were converted to implementation() calls. The difference between api and implementation is crucial for build performance and encapsulation:

  • api: Exposes the dependency to the consumers of the module.
  • implementation: Keeps the dependency internal to the module.

For more information, refer to the official documentation: Difference between api and implementation

6. Applying the Plugin in Specific Modules

With apply false, the plugin is declared but not applied. You need to apply it explicitly in the modules where it's required.

build.gradle.kts (app module):

plugins {
alias(libs.plugins.androidApplication) apply true
alias(libs.plugins.kotlinAndroid) apply true
}

Why Use apply false?

  1. Centralized Version Management: It allows for centralized management of plugin versions in the version catalog, making it easier to update versions across multiple modules.
  2. Selective Application: It enables selective application of plugins only to the necessary modules, avoiding unnecessary application of plugins to all sub-projects.
  3. Cleaner Build Scripts: By using aliases and apply false, you keep the build scripts clean and maintainable, with clear separation between declaration and application of plugins.

This approach provides a clean and maintainable way to manage plugins in complex projects, ensuring that plugins are only applied where necessary while keeping version control centralized. For more details, refer to the official Gradle documentation on version catalogs.

Adding New Plugins/Dependencies

To add new plugins or dependencies:

  • Add Version: Define the version in versions.libs.toml.
[versions]
newLibrary = "1.0.0"
  • Add Library: Define the library in versions.libs.toml.
[libraries]
newLibrary = { module = "com.example:new-library", version.ref = "newLibrary" }
  • Add Plugin: Define the plugin in versions.libs.toml.
[plugins]
newPlugin = { id = "com.example.plugin", version = "1.0.0" }
  • Use in Build Script: Reference the new library or plugin in the build.gradle.kts file.
dependencies {
implementation(libs.newLibrary)
}

plugins {
alias(libs.plugins.newPlugin)
}

Useful Documentation Links

--

--