Dependency Management BuildSrc KotlinDSL
Better Dependency Management Using buildSrc + Kotlin DSL
- Dependency management for better reusability and easy maintenance
Introduction
- Since the start of using Android Studio, Gradle has become a central place for managing many things like dependencies, config data, flavours, etc.
And maintaining the dependencies across multi-module projects has become a challenge over time.
Let’s see in detail how the evolution of dependencies management across multi-module projects happened and the problems faced.
What is buildSrc?
- buildSrc is a directory at the project root level which contains build info.
We can use this directory to enable kotlin-dsl and write logic related to custom configuration and share them across the project.
It was one of the most used approaches in recent days because of its testability,
The directory buildSrc is treated as an included build. Upon discovery of the directory,
Gradle automatically compiles and tests this code and puts it in the classpath of your build script.
For multi-project builds there can be only one buildSrc directory, which has to sit in the root project directory.
buildSrc should be preferred over script plugins as it is easier to maintain, refactor and test the code — Gradle Docs -> https://docs.gradle.org/current/userguide/organizing_gradle_projects.html
- Let’s see how to create a buildSrc directory
Right Click on Root Project → New → Directory → buildSrc
Enable Kotlin DSL in buildSrc
- As Kotlin DSL is adopted from the parent Kotlin language most of its syntax would be similar.
For this inbuildSrc
first, create an empty file build.gradle.kts and then enable the option of ‘kotlin-dsl’ inbuildSrc
import org.gradle.kotlin.dsl.`kotlin-dsl`
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
Click on sync now to finish the set-up.
Once it’s done now we can create our Kotlin DSL scripts for custom build logic. Here let’s do configuration build data and dependency management set-up.
Step 1 :
- Create a
src
directory structure inbuildSrc
directory
Step 2:
- In this directory, we can create our Kotlin files.
Let’s create an object class calledVersions.kt
where we define all the versions related to plugins, dependencies, etc
object Versions {
const val coreKts = "1.7.0"
const val appCompat = "1.4.1"
const val material = "1.5.0"
const val constraintLayout = "2.1.3"
const val jUnit = "4.13.2"
const val jUnitExt = "1.1.3"
const val espresso = "3.4.0"
}
Step 3:
- Let’s create a new file
Dependencies.kt
to define all our plugins, dependencies, etc
The file will be looking as below
/**
* To define dependencies
*/
object Deps {
val coreKts = "androidx.core:core-ktx:${Versions.coreKts}"
val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}"
val material = "com.google.android.material:material:${Versions.material}"
val constraintLayout = "androidx.constraintlayout:constraintlayout:${Versions.constraintLayout}"
val jUnit = "junit:junit:${Versions.jUnit}"
val jUnitExt = "androidx.test.ext:junit:${Versions.jUnitExt}"
val espresso = "androidx.test.espresso:espresso-core:${Versions.espresso}"
}
- While creating a file we can have auto-suggestions poping from `Versions.kt as shown below. So we can easily navigate between file to check the specific values
Step 4:
- We can also create a
ConfigData.kt
for any build configuration related info
object ConfigData {
const val compileSdkVersion = 32
//const val buildToolsVersion = "30.0.3"
const val minSdkVersion = 21
const val targetSdkVersion = 32
const val versionCode = 1
const val versionName = "1.0"
}
Step 5:
- Finally using the data defined in the above classes in the module-level build.gradle.kts file
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk(ConfigData.compileSdkVersion)
defaultConfig {
applicationId "com.example.dependencymanagement_buildsrc_kotlindsl"
minSdk(ConfigData.minSdkVersion)
targetSdk(ConfigData.targetSdkVersion)
versionCode = ConfigData.versionCode
versionName = ConfigData.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation(Deps.coreKts)
implementation(Deps.appCompat)
implementation(Deps.material)
implementation(Deps.constraintLayout)
testImplementation(Deps.jUnit)
androidTestImplementation(Deps.jUnitExt)
androidTestImplementation(Deps.espresso)
}
- The final directory structure would be something like below
buildSrc
├── src
│ └── main
│ ─└── kotlin
│ ──└── Dependencies.kt
├── build.gradle.kts
Note: Though we can do this declaration of versions, dependencies, etc in a single class it would be better to have segregation of classes so that the code will be clean
Summary:
buildSrc + Kotlin DSL
is the best option for dependency management. As it’s a class-level declaration it can be easily tested. The auto-suggestion support and code navigation would help in saving time. Maintain separate classes for each purpose. This approach could be easy for better reusability and easy maintenance.