ChatGPT解决这个技术问题 Extra ChatGPT

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

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?


m
mkobit

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.


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?
I don't understand the question. Please provide an example.
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."
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.
That's exactly what I did above. You don't need to apply to allprojects because project-level extra properties are visible to subprojects.
A
Adrian Baker

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.


Dependency constraints were added in Gralde 4.6, so this will work with Gradle 4.6 or higher.
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.
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.
r
roomsg

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.


it's even a must-have when you want to share the same dependencies across several (multi)projects.
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
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
g
gavenkoa

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.


U
Umeshsolanki

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
    }
}

D
Dhaval Jivani

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}"
}

t
tkruse

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
}

M
Mahozad

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.


S
Suraj Vaishnav

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()
}

how to include annotation processor by this ?? like in case of lombok