What is a configuration which can't be directly resolved?

I reported improper bug:

because I didn’t understand meaning of:

 (n) - Not resolved (configuration is not meant to be resolved)

Later I found the way to force implementation and api configurations to resolve via:

configurations.implementation.canBeResolved = true
configurations.api.canBeResolved = true

because otherwise their resolution is delayed:

$ gradle dependencies --configuration api
api - API dependencies for source set 'main'. (n)
\--- org.springframework:spring-core (n)
(n) - Not resolved (configuration is not meant to be resolved)

DefaultConfiguration has:

private void assertResolvingAllowed() {
    if (!canBeResolved) {
        throw new IllegalStateException("Resolving configuration '" + name + "' directly is not allowed");
    }
}

It is a trick to force broken plugins / build scripts fail fast?

How can I enforce configuration resolution from CLI? I like to troubleshoot resolutions with gradle dependencies --configuration api!

Old trick:

task copyApiDeps(type: Copy) {
    into "$buildDir/apiDeps"
    from configurations.api
}

also fails with:

gradle copyApiDeps
Could not determine the dependencies of task ':copyApiDeps'.
> Resolving configuration 'api' directly is not allowed

and docs has examples now like:

https://docs.gradle.org/current/userguide/dependency_locking.html

task resolveAndLockAll {
    doFirst {
        assert gradle.startParameter.writeDependencyLocks
    }
    doLast {
        configurations.findAll {
            // Add any custom filtering on the configurations to be resolved
            it.canBeResolved
        }.each { it.resolve() }
    }
}

Gradle configurations have been around for a long time and acquired many roles.

Historical and deprecated configurations like compile and runtime would serve both as a dependency container and as a producer of files, for example jars to form a classpath.

In Gradle 3.3, different roles where given to configurations, which can be seen in the linked table (just above linked paragraph).

So taking the Java library plugin in example (with canBeResolved and canBeConsumed after it):

  • implementation is a dependency container (false, false)
  • compileClasspath is used to generate the compilation classpath (true, false)
  • apiElements is used to provide a reduced set of transitive dependencies for compilation (false, true)

The relationships between these configuration is illustrated in the plugin documentation.

In short, you should never change the canBeConsumed and canBeResolved flags of configuration you do not create. Instead refer to the documentation to understand which one to access based on the objective.
And when you declare a configuration, you should properly set the canBeConsumed and canBeResolved flags according to the role you want to give your configuration.

So for example, instead of running gradle dependencies --configuration api you should run gradle dependencies --configuration compileClasspath because it will resolve properly and take into account everything that makes the compile classpath in your project. api declared dependencies but also compileOnly and implementation ones. Given the way Gradle does conflict resolution either of these two configurations could include libraries that would make the resolution result different compared to resolving api in isolation.

2 Likes