Working with Dependencies
Gradle provides an extensive API for navigating, inspecting and post-processing metadata and artifacts of resolved dependencies.
The main entry point for this functionality is the Configuration API. To learn more about the fundamentals of configurations, see Managing Dependency Configurations.
Iterating over dependencies assigned to a configuration
Sometimes you’ll want to implement logic based on the dependencies declared in the build script of a project e.g. to inspect them in a Gradle plugin. You can iterate over the set of dependencies assigned to a configuration with the help of the method Configuration.getDependencies(). Alternatively, you can also use Configuration.getAllDependencies() to include the dependencies declared in superconfigurations. These APIs only return the declared dependencies and do not trigger dependency resolution. Therefore, the dependency sets do not include transitive dependencies. Calling the APIs during the configuration phase of the build lifecycle does not result in a significant performance impact.
Groovy
Kotlin
task iterateDeclaredDependencies {
doLast {
DependencySet dependencySet = configurations.scm.dependencies
dependencySet.each {
logger.quiet "$it.group:$it.name:$it.version"
}
}
}
Iterating over artifacts resolved for a module
None of the dependency reporting helps you with inspecting or further processing the underlying, resolved artifacts of a module. A typical use case for accessing the artifacts is to copy them into a specific directory or filter out files of interest based on a specific file extension.
You can iterate over the complete set of artifacts resolved for a module with the help of the method FileCollection.getFiles(). Every file instance returned from the method points to its location in the dependency cache. Using this method on a Configuration
instance is possible as the interface extends FileCollection
.
Groovy
Kotlin
task iterateResolvedArtifacts {
dependsOn configurations.scm
doLast {
configurations.scm.each {
logger.quiet it.absolutePath
}
}
}
✨
|
Iterating over the artifacts of a module automatically resolves the configuration. A resolved configuration becomes immutable and cannot add or remove dependencies. If needed you can copy a configuration for further modification via Configuration.copy(). |
Navigating the dependency graph
As a plugin developer, you may want to navigate the full graph of dependencies assigned to a configuration e.g. for turning the dependency graph into a visualization. You can access the full graph of dependencies for a configuration with the help of the ResolutionResult.
The resolution result provides various methods for accessing the resolved and unresolved dependencies. For demonstration purposes the sample code uses ResolutionResult.getRoot() to access the root node the resolved dependency graph. Each dependency of this component returns an instance of ResolvedDependencyResult or UnresolvedDependencyResult providing detailed information about the node.
Groovy
Kotlin
task walkDependencyGraph(type: DependencyGraphWalk) {
dependsOn configurations.scm
}
class DependencyGraphWalk extends DefaultTask {
@TaskAction
void walk() {
Configuration configuration = project.configurations.scm
ResolutionResult resolutionResult = configuration.incoming.resolutionResult
ResolvedComponentResult root = resolutionResult.root
logger.quiet configuration.name
traverseDependencies(0, root.dependencies)
}
private void traverseDependencies(int level, Set<? extends DependencyResult> results) {
for (DependencyResult result : results) {
if (result instanceof ResolvedDependencyResult) {
ResolvedComponentResult componentResult = result.selected
ComponentIdentifier componentIdentifier = componentResult.id
String node = calculateIndentation(level) + "- $componentIdentifier.displayName ($componentResult.selectionReason)"
logger.quiet node
traverseDependencies(level + 1, componentResult.dependencies)
} else if (result instanceof UnresolvedDependencyResult) {
ComponentSelector componentSelector = result.attempted
String node = calculateIndentation(level) + "- $componentSelector.displayName (failed)"
logger.quiet node
}
}
}
private String calculateIndentation(int level) {
' ' * level
}
}
Accessing a module’s metadata file
As part of the dependency resolution process, Gradle downloads the metadata file of a module and stores it in the dependency cache. Some organizations enforce strong restrictions on accessing repositories outside of internal network. Instead of downloading artifacts, those organizations prefer to provide an "installable" Gradle cache with all artifacts contained in it to fulfill the build’s dependency requirements.
The artifact query API provides access to the raw files of a module. Currently, it allows getting a handle to the metadata file and some selected, additional artifacts (e.g. a JVM-based module’s source and Javadoc files). The main API entry point is ArtifactResolutionQuery.
Let’s say you wanted to post-process the metadata file of a Maven module. The group, name and version of the module component serve as input to the artifact resolution query. After executing the query, you get a handle to all components that match the criteria and their underlying files. Additionally, it’s very easy to post-process the metadata file. The example code uses Groovy’s XmlSlurper to ask for POM element values.
Groovy
Kotlin
plugins {
id 'java-library'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:18.0'
}
task printGuavaMetadata {
dependsOn configurations.compileClasspath
doLast {
ArtifactResolutionQuery query = dependencies.createArtifactResolutionQuery()
.forModule('com.google.guava', 'guava', '18.0')
.withArtifacts(MavenModule, MavenPomArtifact)
ArtifactResolutionResult result = query.execute()
for(component in result.resolvedComponents) {
Set<ArtifactResult> mavenPomArtifacts = component.getArtifacts(MavenPomArtifact)
ArtifactResult guavaPomArtifact = mavenPomArtifacts.find { it.file.name == 'guava-18.0.pom' }
def xml = new XmlSlurper().parse(guavaPomArtifact.file)
println guavaPomArtifact.file.name
println xml.name
println xml.description
}
}
}