Types of dependencies

There are three main types of dependencies in Gradle:

  1. Module Dependencies: Refer to libraries from external repositories.

  2. Project Dependencies: Refer to other projects in the same multi-project build.

  3. File Dependencies: Refer to local files or directories, such as .jar or .aar files.

1. Module dependencies

Module dependencies are the most common dependencies. They refer to a dependency that is identified by module coordinates (group, name, and version):

build.gradle.kts
dependencies {
    runtimeOnly(group = "org.springframework", name = "spring-core", version = "2.5")
    runtimeOnly("org.springframework:spring-aop:2.5")
    runtimeOnly("org.hibernate:hibernate:3.0.5") {
        isTransitive = true
    }
    runtimeOnly(group = "org.hibernate", name = "hibernate", version = "3.0.5") {
        isTransitive = true
    }
}
build.gradle
dependencies {
    runtimeOnly group: 'org.springframework', name: 'spring-core', version: '2.5'
    runtimeOnly 'org.springframework:spring-core:2.5',
            'org.springframework:spring-aop:2.5'
    runtimeOnly(
        [group: 'org.springframework', name: 'spring-core', version: '2.5'],
        [group: 'org.springframework', name: 'spring-aop', version: '2.5']
    )
    runtimeOnly('org.hibernate:hibernate:3.0.5') {
        transitive = true
    }
    runtimeOnly group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true
    runtimeOnly(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') {
        transitive = true
    }
}

Gradle offers multiple notations for declaring module dependencies, including string notation and map notation.

  • String Notation: Simplifies dependency declaration by combining the group, name, and version into a single string.

  • Map Notation: Allows for specifying each part of the coordinates separately.

For advanced configurations, such as enforcing strict versions, you can also provide a closure when alongside these notations.

2. Project dependencies

Project dependencies allow you to reference other projects within a multi-project Gradle build.

dependency management project dependencies

This is useful for organizing large projects into smaller, modular components:

web-service/build.gradle.kts
dependencies {
    implementation(project(":utils"))
    implementation(project(":api"))
}
web-service/build.gradle
dependencies {
    implementation project(':utils')
    implementation project(':api')
}

Gradle uses the project() function to define a project dependency. This function takes the relative path to the target project within the build. The path is typically defined using a colon (:) to separate different levels of the project structure.

Project dependencies are automatically resolved such that the dependent project is always built before the project that depends on it.

Type-safe project dependencies

Type-safe project accessors are an incubating feature which must be enabled explicitly. Implementation may change at any time.

To add support for type-safe project accessors, add enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") this to your settings.gradle(.kts) file:

settings.gradle.kts
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
settings.gradle
enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS'

One downside of using the project(":some:path") notation is the need to remember project paths for dependencies. Moreover, changing a project path requires manually updating every occurrence, increasing the risk of missing one.

Instead, the experimental type-safe project accessors API provides IDE completion, making it easier to declare dependencies:

web-service/build.gradle.kts
dependencies {
    implementation(projects.utils)
    implementation(projects.api)
}
web-service/build.gradle
dependencies {
    implementation projects.utils
    implementation projects.api
}

With this API, incorrectly specified projects in Kotlin DSL scripts trigger compilation errors, helping you avoid missed updates.

Project accessors are based on project paths. For instance, the path :commons:utils:some:lib becomes projects.commons.utils.some.lib, while kebab-case (some-lib) and snake-case (some_lib) are converted to camel case: projects.someLib.

3. File dependencies

File dependencies allow you to include external JARs or other files directly into your project by referencing their file paths. File dependencies also allow you to add a set of files directly to a configuration without using a repository.

File dependencies are generally discouraged. Instead, prefer declaring dependencies on an external repository, or if necessary, declaring a maven or ivy repository using a file:// URL.
dependency management file dependencies

File dependencies are unique because they represent a direct reference to files on the filesystem without any associated metadata, such as transitive dependencies, origin, or author information.

build.gradle.kts
configurations {
    create("antContrib")
    create("externalLibs")
    create("deploymentTools")
}

dependencies {
    "antContrib"(files("ant/antcontrib.jar"))
    "externalLibs"(files("libs/commons-lang.jar", "libs/log4j.jar"))
    "deploymentTools"(fileTree("tools") { include("*.exe") })
}
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' })
}

In this example, each dependency explicitly specifies its location within the file system. Common methods for referencing these files include:

The order of files in a FileTree is not guaranteed to be stable, even on the same computer. As a result, dependency configurations using FileTree may produce resolution results with varying order, which can impact the cacheability of tasks that use these results as inputs. To ensure more predictable and stable behavior, it is recommended to use the simpler files method where possible.

Alternatively, you can use a flat directory repository to specify the source directory for multiple file dependencies.

Ideally, you should use Maven or Ivy repository with a local URL:

repositories {
    maven {
        url 'file:///path/to/local/files' // Replace with your actual path
    }
}

To add files as dependencies, pass a file collection to the configuration:

build.gradle.kts
dependencies {
    runtimeOnly(files("libs/a.jar", "libs/b.jar"))
    runtimeOnly(fileTree("libs") { include("*.jar") })
}
build.gradle
dependencies {
    runtimeOnly files('libs/a.jar', 'libs/b.jar')
    runtimeOnly fileTree('libs') { include '*.jar' }
}

Note that file dependencies are not included in the published dependency descriptor for your project. However, they are available in transitive dependencies within the same build, meaning they can be used within the current build but not outside it.

You should specify which tasks produce the files for a file dependency. Otherwise, the necessary tasks might not run when you depend on them transitively from another project:

build.gradle.kts
dependencies {
    implementation(files(layout.buildDirectory.dir("classes")) {
        builtBy("compile")
    })
}

tasks.register("compile") {
    doLast {
        println("compiling classes")
    }
}

tasks.register("list") {
    val compileClasspath: FileCollection = configurations["compileClasspath"]
    dependsOn(compileClasspath)
    doLast {
        println("classpath = ${compileClasspath.map { file: File -> file.name }}")
    }
}
build.gradle
dependencies {
    implementation files(layout.buildDirectory.dir('classes')) {
        builtBy 'compile'
    }
}

tasks.register('compile') {
    doLast {
        println 'compiling classes'
    }
}

tasks.register('list') {
    FileCollection compileClasspath = configurations.compileClasspath
    dependsOn compileClasspath
    doLast {
        println "classpath = ${compileClasspath.collect { File file -> file.name }}"
    }
}
$ gradle -q list
compiling classes
classpath = [classes]

Gradle distribution-specific dependencies

Gradle API dependency

You can declare a dependency on the API of the current version of Gradle by using the DependencyHandler.gradleApi() method. This is useful when you are developing custom Gradle tasks or plugins:

build.gradle.kts
dependencies {
    implementation(gradleApi())
}
build.gradle
dependencies {
    implementation gradleApi()
}

Gradle TestKit dependency

You can declare a dependency on the TestKit API of the current version of Gradle by using the DependencyHandler.gradleTestKit() method. This is useful for writing and executing functional tests for Gradle plugins and build scripts:

build.gradle.kts
dependencies {
    testImplementation(gradleTestKit())
}
build.gradle
dependencies {
    testImplementation gradleTestKit()
}

Local Groovy dependency

You can declare a dependency on the Groovy that is distributed with Gradle by using the DependencyHandler.localGroovy() method. This is useful when you are developing custom Gradle tasks or plugins in Groovy:

build.gradle.kts
dependencies {
    implementation(localGroovy())
}
build.gradle
dependencies {
    implementation localGroovy()
}

Documenting dependencies

When declaring a dependency or a dependency constraint, you can provide a reason to clarify why the dependency is included. This helps make your build script and the dependency insight report easier to interpret:

build.gradle.kts
plugins {
    `java-library`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.ow2.asm:asm:7.1") {
        because("we require a JDK 9 compatible bytecode generator")
    }
}
build.gradle
plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation('org.ow2.asm:asm:7.1') {
        because 'we require a JDK 9 compatible bytecode generator'
    }
}

In this example, the because() method provides a reason for including the asm library, which helps explain its purpose in the context of the build:

$ gradle -q dependencyInsight --dependency asm
org.ow2.asm:asm:7.1
  Variant compile:
    | Attribute Name                 | Provided | Requested    |
    |--------------------------------|----------|--------------|
    | org.gradle.status              | release  |              |
    | org.gradle.category            | library  | library      |
    | org.gradle.libraryelements     | jar      | classes      |
    | org.gradle.usage               | java-api | java-api     |
    | org.gradle.dependency.bundling |          | external     |
    | org.gradle.jvm.environment     |          | standard-jvm |
    | org.gradle.jvm.version         |          | 11           |
   Selection reasons:
      - Was requested: we require a JDK 9 compatible bytecode generator

org.ow2.asm:asm:7.1
\--- compileClasspath

A web-based, searchable dependency report is available by adding the --scan option.