In Gradle, how do I declare common dependencies in a single place?
Asked Answered
R

10

127

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?

Romance answered 3/3, 2012 at 15:13 Comment(0)
V
190

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.

Vibrato answered 3/3, 2012 at 16:15 Comment(12)
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?Romance
I don't understand the question. Please provide an example.Vibrato
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."Mort
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.Inexperienced
That's exactly what I did above. You don't need to apply to allprojects because project-level extra properties are visible to subprojects.Vibrato
@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" } }.Vibrato
Can I use advanced configuration AND multiple dependencies?Articulate
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', ] ]Articulate
Im trying to use this but getting the "property not found" error. Has anyone else experienced this? Please see this: #34339326Agni
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?Indohittite
@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.Palliative
how to include annotation processor by this ?? like in case of lombokPalliative
H
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.

Hinge answered 2/1, 2019 at 23:10 Comment(3)
Dependency constraints were added in Gralde 4.6, so this will work with Gradle 4.6 or higher.Retributive
I think Gradle provides that the Java Platform Plugin 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.Web
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.Speculate
R
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.

Raskind answered 31/10, 2014 at 14:11 Comment(3)
it's even a must-have when you want to share the same dependencies across several (multi)projects.Raskind
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, thoughPodesta
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 behaviorRandolf
M
7

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.

Marvelmarvella answered 12/2, 2022 at 15:42 Comment(0)
D
4

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.

Discourage answered 18/2, 2017 at 7:37 Comment(0)
T
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
    }
}
Trexler answered 8/8, 2021 at 8:16 Comment(0)
L
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

Lucialucian answered 16/8, 2021 at 3:46 Comment(0)
T
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}"
}
Troudeloup answered 2/10, 2017 at 7:32 Comment(0)
S
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
}
Sialkot answered 1/12, 2017 at 0:58 Comment(0)
M
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()
}
Maryannamaryanne answered 24/6, 2019 at 5:35 Comment(1)
how to include annotation processor by this ?? like in case of lombokPalliative

© 2022 - 2024 — McMap. All rights reserved.