126

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?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Stanislav Bashkyrtsev
  • 14,470
  • 7
  • 42
  • 45

10 Answers10

189

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.

mkobit
  • 43,979
  • 12
  • 156
  • 150
Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • 4
    Thanks, this solves my question, but still have a concern though.. In Maven we can leave the version empty and if this is a lib, it's convenient because you can use it in our app and make dependencyManagement to define what version of the lib it should take. How would you do the same with Gradle? – Stanislav Bashkyrtsev Mar 05 '12 at 13:04
  • I don't understand the question. Please provide an example. – Peter Niederwieser Mar 05 '12 at 13:56
  • 4
    Peter, what ctapobep is saying is that in maven you can declare dependencies with version (and scope) in a parent (or aggregator) pom in the dependencyManagement section. Then in the "concrete" pom, you needn't re-declare the version; just artifact and groupId. Basically it tells maven "I need X:Y, but use whatever version the parent has configured." – Michael Campbell Apr 19 '13 at 23:14
  • 2
    To avoid this kind of duplication, I tend to create a separate `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 project `build.gradle`, I include `allprojects { 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. – Steinar Oct 10 '13 at 12:36
  • 1
    That's exactly what I did above. You don't need to apply to `allprojects` because project-level extra properties are visible to subprojects. – Peter Niederwieser Oct 10 '13 at 12:51
  • @MichaelCampbell My solution also declares versions just once. If you want to manage transitive versions like Maven's `dependencyManagement` does, you can use version forcing, e.g. `allprojects { configurations.all { resolutionStrategy.force "foo:bar:2.0" } }`. – Peter Niederwieser Oct 31 '14 at 14:19
  • Can I use advanced configuration AND multiple dependencies? – Doppelganger Mar 18 '15 at 19:23
  • Found it, is something like: ext.libraries = [ shared: [ dependencies.create('org.simpleframework:simple-xml:2.7.+', { exclude module: 'stax' exclude module: 'stax-api' exclude module: 'stax2-api' exclude module: 'xpp3' }), 'com.google.code.ksoap2-android:ksoap2-android:3.3.0', ] ] – Doppelganger Mar 18 '15 at 19:39
  • Im trying to use this but getting the "property not found" error. Has anyone else experienced this? Please see this: http://stackoverflow.com/questions/34338826/gradle-no-such-property-for-class-error-thrown-in-sub-module?noredirect=1#comment56456648_34338826 – java123999 Dec 21 '15 at 09:10
  • Android studio used to show if a library has updates, with this way it no longer shows that. How do you keep track if the library has updates? – Udit Mukherjee Jul 12 '16 at 06:41
  • @PeterNiederwieser In parent build.gradle I did ext.libraries = [ lombok: "org.projectlombok:lombok:1.18.8" ] In Child build.gradle I did dependencies { implementation libraries.lombok } Its working in intellij while coding But When I build project using command [gradle build] Build get failed because it cannot find getter setter created by lombok. – Pritish Joshi Jan 22 '20 at 10:27
  • how to include annotation processor by this ?? like in case of lombok – Pritish Joshi Jan 22 '20 at 10:37
12

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.

Adrian Baker
  • 9,297
  • 1
  • 26
  • 22
  • 1
    Dependency constraints were added in Gralde 4.6, so this will work with Gradle 4.6 or higher. – Jim Hurne Jan 26 '19 at 14:58
  • I think Gradle provides that the [Java Platform Plugin](https://docs.gradle.org/current/userguide/java_platform_plugin.html) is used in such case. However, the Gradle documentation is not very clear at this point. I guess the usage of `allprojects` is fine as well. – JojOatXGME May 12 '19 at 13:36
  • i want to declare the constraints in the root project but only in one of my subprojects, i want to load all of those dependencies that have constraints defined. – dtc Feb 13 '20 at 23:06
8

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.

roomsg
  • 1,811
  • 16
  • 22
  • it's even a must-have when you want to share the same dependencies across several (multi)projects. – roomsg Nov 21 '14 at 20:53
  • 8
    Although convenient, this plugin may have significant performance footprint. For 30 subprojects with 200+ dependencies it adds up to 1 minute to dependency resolution phase. For small projects it works like a charm, though – Jk1 Jun 06 '15 at 23:33
  • it also overrides transitive dependency versions, say you have declared version 3.0.0 in the dependency management, but for one of the subprojects you need to use an older version e.g 2.5.0, then if you have a project dependent on this older project the transitive dependency will be overwritten from 2.5.0 to what's declared in the dependency management plugin so 3.0.0 in this case a very weird behavior – KameeCoding Nov 27 '18 at 08:40
5

As datta said in their answer, Gradle now has something called version catalogs.
Here is an example for Kotlin DSL (*.kts files). 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 a 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.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
3

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.

gavenkoa
  • 45,285
  • 19
  • 251
  • 303
3

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
    }
}
Umeshsolanki
  • 126
  • 1
  • 5
2

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

datta
  • 521
  • 2
  • 5
  • 16
1

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}"
}
Dhaval Jivani
  • 9,467
  • 2
  • 48
  • 42
1

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
}
tkruse
  • 10,222
  • 7
  • 53
  • 80
0

To keep you gradle file clean, we can group dependency in an array and implement them later.

  1. 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'
  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}"
]
  1. 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()
}
Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46