Gradle builds can declare dependencies on modules hosted in repositories, files and other Gradle projects. You can find examples for common scenarios in this section. For more information, see the full reference on all types of dependencies.

Every dependency needs to be assigned to a configuration when declared in a build script. For more information on the purpose and syntax of configurations, see Managing Dependency Configurations.

Declaring a dependency to a module

Modern software projects rarely build code in isolation. Projects reference modules for the purpose of reusing existing and proven functionality. Upon resolution, selected versions of modules are downloaded from dedicated repositories and stored in the dependency cache to avoid unnecessary network traffic.

dependency management dependencies to modules
Figure 1. Resolving dependencies from remote repositories

Declaring a concrete version of a dependency

A typical example for such a library in a Java project is the Spring framework. The following code snippet declares a compile-time dependency on the Spring web module by its coordinates: org.springframework:spring-web:5.0.2.RELEASE. Gradle resolves the module including its transitive dependencies from the Maven Central repository and uses it to compile Java source code. The version attribute of the dependency coordinates points to a concrete version indicating that the underlying artifacts do not change over time. The use of concrete versions ensure reproducibility for the aspect of dependency resolution.

Example 1. Declaring a dependency with a concrete version
GroovyKotlin
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.0.2.RELEASE'
}

A Gradle project can define other types of repositories hosting modules. You can learn more about the syntax and API in the section on declaring repositories. Refer to the chapter on the Java Plugin for a deep dive on declaring dependencies for a Java project. The resolution behavior for dependencies is highly customizable.

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.

Example 2. Declaring a dependency without version
GroovyKotlin
build.gradle
dependencies {
    implementation 'org.springframework:spring-web'
}

dependencies {
    constraints {
        implementation 'org.springframework:spring-web:5.0.2.RELEASE'
    }
}

Declaring a dynamic version

Projects might adopt a more aggressive approach for consuming dependencies to modules. For example you might want to always integrate the latest version of a dependency to consume cutting edge features at any given time. A dynamic version allows for resolving the latest version or the latest version of a version range for a given module.

Using dynamic versions in a build bears the risk of potentially breaking it. As soon as a new version of the dependency is released that contains an incompatible API change your source code might stop compiling.

Example 3. Declaring a dependency with a dynamic version
GroovyKotlin
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.+'
}

A build scan can effectively visualize dynamic dependency versions and their respective, selected versions.

dependency management dynamic dependency build scan
Figure 2. Dynamic dependencies in build scan

By default, Gradle caches dynamic versions of dependencies for 24 hours. Within this time frame, Gradle does not try to resolve newer versions from the declared repositories. The threshold can be configured as needed for example if you want to resolve new versions earlier.

Declaring a changing version

A team might decide to implement a series of features before releasing a new version of the application or library. A common strategy to allow consumers to integrate an unfinished version of their artifacts early and often is to release a module with a so-called changing version. A changing version indicates that the feature set is still under active development and hasn’t released a stable version for general availability yet.

In Maven repositories, changing versions are commonly referred to as snapshot versions. Snapshot versions contain the suffix -SNAPSHOT. The following example demonstrates how to declare a snapshot version on the Spring dependency.

Example 4. Declaring a dependency with a changing version
GroovyKotlin
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
    maven {
        url 'https://repo.spring.io/snapshot/'
    }
}

dependencies {
    implementation 'org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT'
}

By default, Gradle caches changing versions of dependencies for 24 hours. Within this time frame, Gradle does not try to resolve newer versions from the declared repositories. The threshold can be configured as needed for example if you want to resolve new snapshot versions earlier.

Gradle is flexible enough to treat any version as changing version e.g. if you wanted to model snapshot behavior for an Ivy module. All you need to do is to set the property ExternalModuleDependency.setChanging(boolean) to true.

Rich version declaration

Gradle supports a rich model for declaring versions, which allows to combine different level of version information. The terms and their meaning are explained below, from the strongest to the weakest:

strictly

Any version not matched by this version notation will be excluded. This is the strongest version declaration. It will cause dependency resolution to fail if no version acceptable by this clause can be selected. This term supports dynamic versions.

When defined, overrides previous require declaration and clears previous reject.

require

Implies that the selected version cannot be lower than what require accepts but could be higher through conflict resolution, even if higher has an exclusive higher bound. This is what a direct version on a dependency translates to. This term supports dynamic versions.

When defined, overrides previous strictly declaration and clears previous reject.

prefer

This is a very soft version declaration. It applies only if there is no stronger non dynamic opinion on a version for the module. This term does not support dynamic versions.

Definition can complement strictly or require.

There is also an additional term outside of the level hierarchy:

reject

Declares that specific version(s) are not accepted for the module. This will cause dependency resolution to fail if the only versions selectable are also rejected. This term supports dynamic versions.

The following table illustrates a number of use cases and how to combine the different terms for rich version declaration:

Table 1. Rich version use cases
Which version(s) of this dependency are acceptable? strictly require prefer rejects Selection result

Tested with version 1.5, believe all future versions should work

1.5

Any version starting from 1.5, equivalent of org:foo:1.5. An upgrade to 2.4 is accepted.

Tested with 1.5, soft constraint upgrades according to semantic versioning

[1.0, 2.0[

1.5

Any version between 1.0 and 2.0, 1.5 if nobody else cares. An upgrade to 2.4 is accepted.
🔒

Tested with 1.5, but follows semantic versioning

[1.0, 2.0[

1.5

Any version between 1.0 and 2.0 excluded, 1.5 if nobody else cares.
🔒

Same as above, with 1.4 known broken

[1.0, 2.0[

1.5

1.4

Any version between 1.0 and 2.0 excluded except for 1.4, 1.5 if nobody else cares.
🔒

No opinion, works with 1.5

1.5

1.5 if no other opinion, any otherwise

No opinion, prefer latest release

latest.release

The latest release at build time
🔒

On the edge, latest release, no downgrade

latest.release

The latest release at build time
🔒

No other version than 1.5

1.5

1.5, or failure if another strict or higher require constraint disagrees

1.5 or a patch version of it exclusively

[1.5,1.6[

Latest 1.5.x patch release, or failure if another strict or higher require constraint disagrees
🔒

Lines annotated with a lock (🔒) indicate that leveraging dependency locking makes sense in this context. Another concept that relates with rich version declaration is the ability to publish resolved versions instead of declared ones.

Using strictly, especially for a library, must be a well thought process as it can have a serious impact on downstream consumers. At the same time, used correctly, it will help consumers understand what combination of libraries do not work together in their context.

Rich version information will be preserved when using the Gradle metadata format. However conversion to Ivy or Maven metadata formats will be lossy. The highest level will be published, that is strictly or require over prefer. In addition, any reject will be ignored.

Rich version declaration is accessed through the version DSL method on a dependency or constraint declaration which gives access to MutableVersionConstraint.

Example 5. Rich version declaration
GroovyKotlin
build.gradle
dependencies {
    implementation('org.slf4j:slf4j-api') {
        version {
            strictly '[1.7, 1.8['
            prefer '1.7.25'
        }
    }

    constraints {
        implementation('org.springframework:spring-core') {
            version {
                require '4.2.9.RELEASE'
                reject '4.3.16.RELEASE'
            }
        }
    }
}

Declaring a file dependency

Projects sometimes do not rely on a binary repository product e.g. JFrog Artifactory or Sonatype Nexus for hosting and resolving external dependencies. It’s common practice to host those dependencies on a shared drive or check them into version control alongside the project source code. Those dependencies are referred to as file dependencies, the reason being that they represent a file without any metadata (like information about transitive dependencies, the origin or its author) attached to them.

dependency management file dependencies
Figure 3. Resolving file dependencies from the local file system and a shared drive

The following example resolves file dependencies from the directories ant, libs and tools.

Example 6. Declaring multiple file dependencies
GroovyKotlin
build.gradle
configurations {
    antContrib
    externalLibs
    deploymentTools
}

dependencies {
    antContrib files('ant/antcontrib.jar')
    externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
    deploymentTools(fileTree('tools') { include '*.exe' })
}

As you can see in the code example, every dependency has to define its exact location in the file system. The most prominent methods for creating a file reference are Project.files(java.lang.Object…​), ProjectLayout.files(java.lang.Object…​) and Project.fileTree(java.lang.Object) Alternatively, you can also define the source directory of one or many file dependencies in the form of a flat directory repository.

Declaring a project dependency

Software projects often break up software components into modules to improve maintainability and prevent strong coupling. Modules can define dependencies between each other to reuse code within the same project.

Gradle can model dependencies between modules. Those dependencies are called project dependencies because each module is represented by a Gradle project. At runtime, the build automatically ensures that project dependencies are built in the correct order and added to the classpath for compilation. The chapter Authoring Multi-Project Builds discusses how to set up and configure multi-project builds in more detail.

dependency management project dependencies
Figure 4. Dependencies between projects

The following example declares the dependencies on the utils and api project from the web-service project. The method Project.project(java.lang.String) creates a reference to a specific subproject by path.

Example 7. Declaring project dependencies
GroovyKotlin
build.gradle
project(':web-service') {
    dependencies {
        implementation project(':utils')
        implementation project(':api')
    }
}

Resolving specific artifacts from a module dependency

Whenever Gradle tries to resolve a module from a Maven or Ivy repository, it looks for a metadata file and the default artifact file, a JAR. The build fails if none of these artifact files can be resolved. Under certain conditions, you might want to tweak the way Gradle resolves artifacts for a dependency.

  • The dependency only provides a non-standard artifact without any metadata e.g. a ZIP file.

  • The module metadata declares more than one artifact e.g. as part of an Ivy dependency descriptor.

  • You only want to download a specific artifact without any of the transitive dependencies declared in the metadata.

Gradle is a polyglot build tool and not limited to just resolving Java libraries. Let’s assume you wanted to build a web application using JavaScript as the client technology. Most projects check in external JavaScript libraries into version control. An external JavaScript library is no different than a reusable Java library so why not download it from a repository instead?

Google Hosted Libraries is a distribution platform for popular, open-source JavaScript libraries. With the help of the artifact-only notation you can download a JavaScript library file e.g. JQuery. The @ character separates the dependency’s coordinates from the artifact’s file extension.

Example 8. Resolving a JavaScript artifact for a declared dependency
GroovyKotlin
build.gradle
repositories {
    ivy {
        url 'https://ajax.googleapis.com/ajax/libs'
        patternLayout {
            artifact '[organization]/[revision]/[module].[ext]'
        }
    }
}

configurations {
    js
}

dependencies {
    js 'jquery:jquery:3.2.1@js'
}

Some modules ship different "flavors" of the same artifact or they publish multiple artifacts that belong to a specific module version but have a different purpose. It’s common for a Java library to publish the artifact with the compiled class files, another one with just the source code in it and a third one containing the Javadocs.

In JavaScript, a library may exist as uncompressed or minified artifact. In Gradle, a specific artifact identifier is called classifier, a term generally used in Maven and Ivy dependency management.

Let’s say we wanted to download the minified artifact of the JQuery library instead of the uncompressed file. You can provide the classifier min as part of the dependency declaration.

Example 9. Resolving a JavaScript artifact with classifier for a declared dependency
GroovyKotlin
build.gradle
repositories {
    ivy {
        url 'https://ajax.googleapis.com/ajax/libs'
        patternLayout {
            artifact '[organization]/[revision]/[module](.[classifier]).[ext]'
        }
    }
}

configurations {
    js
}

dependencies {
    js 'jquery:jquery:3.2.1:min@js'
}