I’d like to take a moment to describe how to organize Gradle dependency versions in a separate file. This doesn’t mean this is the only way to do it, of course: it’s just one approach that’s worked well for me.
Step 1: Create a dependencies.gradle file
I usually create this file and add it to the gradle
directory.
$ tree --charset=ascii -I 'build|out|src' . |-- LICENSE |-- README.md |-- build.gradle |-- gradle | |-- dependencies.gradle | `-- wrapper | |-- gradle-wrapper.jar | `-- gradle-wrapper.properties |-- gradlew |-- gradlew.bat `-- settings.gradle
Step 2: Add an ext.versions
map containing dependency versions
Add the following code to the dependencies.gradle
file:
ext.versions = [ 'assertj': '3.16.1', 'guava' : '29.0-jre', 'jackson': '2.11.2', 'junit' : '4.13', 'logback': '1.2.3', 'lombok' : '1.18.12', 'slf4j' : '1.7.30', ]
Step 3: Apply the file to build.gradle
This imports the versions
map variable by applying dependencies.gradle
as a script plugin. This is accomplished by adding the following line to build.gradle
:
apply from: 'gradle/dependencies.gradle'
Step 4: Use the versions
map in your build file!
Now you can begin using the versions variable in build.gradle
. Instead of hard-coding a version number, reference it from the versions
map. So instead of this:
"org.projectlombok:lombok:1.18.12"
You can use this:
"org.projectlombok:lombok:$versions.lombok"
Here are the full contents of an example build.gradle
:
plugins { id 'java' } apply from: 'gradle/dependencies.gradle' dependencies { runtimeOnly "ch.qos.logback:logback-classic:$versions.logback" implementation "com.fasterxml.jackson.core:jackson-databind:$versions.jackson" implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$versions.jackson" implementation "com.google.guava:guava:$versions.guava" compileOnly "org.projectlombok:lombok:$versions.lombok" annotationProcessor "org.projectlombok:lombok:$versions.lombok" implementation "org.slf4j:slf4j-api:$versions.slf4j" testImplementation "junit:junit:$versions.junit" testImplementation "org.assertj:assertj-core:$versions.assertj" testCompileOnly "org.projectlombok:lombok:$versions.lombok" testAnnotationProcessor "org.projectlombok:lombok:$versions.lombok" } repositories { jcenter() }
What’s going on here?
Every Gradle build file is backed by an implicit Project
instance. It just so happens that new properties can be added to this Project
instance via an extension property named ext
. When we added ext.versions = [...]
in dependencies.gradle
, we created a Groovy map literal on the Project
instance.
A key point here is that Gradle’s build language is based on Groovy. In build.gradle
, when we use the syntax "org.projectlombok:lombok:$versions.lombok"
, Groovy is smart enough to perform string interpolation on this string literal, replacing $versions.lombok
with the version number string 1.18.12
we defined in the map.
Important: for Groovy string interpolation to work correctly, you must use double quotes when defining the dependency coordinates! This means the following single quoted dependency will result in an error:
compileOnly 'org.projectlombok:lombok:$versions.lombok'
Note: at time of writing I’m using Gradle 6.6 to test this, however the approach should work for older versions as well.