Keywords: configuration

# Creating Dependency Configurations

version 9.6.1

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.

Configurations are added automatically by applied plugins or can be created manually within your build.

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

## Declarable, Resolvable, and Consumable configurations

There are 3 types of configurations that fulfil different roles:

# | Role | Name | What it means | Java example
--- | --- | --- | --- | ---
1 | Bucket Role | Declarable configuration | Define a set of dependencies. | `implementation`
2 | Consumer Role | Resolvable configuration | Resolve dependencies into concrete artifacts/files. | `runtimeClasspath`
3 | Producer Role | Consumable configuration | Expose built artifacts for other projects to consume. | `runtimeElements`

### 1. 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. 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. 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`, `canBeConsumed` and `canBeResolved` flags help distinguish the roles of these configurations and are required when using older APIs.

## Configurations added by Plugins

Gradle plugins often create configurations (like `implementation`, `api`, `testImplementation`) for you. Let’s look at a few popular ones.

### Java Plugin

When you apply the Java or Java Library (`java`, `java-library`, `application`) plugin, Gradle adds the following configurations.

1. Declarable (you put dependencies into these):

   - `api` (`java-library` only), `implementation`, `compileOnly`, `runtimeOnly`, plus test variants (`testImplementation`, `testCompileOnly`, `testRuntimeOnly`), etc.

2. Resolvable (you resolve these to get jars/dirs):

   - `compileClasspath`, `runtimeClasspath`, and their test equivalents (`testCompileClasspath`, `testRuntimeClasspath`).

3. Consumable (Gradle publishes these so other projects can depend on you):

   - `apiElements` (`java-library` only) and `runtimeElements`.

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'
    }

There is a relationship between these configurations.

- What you declare in `api`:

  - Your project: on `compileClasspath` and (if needed) `runtimeClasspath`.

  - Consumers: exported via `apiElements` → ends up on consumers’ compile and runtime classpaths.

- What you declare in `implementation`:

  - Your project: on `compileClasspath` and `runtimeClasspath`.

  - Consumers: not exported (kept internal). It’s not on consumers’ compile classpath; the runtime part is included in your `runtimeElements` so consumers can run you without needing to compile against those internals.

- What you declare in `compileOnly`:

  - Your project: only on `compileClasspath` (not on `runtimeClasspath`).

  - Consumers: not exported (neither `apiElements` nor `runtimeElements`).

- What you declare in `runtimeOnly`:

  - Your project: only on `runtimeClasspath`.

  - Consumers: included in `runtimeElements` (so they can run you), but not in `apiElements`.

- Test configurations (`testImplementation`, etc.) affect only test resolvable classpaths (`testCompileClasspath`, `testRuntimeClasspath`) and are never published to consumers.

### Android Gradle Plugin

When you apply the Android plugin (`com.android.application` or `com.android.library`), Gradle and the Android Gradle Plugin (AGP) add a richer set of configurations to handle multiple build variants (combinations of build types and product flavors). Each variant has its own declarable, resolvable, and consumable configurations, following the same conceptual model as the Java plugin.

1. Declarable (you put dependencies into these):

   - `implementation`, `compileOnly`, `runtimeOnly`, `annotationProcessor`, and test equivalents (`androidTestImplementation`, `androidTestCompileOnly`, `androidTestRuntimeOnly`, `testImplementation`, `testCompileOnly`, `testRuntimeOnly`), plus variant-specific versions such as `debugImplementation`, `releaseImplementation`, and flavor-specific configurations (e.g., `freeImplementation`, `paidImplementation`).

2. Resolvable (Gradle resolves these to get the actual JARs, AARs, or directories):

   - Each build variant defines its own resolvable classpaths: `debugCompileClasspath`, `debugRuntimeClasspath`, `releaseCompileClasspath`, `releaseRuntimeClasspath`, and similar sets for test variants (`testDebugCompileClasspath`, `testDebugRuntimeClasspath`, etc.). These are used internally by AGP tasks such as `compileDebugJavaWithJavac` or `mergeReleaseResources`.

3. Consumable (Gradle publishes these so other modules can depend on your Android library):

   - For library modules (`com.android.library`), AGP defines variant-specific consumable configurations: `debugApiElements`, `debugRuntimeElements`, `releaseApiElements`, and `releaseRuntimeElements`. These control what other modules receive when they declare a dependency on a particular variant of your library.

build.gradle.kts

    plugins {
        id("com.android.application") version "9.0.1"
    }

    dependencies {
        implementation("androidx.core:core-ktx:1.13.1")
        annotationProcessor("androidx.room:room-compiler:2.6.1")
        implementation("androidx.room:room-ktx:2.6.1")
        debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
        testImplementation("junit:junit:4.13.2")
        androidTestImplementation("androidx.test.ext:junit:1.2.1")
        androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
    }

build.gradle

    plugins {
        id("com.android.application") version '9.0.1'
    }

    dependencies {
        implementation("androidx.core:core-ktx:1.13.1")
        annotationProcessor("androidx.room:room-compiler:2.6.1")
        implementation("androidx.room:room-ktx:2.6.1")
        debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
        testImplementation("junit:junit:4.13.2")
        androidTestImplementation("androidx.test.ext:junit:1.2.1")
        androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
    }

There is a relationship between these configurations, similar to the Java plugin, but scoped per variant.

- What you declare in `implementation`:

  - Your module: included in that variant’s `compileClasspath` and `runtimeClasspath`.

  - Consumers: not exported; remains internal to that variant.

- What you declare in `api` (for Android library modules):

  - Your module: visible on that variant’s `compileClasspath` and `runtimeClasspath`.

  - Consumers: exported via that variant’s `apiElements` configuration → appears on consumers’ compile and runtime classpaths.

- What you declare in `compileOnly` or `annotationProcessor`:

  - Your module: used only for compilation; not packaged or exposed to consumers.

- What you declare in `runtimeOnly`:

  - Your module: added only to the variant’s runtime classpath.

  - Consumers: included in the corresponding `runtimeElements` variant, so downstream modules can run your library.

- Test configurations (`testImplementation`, `androidTestImplementation`, etc.) affect only their respective resolvable classpaths (`testCompileClasspath`, `androidTestCompileClasspath`, etc.) and are never published to consumers.

### Kotlin Multiplatform Plugin

When you apply the Kotlin Multiplatform plugin (`org.jetbrains.kotlin.multiplatform`), Gradle creates configurations for each source set and target. The same underlying concepts apply — declarable, resolvable, and consumable — but they’re managed per source set instead of per variant or build type.

1. Declarable (you put dependencies into these):

   - Each source set has its own declarable configuration, for example:

   - `commonMainImplementation`, `commonMainApi`, `commonMainCompileOnly`

   - `jvmMainImplementation`, `jsMainImplementation`, `iosMainImplementation`, etc.

   - and corresponding test configurations such as `commonTestImplementation`, `jvmTestImplementation`, and so on.

   - These are where you declare dependencies that apply to a specific source set or to all targets sharing that source set.

2. Resolvable (Gradle resolves these to get JARs, KLIBs, or directories):

   - Every compilable source set has a corresponding resolvable configuration, such as:

   - `commonMainCompileClasspath`, `jvmMainCompileClasspath`, `iosArm64MainCompileClasspath`, etc.

   - These are used internally by Kotlin compilation tasks like `compileKotlinJvm`, `compileKotlinJs`, and `compileKotlinIosArm64` to obtain the appropriate libraries and dependencies for that target.

3. Consumable (Gradle publishes these so other modules or projects can depend on your multiplatform component):

   - KMP creates consumable configurations for each target, such as:

   - `jvmApiElements`, `jvmRuntimeElements`, `jsApiElements`, `iosArm64ApiElements`, etc.

   - When you publish a KMP library, these configurations define the artifacts and metadata that Gradle and consumers use to resolve the right variant for their platform.

build.gradle.kts

    plugins {
        alias(libs.plugins.kotlinMultiplatform)
        alias(libs.plugins.androidApplication)
        alias(libs.plugins.composeMultiplatform)
        alias(libs.plugins.composeCompiler)
    }

    kotlin {
        androidTarget {
            compilerOptions {
                jvmTarget.set(JvmTarget.JVM_11)
            }
        }

        sourceSets {
            androidMain.dependencies {
                implementation(compose.preview)
                implementation(libs.androidx.activity.compose)
            }
            commonMain.dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)
                implementation(compose.ui)
                implementation(compose.components.resources)
                implementation(compose.components.uiToolingPreview)
                implementation(libs.androidx.lifecycle.viewmodelCompose)
                implementation(libs.androidx.lifecycle.runtimeCompose)
            }
            commonTest.dependencies {
                implementation(libs.kotlin.test)
            }
        }
    }

build.gradle

    plugins {
        alias(libs.plugins.kotlinMultiplatform)
        alias(libs.plugins.androidApplication)
        alias(libs.plugins.composeMultiplatform)
        alias(libs.plugins.composeCompiler)
    }

    kotlin {
        androidTarget {
            compilerOptions {
                jvmTarget.set(JvmTarget.JVM_11)
            }
        }

        sourceSets {
            androidMain.dependencies {
                implementation(compose.preview)
                implementation(libs.androidx.activity.compose)
            }
            commonMain.dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material3)
                implementation(compose.ui)
                implementation(compose.components.resources)
                implementation(compose.components.uiToolingPreview)
                implementation(libs.androidx.lifecycle.viewmodelCompose)
                implementation(libs.androidx.lifecycle.runtimeCompose)
            }
            commonTest.dependencies {
                implementation(libs.kotlin.test)
            }
        }
    }

There is a relationship between these configurations, similar to Java and Android, but organized per source set and target.

- What you declare in `commonMainApi`:

  - Shared source sets: visible to all targets depending on `commonMain`.

  - Consumers: exported via the relevant `apiElements` for each target (e.g., `jvmApiElements`, `iosApiElements`), so downstream projects can compile against the shared API.

- What you declare in `commonMainImplementation`:

  - Your project: included in compilation for all targets that depend on `commonMain`.

  - Consumers: not exported — internal only.

- What you declare in target-specific configurations (e.g., `jvmMainImplementation`, `iosMainImplementation`):

  - Your project: visible only to that target’s compilation and runtime.

  - Consumers: packaged and published through that target’s consumable configuration (`jvmRuntimeElements`, etc.), but not exposed as API.

- `compileOnly` and `runtimeOnly` equivalents work similarly per source set: used only during compilation or runtime for that target, not exported to consumers.

- Test source sets (`commonTest`, `jvmTest`, etc.) have their own declarable and resolvable configurations, but are never published to consumers.

## 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 = configurations.create("implementation") {
        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").description = "example"  // Eagerly realizes the "name" configuration

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

build.gradle

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

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

    // Instead of
    configurations.getByName('name').description = 'example'  // Eagerly realizes the 'name' configuration

    // Use
    configurations.named('name').configure {
        description = 'example'                     // 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 = configurations.create("asciidoclet")

    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 = configurations.create("smokeTest") {
        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.