In Maven there is a very useful feature where you can define a dependency in the <dependencyManagement>
section of the parent POM, and reference that dependency from child modules without specifying the version or scope or whatever.
What are the alternatives in Gradle?
You can declare common dependencies in a parent script:
ext.libraries = [ // Groovy map literal
spring_core: "org.springframework:spring-core:3.1",
junit: "junit:junit:4.10"
]
From a child script, you can then use the dependency declarations like so:
dependencies {
compile libraries.spring_core
testCompile libraries.junit
}
To share dependency declarations with advanced configuration options, you can use DependencyHandler.create
:
libraries = [
spring_core: dependencies.create("org.springframework:spring-core:3.1") {
exclude module: "commons-logging"
force = true
}
]
Multiple dependencies can be shared under the same name:
libraries = [
spring: [ // Groovy list literal
"org.springframework:spring-core:3.1",
"org.springframework:spring-jdbc:3.1"
]
]
dependencies { compile libraries.spring }
will then add both dependencies at once.
The one piece of information that you cannot share in this fashion is what configuration (scope in Maven terms) a dependency should be assigned to. However, from my experience it is better to be explicit about this anyway.
As of Gradle 4.6, dependency constraints are suggested in the documentation as the way to achieve this. From https://docs.gradle.org/current/userguide/declaring_dependencies.html#declaring_a_dependency_without_version:
A recommended practice for larger projects is to declare dependencies without versions and use dependency constraints for version declaration. The advantage is that dependency constraints allow you to manage versions of all dependencies, including transitive ones, in one place.
In your parent build.gradle
file:
allprojects {
plugins.withType(JavaPlugin).whenPluginAdded {
dependencies {
constraints {
implementation("com.google.guava:guava:27.0.1-jre")
}
}
}
}
Wrapping the dependencies block with a check for the Java plugin (... whenPluginAdded {
) isn't strictly necessary, but it will then handle adding a non-Java project to the same build.
Then in a child gradle project you can simply omit the verison:
apply plugin: "java"
dependencies {
implementation("com.google.guava:guava")
}
Child builds can still choose to specify a higher version. If a lower version is specified it is automatically upgraded to the version in the constraint.
allprojects
is fine as well.
It's a late reply, yet you might also want to have a look at: http://plugins.gradle.org/plugin/io.spring.dependency-management It provides possibility to import a maven 'bom', and reuse the definitions defined in the 'bom'. It's certainly a nice help when gradually migrating from maven to gradle ! Enjoying it right now.
io.spring.gradle:dependency-management-plugin
plugin has problems with new Gradle 3.x series but stable for 2.x series. For reference look to bug report Drop support for Gradle 3 #115
In case of Spring (main promoter of BOM usage) you may end with:
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath 'io.spring.gradle:dependency-management-plugin:1.0.0.RELEASE'
}
}
repositories {
mavenLocal()
jcenter()
}
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom 'io.spring.platform:platform-bom:Athens-SR3'
}
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
testCompile 'org.springframework.boot:spring-boot-starter-test'
}
Note that io.spring.platform:platform-bom
have org.springframework.boot:spring-boot-starter-parent
as parent so it is compatable with Spring Boot
You can verify actual dependency resolution via:
$ gradle dependencies
$ gradle dependencies --configuration compile
$ gradle dependencies -p $SUBPROJ
$ gradle buildEnvironment
$ gradle buildEnvironment -p $SUBPROJ
or with task:
task showMeCache {
configurations.compile.each { println it }
}
Read official Soring blog post Better dependency management for Gradle to understand the reason of introducing io.spring.gradle:dependency-management-plugin
.
I'll prefer to create common_dependencies.gradle file in root project with content
buildscript {
ext {
commonDependencies = [
redis : 'redis.clients:jedis:3.6.3',
lettuce : 'io.lettuce:lettuce-core:6.1.4.RELEASE'
]
}
}
then in root/submodule's build.gradle
apply from: rootProject.file("common_dependencies.gradle")
dependencies {
commonDependencies.values().forEach {
implementation it
}
}
As of gradle 7.1.1 you can use version catalog
https://docs.gradle.org/current/userguide/platforms.html
https://melix.github.io/blog/2021/03/version-catalogs.html
You can centralize a dependency using below code :
In gradle.properties
COMPILE_SDK_VERSION=26
BUILD_TOOLS_VERSION=26.0.1
TARGET_SDK_VERSION=26
MIN_SDK_VERSION=14
ANDROID_SUPPORT_VERSION=26.0.2
In each module add to build.gradle
:
android {
compileSdkVersion COMPILE_SDK_VERSION as int
buildToolsVersion BUILD_TOOLS_VERSION as String
defaultConfig {
minSdkVersion MIN_SDK_VERSION as int
targetSdkVersion TARGET_SDK_VERSION as int
versionCode 1
versionName "1.0"
}
}
dependencies {
compile "com.android.support:appcompat-v7:${ANDROID_SUPPORT_VERSION}"
compile "com.android.support:support-v4:${ANDROID_SUPPORT_VERSION}"
compile "com.android.support:support-annotations:${ANDROID_SUPPORT_VERSION}"
compile "com.android.support:support-vector-drawable:${ANDROID_SUPPORT_VERSION}"
compile "com.android.support:design:${ANDROID_SUPPORT_VERSION}"
}
This blog post suggest managing dependencies and groups as configurations: https://www.javacodegeeks.com/2016/05/manage-dependencies-gradle-multi-project-build.html
I have not tried it myself, but it looks interesting.
Root project build.gradle
subprojects {
configurations {
commonsIo
}
dependencies {
commonsIo 'commons-io:commons-io:2.5'
}
}
Sub-project build.gradle
configurations {
compile.extendsFrom commonsIo
}
As datta said in their answer, Gradle now has something called version catalogs.
Here is an example for Kotlin DSL (*.kts). Note that I'm using Gradle 7.4.
Defining dependencies in settings.gradle.kts:
// Configure dependencies aspects applied to all projects
dependencyResolutionManagement {
// By default, repositories declared by a project will override the ones here.
// You can change this behavior with the repositoriesMode property.
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
// Define repositories for all projects
repositories {
mavenCentral()
maven("https://jitpack.io")
}
versionCatalogs {
create("libs") {
// Versions are useful specially when you have libraries with the same
// group and version which are updated together with the same version
version("room", "2.4.1")
// │ │
// │ └───> The version notation
// └───> Your desired name (alias)
library("material", "com.google.android.material:material:1.4.0")
// │ │
// │ └───> The dependency notation (coordinates)
// ├───> Your desired name (alias); only letters, digits and _ - .
// └───> Note that _ - . will all be normalized to .
// You can configure the version as you would in regular build file
// Note that the group and module are separate parameters
library("junit5", "org.junit.jupiter", "junit-jupiter").version {
prefer("5.8.0")
}
// Using the same version for multiple dependencies
library("room-ktx", "androidx.room", "room-ktx").versionRef("room")
library("room-runtime", "androidx.room", "room-runtime").versionRef("room")
}
}
}
Usage in build.gradle.kts:
dependencies {
implementation(libs.material)
implementation(libs.room.ktx)
implementation(libs.room.runtime)
testImplementation(libs.junit5)
}
As you can see, not only can you declare dependencies, but also you can declare the repositories here as well (instead of using allprojects
block in your top-level build script to define repositories for all subprojects).
For groovy syntax of above solution and for more information about version catalogs and centralizing your repository and dependency configurations, see Gradle official guides.
To keep you gradle file clean, we can group dependency in an array and implement them later.
Add version of libraries like this in build.gradle (app level) outside of dependency block:
// declare versions of library final RetrofitVersion = '2.3.0' final OkHttpVersion = '3.9.1'
Create an array of related dependency, so you can easily find it later. Add it in build.gradle (app level) outside of dependency block:
// Using version in library and add dependency along with access name(like retrofit(first one)) final networkDependencies = [ retrofit : "com.squareup.retrofit2:retrofit:${RetrofitVersion}", retrofitGsonConverter: "com.squareup.retrofit2:converter-gson:${RetrofitVersion}", retrofitRxJavaAdapter: "com.squareup.retrofit2:adapter-rxjava2:${RetrofitVersion}", okHttp3 : "com.squareup.okhttp3:okhttp:${OkHttpVersion}", okHttp3Logging : "com.squareup.okhttp3:logging-interceptor:${OkHttpVersion}" ]
And in dependency block:
// Implement all the dependency from array dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation networkDependencies.values() }
So the final code will look like this:
final RetrofitVersion = '2.3.0'
final OkHttpVersion = '3.9.1'
final networkDependencies = [
retrofit : "com.squareup.retrofit2:retrofit:${RetrofitVersion}",
retrofitGsonConverter: "com.squareup.retrofit2:converter-gson:${RetrofitVersion}",
retrofitRxJavaAdapter: "com.squareup.retrofit2:adapter-rxjava2:${RetrofitVersion}",
okHttp3 : "com.squareup.okhttp3:okhttp:${OkHttpVersion}",
okHttp3Logging : "com.squareup.okhttp3:logging-interceptor:${OkHttpVersion}"
]
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation networkDependencies.values()
}
Success story sharing
dependencies.gradle
script where I define all my dependencies as properties, e.g:ext.GROOVY = 'org.codehaus.groovy:groovy-all:2.1.6'
. In the root projectbuild.gradle
, I includeallprojects { apply from: "$rootDir/dependencies.gradle" }
. Then all dependencies are defined in one file instead of spreading them around, and more "easy to read" constants are used in the dependency configurations.allprojects
because project-level extra properties are visible to subprojects.