Declaring Dependencies

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.

Figure: Resolving dependencies from remote repositories

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: Declaring a dependency with a concrete version

build.gradle

apply plugin: '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 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 constrains allow you to manage versions of all dependencies, including transitive ones, in one place.

Example: Declaring a dependency without version

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: Declaring a dependency with a dynamic version

build.gradle

apply plugin: '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.

Figure: Dynamic dependencies in build scan

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: Declaring a dependencies with a changing version

build.gradle

apply plugin: '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.

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.

Figure: Resolving file dependencies from the local file system and a shared drive

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: Declaring multiple file dependencies

build.gradle

configurations {
    antContrib
    externalLibs
    deploymentTools
}

dependencies {
    antContrib files('ant/antcontrib.jar')
    externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
    deploymentTools fileTree(dir: '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[]) 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.

Figure: Dependencies between projects

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: Declaring project dependencies

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: Resolving a JavaScript artifact for a declared dependency

build.gradle

repositories {
    ivy {
        url 'https://ajax.googleapis.com/ajax/libs'
        layout 'pattern', {
            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: Resolving a JavaScript artifact with classifier for a declared dependency

build.gradle

repositories {
    ivy {
        url 'https://ajax.googleapis.com/ajax/libs'
        layout 'pattern', {
            artifact '[organization]/[revision]/[module](.[classifier]).[ext]'
        }
    }
}

configurations {
    js
}

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