Gradle Version Catalog – New Way to Manage Dependencies

Table of Contents

Managing Dependency Versions in Gradle

When developing software, you rarely write all the code from scratch. Usually, libraries or frameworks are leveraged, allowing developers to use numerous features without building everything from the ground up. These codes can face various issues, such as deprecated features, regressions, or version conflicts between dependencies. Hence, managing stable versions is crucial.

The most fundamental method of version management in gradle is explicitly specifying the version:

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-html:0.9.1")
}

Problem 1: Managing Dependency Versions in Multi-Module Projects

In each dependency, the version is specified explicitly. This approach is straightforward for single-module projects. However, in multi-module projects, where multiple build.gradle files are created, this necessitates repeating the version specification.

// moduleA/build.gradle.kts
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-html:0.9.1")
}

// moduleB/build.gradle.kts
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-html:0.9.1")
}

If you wish to upgrade the version, both files need modification. To streamline this, versions can be managed in a separate file as follows:

// gradle.properties
kotlinXVersion=0.9.1

// moduleA/build.gradle.kts
val kotlinXVersion: String by project
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-html:${kotlinXVersion}")
}

// moduleB/build.gradle.kts
val kotlinXVersion: String by project
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-html:${kotlinXVersion}")
}

With this setup, updating the gradle.properties file changes the version across all modules. However, there is still some boilerplate code, such as declaring the kotlinXVersion variable.

Problem 2: Managing Plugin Versions

Plugin version management differs slightly. In single-module projects, plugins are typically used as follows:

// build.gradle.kts
plugins {
	kotlin("jvm") version 1.8.22
}

To manage plugin versions in gradle.properties, along with other dependencies, a different approach is required. Unfortunately, explicit version writing is the only option in the plugins block. Therefore, separate management in settings.gradle is needed, which can be inconvenient.

// gradle.properties
kotlinVersion=1.8.22

// settings.gradle.kts
pluginManagement {
    val kotlinVersion: String by settings
    
    plugins {
        kotlin("jvm") version kotlinVersion
    }
}

// build.gradle.kts
plugins {
	kotlin("jvm")
}

Additionally, in traditional Gradle, dependency versions could be managed through ext, buildSrc, composite build, but these methods also involve navigating multiple files.

Gradle Version Catalog

This is where version catalog comes into play. Gradle introduced the Version Catalog feature in Gradle 7.4, enabling the management of multiple dependencies in one place. It also generates type-safe accessors, eliminating the need for repeated string writing.

Creating a Version Catalog

// gradle.properties
kotlinVersion=1.8.22
springBootVersion=3.2.0

// settings.gradle.kts
dependencyResolutionManagement {
    val kotlinVersion: String by settings
    val springBootVersion: String by settings

    versionCatalogs {
        create("kt") {
            plugin("jvm", "org.jetbrains.kotlin.jvm").version(kotlinVersion)
        }
        
        create("spring") {
            plugin("kotlin", "org.jetbrains.kotlin.plugin.spring").version(kotlinVersion)
            plugin("boot", "org.springframework.boot").version(springBootVersion)
            plugin("dependencyManagement", "io.spring.dependency-management").version(springDependencyManagementVersion)
            
            library("boot-starter-jpa", "org.springframework.boot", "spring-boot-starter-data-jpa").version(springBootVersion)
            library("boot-starter-thymeleaf", "org.springframework.boot", "spring-boot-starter-thymeleaf").version(springBootVersion)
            library("boot-starter-web", "org.springframework.boot", "spring-boot-starter-web").version(springBootVersion)
        }
}
  • create({catalogName}) : creates catalog with specified name
  • plugin({alias}, {id}).version({version}) : register plugin with alias and version
  • library({alias}, {group}, {artifact}).version({version}) : set alias and version of dependency. If explicit version is not required (due to dependency management in place etc.), use .withoutVersion()

Using the Version Catalog

Once the version catalog is set up, it’s time to use it. Plugins can be utilized with automatically generated type-safe accessors through alias(). In the version catalog, aliases with hyphens (-) are transformed into dots (.)

plugins {
    alias(kt.plugins.jvm)
    alias(spring.plugins.kotlin)
    alias(spring.plugins.boot)
    alias(spring.plugins.dependencyManagement)
}

Note: If the project builds fine, but IDE complains with can't be called by implicit receiver, it might be gradle regression issue. Updating your gradle version might fix it.

https://github.com/gradle/gradle/issues/22797

For dependencies, simply use the type-safe accessors directly.

dependencies {
    implementation(spring.boot.starter.jpa)
    implementation(spring.boot.starter.thymeleaf)
    implementation(spring.boot.starter.web)
}

Note: In Kotlin DSL, blocks like allprojects or subprojects cannot directly use accessors; they must be called through rootProject.

allprojects {
    dependencies {
        implementation(rootProject.spring.boot.starter.jpa)
        implementation(rootProject.spring.boot.starter.thymeleaf)
        implementation(rootProject.spring.boot.starter.web)
    }
}
https://github.com/gradle/gradle/issues/16634

Conclusion

Version catalog enables easier global dependency management compared to the traditional methods. I’ve covered the very basic features of version catalog here in this article. Though not covered in this article, the bundle feature is also available, which allows grouping several dependencies for unified use. For more details on bundle and other features, refer to Gradle’s official documentation: Gradle Platforms.

One thought on “Gradle Version Catalog – New Way to Manage Dependencies

Leave a Reply

Your email address will not be published. Required fields are marked *