# Declaring Dependencies Basics

version 8.14.4

## 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.

Module dependencies use the `ExternalModuleDependency` and `DependencyHandler` APIs. These APIs provide various properties and configuration methods for defining dependencies.

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.

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.

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:

- `Project.files()`: Accepts one or more file paths directly.

- `ProjectLayout.files()`: Accepts one or more file paths directly.

- `Project.fileTree()`: Defines a directory and includes or excludes specific file patterns.

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