Viewing and debugging dependencies
Gradle provides sufficient tooling to navigate large dependency graphs and mitigate situations that can lead to dependency hell. Users can choose to render the full graph of dependencies as well as identify the selection reason and origin for a dependency. The origin of a dependency can be a declared dependency in the build script or a transitive dependency in graph plus their corresponding configuration. Gradle offers both capabilities through visual representation via build scans and as command line tooling.
Build scans
If you do not know what build scans are, be sure to check them out! |
A build scan can visualize dependencies as a navigable, searchable tree. Additional context information can be rendered by clicking on a specific dependency in the graph.
Listing dependencies in a project
Gradle can visualize the whole dependency tree for every configuration available in the project.
Rendering the dependency tree is particularly useful if you’d like to identify which dependencies have been resolved at runtime. It also provides you with information about any dependency conflict resolution that occurred in the process and clearly indicates the selected version. The dependency report always contains declared and transitive dependencies.
The |
Let’s say you’d want to create tasks for your project that use the JGit library to execute SCM operations e.g. to model a release process. You can declare dependencies for any external tooling with the help of a custom configuration so that it doesn’t pollute other contexts like the compilation classpath for your production source code.
Every Gradle project provides the task dependencies
to render the so-called dependency report from the command line.
By default the dependency report renders dependencies for all configurations.
To focus on the information about one configuration, provide the optional parameter --configuration
.
For example, to show dependencies that would be on the test runtime classpath in a Java project, run:
gradle -q dependencies --configuration testRuntimeClasspath
Just like with project and task names, you can use abbreviated names to select a configuration. For example, you can specify tRC instead of testRuntimeClasspath if the pattern matches to a single configuration.
|
To see a list of all the pre-defined configurations added by the java plugin, see the documentation for the Java Plugin.
|
repositories {
mavenCentral()
}
configurations {
scm
}
dependencies {
scm 'org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r'
}
repositories {
mavenCentral()
}
configurations {
create("scm")
}
dependencies {
"scm"("org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r")
}
Example: Rendering the dependency report for a custom configuration
gradle -q dependencies --configuration scm
> gradle -q dependencies --configuration scm ------------------------------------------------------------ Root project 'dependencies-report' ------------------------------------------------------------ scm \--- org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r +--- com.jcraft:jsch:0.1.54 +--- com.googlecode.javaewah:JavaEWAH:1.1.6 +--- org.apache.httpcomponents:httpclient:4.3.6 | +--- org.apache.httpcomponents:httpcore:4.3.3 | +--- commons-logging:commons-logging:1.1.3 | \--- commons-codec:commons-codec:1.6 \--- org.slf4j:slf4j-api:1.7.2 A web-based, searchable dependency report is available by adding the --scan option.
The dependencies report provides detailed information about the dependencies available in the graph.
Any dependency that could not be resolved is marked with FAILED
in red color.
Dependencies with the same coordinates that can occur multiple times in the graph are omitted and indicated by an asterisk.
Dependencies that had to undergo conflict resolution render the requested and selected version separated by a right arrow character.
Identifying which dependency version was selected and why
Large software projects inevitably deal with an increased number of dependencies either through direct or transitive dependencies. The dependencies report provides you with the raw list of dependencies but does not explain why they have been selected or which dependency is responsible for pulling them into the graph.
Let’s have a look at a concrete example.
A project may request two different versions of the same dependency either as direct or transitive dependency.
Gradle applies version conflict resolution to ensure that only one version of the dependency exists in the dependency graph.
In this example the conflicting dependency is represented by commons-codec:commons-codec
.
repositories {
mavenCentral()
}
configurations {
scm
}
dependencies {
scm 'org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r'
scm 'commons-codec:commons-codec:1.7'
}
repositories {
mavenCentral()
}
configurations {
create("scm")
}
dependencies {
"scm"("org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r")
"scm"("commons-codec:commons-codec:1.7")
}
The dependency tree in a build scan renders the selection reason (conflict resolution) as well as the origin of a dependency if you click on a dependency and select the "Required By" tab.
Every Gradle project provides the task dependencyInsight
to render the so-called dependency insight report from the command line.
Given a dependency in the dependency graph you can identify the selection reason and track down the origin of the dependency selection.
You can think of the dependency insight report as the inverse representation of the dependency report for a given dependency.
The task takes the following parameters:
--dependency <dependency>
(mandatory)-
Indicates which dependency to focus on. It can be a complete
group:name
, or part of it. If multiple dependencies match, they are all printed in the report. --configuration <name>
(mandatory)-
Indicates which configuration to resolve for showing the dependency information (camelCase also supported like in
dependencies
task). Note that the Java plugin wires a convention with the valuecompileClasspath
, making the parameter optional. --singlepath
(optional)-
Indicates to render only a single path to the dependency. This might be useful to trim down the output in large graphs.
The |
Example: Using the dependency insight report for a given dependency
gradle -q dependencyInsight --dependency commons-codec --configuration scm
> gradle -q dependencyInsight --dependency commons-codec --configuration scm commons-codec:commons-codec:1.7 variant "default" [ org.gradle.status = release (not requested) ] Selection reasons: - By conflict resolution : between versions 1.7 and 1.6 commons-codec:commons-codec:1.7 \--- scm commons-codec:commons-codec:1.6 -> 1.7 \--- org.apache.httpcomponents:httpclient:4.3.6 \--- org.eclipse.jgit:org.eclipse.jgit:4.9.2.201712150930-r \--- scm A web-based, searchable dependency report is available by adding the --scan option.
As indicated above, omitting the --configuration
parameter in a project that is not a Java project will lead to an error:
> Dependency insight report cannot be generated because the input configuration was not specified. It can be specified from the command line, e.g: ':dependencyInsight --configuration someConf --dependency someDep'
For more information about configurations, see the documentation on declaring dependencies, which describes what dependency configurations are.
Understanding selection reasons
The "Selection reasons" part of the dependency insight report will list the different reasons as to why a dependency was selected. Have a look at the table below to understand the meaning of the different terms used:
Reason | Meaning |
---|---|
(Absent) |
This means that no other reason than having a reference, direct or transitive, was present |
Was requested : <text> |
The dependency appears in the graph, and the inclusion came with a |
Was requested : didn’t match versions <versions> |
The dependency appears in the graph, with a dynamic version, which did not include the listed versions.
This can also be followed by a |
Was requested : reject version <versions> |
The dependency appears in the graph, with a rich version containing one or more |
By conflict resolution : between versions <version> |
The dependency appeared multiple times in the graph, with different version requests. This resulted in conflict resolution to select the most appropriate version. |
By constraint |
A dependency constraint participated in the version selection.
This can also be followed by a |
By ancestor |
There is a rich version with a |
Selected by rule |
A dependency resolution rule overruled the default selection process.
This can also be followed by a |
Rejection : <version> by rule because <text> |
A |
Rejection: version <version>: <attributes information> |
The dependency has a dynamic version, and some versions did not match the requested attributes. |
Forced |
The build enforces the version of the dependency. |
Note that if multiple selection reasons exist in the graph, they will all be listed.
Resolving version conflicts
If the selected version does not match your expectation, Gradle offers a series of tools to help you control transitive dependencies.
Resolving variant selection errors
Sometimes a selection error will happen at the variant selection level. Have a look at the dedicated section to understand these errors and how to resolve them.
Resolving unsafe configuration resolution errors
Resolving a configuration can have side effects on Gradle’s project model, so Gradle needs manage access to each project’s configurations. There are a number of ways a configuration might be resolved unsafely. Gradle will produce a deprecation warning for each unsafe access. Each of these are bad practices and can cause strange and indeterminate errors.
If your build has an unsafe access deprecation warning, it needs to be fixed.
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.
In most cases, this issue can be resolved by creating a cross-project dependency on the other project. See the documentation for sharing outputs between projects for more information.
If you find a use case that can’t be resolved using these techniques, please let us know by filing a GitHub Issue adhering to our issue guidelines.