# Declaring Dependency Configurations

version 9.3.0-20251105003134+0000

In Gradle, dependencies are associated with specific scopes, such as compile-time or runtime. These scopes are represented by configurations, each identified by a unique name.

Gradle plugins often add pre-defined configurations to your project. For example, when applied, the Java plugin adds configurations to your project for source code compilation (`implementation`), test execution (`testImplementation`), and more (`api`, `compileOnly`, `runtimeOnly`):

build.gradle.kts

    plugins {
        `java-library`
    }
    dependencies {
        implementation("org.hibernate:hibernate-core:3.6.7.Final")
        testImplementation("junit:junit:4.+")
        api("com.google.guava:guava:23.0")
    }

build.gradle

    plugins {
        id 'java-library'
    }
    dependencies {
        implementation 'org.hibernate:hibernate-core:3.6.7.Final'
        testImplementation 'junit:junit:4.+'
        api 'com.google.guava:guava:23.0'
    }

In this example, applying the Java Library plugin sets up the most common dependency configurations automatically:

- `implementation` — These libraries are available when compiling your code and at runtime, but they’re internal. If another project depends on your library, it won’t automatically see `hibernate-core`.

- `testImplementation` — These are used only to compile and run your tests. They’re not packaged or exposed to consumers.

- `api` — These libraries are part of your public surface. Consumers of your library will compile against `guava` automatically and get it on their classpath when they depend on your library.

## Resolvable and consumable configurations

Configurations aren’t used just for declaring dependencies, they serve various roles in dependency management:

1. Declaring Dependencies Role: Configurations that define a set of dependencies.

2. Consumer Role: Configurations that are used to resolve dependencies into artifacts.

3. Producer Role: Configurations that expose artifacts for consumption by other projects.

### 1. Configurations for declaring dependencies - Declarable configurations

To declare dependencies in your project, you can use or create declarable configurations.

These configurations help organize and categorize dependencies for different parts of the project.

For example, to express a dependency on another project, you would use a declarable configurations like `implementation`:

build.gradle.kts

    dependencies {
        // add a project dependency to the implementation configuration
        implementation(project(":lib"))
    }

build.gradle

    dependencies {
        // add a project dependency to the implementation configuration
        implementation project(":lib")
    }

Configurations used for declaring dependencies define and manage the specific libraries or projects your code requires for tasks such as compilation or testing.

### 2. Configurations for consumers - Resolvable configurations

To control how dependencies are resolved and used within your project, you can use or create resolvable configurations.

These configurations define classpaths and other sets of artifacts that your project needs during different stages, like compilation or runtime.

For example, the `implementation` configuration declares the dependencies, while `compileClasspath` and `runtimeClasspath` are resolvable configurations designed for specific purposes. When resolved, they represent the classpaths needed for compilation and runtime, respectively.

The following shows three different APIs available to create a resolvable configuration called `compileClasspath`:

build.gradle.kts

    configurations {
        // declare a resolvable configuration that is going to resolve the compile classpath of the application

        // Using lazy & newer API: realized only when needed
        resolvable("compileClasspath") {
            extendsFrom(implementation)
        }
        // Using lazy & older API: realized only when needed
        register("compileClasspath-lazy") {
            isCanBeConsumed = false
            isCanBeDeclared = false
            extendsFrom(implementation)
        }

        // Using eager & older API: realized immediately - avoid
        create("compileClasspath-eager") {
            isCanBeConsumed = false
            isCanBeDeclared = false
            extendsFrom(implementation)
        }
    }

build.gradle

    configurations {
        // declare a resolvable configuration that is going to resolve the compile classpath of the application

        // Using lazy & newer API: realized only when needed
        resolvable("compileClasspath") {
            extendsFrom(implementation)
        }
        // Using lazy & older API: realized only when needed
        register("compileClasspath-lazy") {
            canBeConsumed = false
            canBeDeclared = false
            extendsFrom(implementation)
        }

        // Using eager & older API: realized immediately - avoid
        create("compileClasspath-eager") {
            canBeConsumed = false
            canBeDeclared = false
            extendsFrom(implementation)
        }
    }

Resolvable configurations are those that can be resolved to produce a set of files or artifacts. These configurations are used to define the classpath for different stages of a build process, such as compilation or runtime.

### 3. Configurations for producers - Consumable configurations

Consumable configurations are used to expose artifacts to other projects.

These configurations define what parts of your project can be consumed by others, like APIs or runtime dependencies, but are not meant to be resolved directly within your project.

For example, the `exposedApi` configuration is a consumable configuration that exposes the API of a component to consumers.

The following shows three different APIs available to create a consumable configuration called `exposedApi`:

build.gradle.kts

    configurations {
        // a consumable configuration meant for consumers that need the API of this component

        // Using lazy & newer API: realized only when needed
        consumable("exposedApi") {
            extendsFrom(implementation)
        }
        // Using lazy & older API: realized only when needed
        register("exposedApi-lazy") {
            isCanBeResolved = false
            isCanBeDeclared = false
            extendsFrom(implementation)
        }

        // Using eager & older API: realized immediately - avoid
        create("exposedApi-eager") {
            isCanBeResolved = false
            isCanBeDeclared = false
            extendsFrom(implementation)
        }
    }

build.gradle

    configurations {
        // a consumable configuration meant for consumers that need the API of this component

        // Using lazy & newer API: realized only when needed
        consumable("exposedApi") {
            extendsFrom(implementation)
        }
        // Using lazy & older API: realized only when needed
        register("exposedApi-lazy") {
            canBeResolved = false
            canBeDeclared = false
            extendsFrom(implementation)
        }

        // Using eager & older API: realized immediately - avoid
        create("exposedApi-eager") {
            canBeResolved = false
            canBeDeclared = false
            extendsFrom(implementation)
        }
    }

A library typically provides consumable configurations like `apiElements` (for compilation) and `runtimeElements` (for runtime dependencies). These configurations expose the necessary artifacts for other projects to consume, without being resolvable within the current project.

The `canBeDeclared`, `isCanBeConsumed` and `isCanBeResolved` flags help distinguish the roles of these configurations and are required when using older APIs.

## Configuration flags and roles

Configurations have three key flags:

- `canBeResolved`: Indicates that this configuration is intended for resolving a set of dependencies into a dependency graph. A resolvable configuration should not be declarable or consumable.

- `canBeConsumed`: Indicates that this configuration is intended for exposing artifacts outside this project. A consumable configuration should not be declarable or resolvable.

- `canBeDeclared`: Indicates that this configuration is intended for declaring dependencies. A declarable configuration should not be resolvable or consumable.

| Configurations should only have one of these flags enabled.

In short, a configuration’s role is determined by the `canBeResolved`, `canBeConsumed`, or `canBeDeclared` flag:

Configuration role | Can be resolved | Can be consumed | Can be declared
--- | --- | --- | ---
Dependency Scope | false | false | true
Resolve for certain usage | true | false | false
Exposed to consumers | false | true | false
Legacy, don’t use | true | true | true

For backwards compatibility, the flags have a default value of `true`, but as a plugin author, you should always determine the right values for those flags, or you might accidentally introduce resolution errors.

This example demonstrates how to manually declare the core Java configurations (normally provided by the Java plugin) in Gradle:

build.gradle.kts

    // declare a "configuration" named "implementation"
    val implementation by configurations.creating {
        isCanBeConsumed = false
        isCanBeResolved = false
    }

    dependencies {
        // add a project dependency to the implementation configuration
        implementation(project(":lib"))
    }

    configurations {
        // declare a resolvable configuration that is going to resolve the compile classpath of the application
        resolvable("compileClasspath") {
            extendsFrom(implementation)
        }
        // declare a resolvable configuration that is going to resolve the runtime classpath of the application
        resolvable("runtimeClasspath") {
            extendsFrom(implementation)
        }
    }

    configurations {
        // a consumable configuration meant for consumers that need the API of this component
        consumable("exposedApi") {
            extendsFrom(implementation)
        }
        // a consumable configuration meant for consumers that need the implementation of this component
        consumable("exposedRuntime") {
            extendsFrom(implementation)
        }
    }

build.gradle

    // declare a "configuration" named "implementation"
    configurations {
        // declare a "configuration" named "implementation"
        implementation {
            canBeConsumed = false
            canBeResolved = false
        }
    }

    dependencies {
        // add a project dependency to the implementation configuration
        implementation project(":lib")
    }

    configurations {
        // declare a resolvable configuration that is going to resolve the compile classpath of the application
        resolvable("compileClasspath") {
            extendsFrom(implementation)
        }
        // declare a resolvable configuration that is going to resolve the runtime classpath of the application
        resolvable("runtimeClasspath") {
            extendsFrom(implementation)
        }
    }

    configurations {
        // a consumable configuration meant for consumers that need the API of this component
        consumable("exposedApi") {
            extendsFrom(implementation)
        }
        // a consumable configuration meant for consumers that need the implementation of this component
        consumable("exposedRuntime") {
            extendsFrom(implementation)
        }
    }

The following configurations are created:

- `implementation`: Used for declaring project dependencies but neither consumed nor resolved.

- `compileClasspath` + `runtimeClasspath`: Resolvable configurations that collect compile-time and runtime dependencies from `implementation`.

- `exposedApi` + `exposedRuntime`: Consumable configurations that expose artifacts (API and runtime) to other projects, but aren’t meant for internal resolution.

This setup mimics the behavior of the `implementation`, `compileClasspath`, `runtimeClasspath`, `apiElements`, and `runtimeElements` configurations in the Java plugin.

## Avoiding eager configuration

With lazy configuration, Gradle only initializes configurations when they’re actually needed, for example, when they’re published or consumed. To benefit from this, make sure your build logic doesn’t force configurations to realize early.

For example:

build.gradle.kts

    // Instead of
    configurations.all {}                           // Eagerly realizes all configurations

    // Use
    configurations.configureEach {}                 // Only called for realized configurations

    // Instead of
    configurations.getByName("name").doSomething()  // Eagerly realizes the "name" configuration

    // Use
    configurations.named("name").configure {
        doSomething()                               // Only called if "name" configuration is realized
    }

`all {}` iterates every configuration eagerly at configuration time. While `configureEach {}` registers the action but delays running it until the configuration is actually realized.

`getByName()` returns and realizes the configuration immediately. While `named()` returns a lazy handle so you can configure it without triggering realization.

By using these lazy APIs, Gradle can skip unnecessary work for configurations that aren’t needed, reducing configuration time and memory use.

## Deprecated configurations

In the past, some configurations did not define which role they were intended to be used for.

A deprecation warning is emitted when a configuration is used in a way that was not intended. To fix the deprecation, you will need to stop using the configuration in the deprecated role. The exact changes required depend on how the configuration is used and if there are alternative configurations that should be used instead.

## Creating custom configurations

You can define custom configurations to declare separate scopes of dependencies for specific purposes.

Suppose you want to generate Javadocs with AsciiDoc formatting embedded within your Java source code comments. By setting up the `asciidoclet` configuration, you enable Gradle to use Asciidoclet, allowing your Javadoc task to produce HTML documentation with enhanced formatting options:

build.gradle.kts

    val asciidoclet by configurations.creating

    dependencies {
        asciidoclet("org.asciidoctor:asciidoclet:1.+")
    }

    tasks.register("configureJavadoc") {
        doLast {
            tasks.javadoc {
                options.doclet = "org.asciidoctor.Asciidoclet"
                options.docletpath = asciidoclet.files.toList()
            }
        }
    }

build.gradle

    configurations {
        asciidoclet
    }

    dependencies {
        asciidoclet 'org.asciidoctor:asciidoclet:1.+'
    }

You can manage custom configurations using the `configurations` block. Configurations must have names and can extend each other. For more details, refer to the `ConfigurationContainer` API.

| Configurations are intended to be used for a single role: declaring dependencies, performing resolution, or defining consumable variants.

There are three main use cases for creating custom configurations:

1. API/Implementation Separation: Create custom configurations to separate API dependencies (exposed to consumers) from implementation dependencies (used internally during compilation or runtime).

   - You might create an `api` configuration for libraries that consumers will depend on, and an `implementation` configuration for libraries that are only needed internally. The `api` configuration is typically consumed by downstream projects, while `implementation` dependencies are hidden from consumers but used internally.

   - This separation ensures that your project maintains clean boundaries between its public API and strictly internal mechanisms.

2. Resolvable Configuration Creation: Create a custom resolvable configuration to resolve specific sets of dependencies, like classpaths, at various build stages.

   - You might create a `compileClasspath` configuration that resolves only the dependencies needed to compile your project. Similarly, you could create a `runtimeClasspath` configuration to resolve the dependencies needed to run the project at runtime.

   - This allows fine-grained control over which dependencies are available during different build phases, such as compilation or testing.

3. Consumable Configuration from Dependency Configuration: Create a custom consumable configuration to expose artifacts or dependencies for other projects to consume, typically when your project produces artifacts like JARs.

   - You might create an `exposedApi` configuration to expose the API dependencies of your project for consumption by other projects. Similarly, a `runtimeElements` configuration could be created to expose the runtime dependencies or artifacts that other projects need.

   - Consumable configurations ensure that only the necessary artifacts or dependencies are exposed to consumers.

### Configuration API incubating methods

Several incubating factory methods—`resolvable()`, `consumable()`, and `dependencyScope()`—within the `ConfigurationContainer` API can be used to simplify the creation of configurations with specific roles.

These methods help build authors document the purpose of a configuration and avoid manually setting various configuration flags, streamlining the process and ensuring consistency:

- `resolvable()`: Creates a configuration intended for resolving dependencies. This means the configuration can be used to resolve dependencies but not consumed by other projects.

- `consumable()`: Creates a configuration meant to be consumed by other projects but not used to resolve dependencies itself.

- `dependencyScope()`: Creates a configuration that establishes a dependency scope, setting up the necessary properties to act both as a consumer and provider, depending on the use case.

## Configuration inheritance

Configurations can inherit from other configurations, creating an inheritance hierarchy.

Configurations form an inheritance hierarchy using the `Configuration.extendsFrom(Configuration…​)` method. A configuration can extend any other configuration other than a detached configuration, regardless of how it is defined in the build script or plugin.

| Avoid extending consumable or resolvable configurations with configurations that are not consumable or resolvable, respectively.

For example, in a project that already uses JUnit for testing, you can define a dedicated configuration named `smokeTest` to run smoke tests. Each smoke test performs an HTTP request to verify a web service endpoint. To reuse the existing test framework dependencies, the `smokeTest` configuration should extend from `testImplementation`. This allows smoke tests to leverage the same dependencies as unit tests without duplication. The configuration can be declared in `build.gradle(.kts)` as follows:

build.gradle.kts

    val smokeTest by configurations.creating {
        extendsFrom(configurations.testImplementation.get())
    }

    dependencies {
        testImplementation("junit:junit:4.13")
        smokeTest("org.apache.httpcomponents:httpclient:4.5.5")
    }

build.gradle

    configurations {
        smokeTest.extendsFrom testImplementation
    }

    dependencies {
        testImplementation 'junit:junit:4.13'
        smokeTest 'org.apache.httpcomponents:httpclient:4.5.5'
    }

This setup enables the `smokeTest` source set to inherit JUnit and any other testing dependencies, making it easier to define and execute smoke tests while keeping them separate from unit tests.

| Configurations can only extend configurations within the same project.

When extending a configuration, the new configuration inherits:

- dependencies

- dependency constraints

- exclude rules

- artifacts

- capabilities

The extension does not include attributes. It also does not extend consumable/resolvable/declarable status.

## Dependency resolution

The entrypoint to all dependency resolution APIs is a resolvable Configuration. The Java plugins primarily use the `compileClasspath`, and `runtimeClasspath` configurations to resolve jars for compilation and runtime respectively.

A resolvable configuration is intended for initiating dependency resolution. The dependencies to be resolved are declared on dependency scope configurations. The Java plugins use the `api`, `implementation`, and `runtimeOnly` dependency scope configurations, among others, as a source of dependencies to be resolved by the resolvable configurations.

Consider the following example that demonstrates how to declare a set of configurations intended for resolution:

| This example uses incubating APIs.

build.gradle.kts

    val implementation = configurations.dependencyScope("implementation")
    val runtimeClasspath = configurations.resolvable("runtimeClasspath") {
        extendsFrom(implementation.get())
    }

build.gradle

    configurations {
        dependencyScope("implementation")
        resolvable("runtimeClasspath") {
            extendsFrom(implementation)
        }
    }

Dependencies can be declared on the `implementation` configuration using the dependencies block. See the Declaring Dependencies chapter for more information on the types of dependencies that can be declared, and the various options for customizing dependency declarations.

build.gradle.kts

    dependencies {
        implementation("com.google.guava:guava:33.2.1-jre")
    }

build.gradle

    dependencies {
        implementation("com.google.guava:guava:33.2.1-jre")
    }

Now that we’ve created a dependency scope configuration for declaring dependencies, and a resolvable configuration for resolving those dependencies, we can use Gradle’s dependency resolution APIs to access the results of resolution.

## Unsafe configuration resolution errors

Resolving a configuration can have side effects on Gradle’s project model. As a result, Gradle must manage access to each project’s configurations.

There are a number of ways a configuration might be resolved unsafely. For example:

- A task from one project directly resolves a configuration in another project in the task’s action.

- A task specifies a configuration from another project as an input file collection.

- A build script for one project resolves a configuration in another project during evaluation.

- Project configurations are resolved in the settings file.

Gradle produces a deprecation warning for each unsafe access.

Unsafe access can cause indeterminate errors. You should fix unsafe access warnings in your build.

In most cases, you can resolve unsafe accesses by creating a proper cross-project dependency.