# Working with Dependencies

version 5.4.1

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.

Example 1. Iterating over the dependencies assigned to a configuration
build.gradle

    task iterateDeclaredDependencies {
        doLast {
            DependencySet dependencySet = configurations.scm.dependencies

            dependencySet.each {
                logger.quiet "$it.group:$it.name:$it.version"
            }
        }
    }

build.gradle.kts

    tasks.register("iterateDeclaredDependencies") {
        doLast {
            val dependencySet = configurations["scm"].dependencies

            dependencySet.forEach {
                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`.

Example 2. Iterating over the artifacts resolved for a module
build.gradle

    task iterateResolvedArtifacts {
        dependsOn configurations.scm

        doLast {
            configurations.scm.each {
                logger.quiet it.absolutePath
            }
        }
    }

build.gradle.kts

    tasks.register("iterateResolvedArtifacts") {
        val scm = configurations["scm"]
        dependsOn(scm)

        doLast {
            scm.forEach {
                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.

Example 3. Walking the resolved and unresolved dependencies of a configuration
build.gradle

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

build.gradle.kts

    tasks.register<DependencyGraphWalk>("walkDependencyGraph") {
        dependsOn(configurations["scm"])
    }

    open class DependencyGraphWalk: DefaultTask() {
        @TaskAction
        fun walk() {
            val configuration: Configuration = project.configurations.getByName("scm")
            val resolutionResult: ResolutionResult = configuration.incoming.resolutionResult
            val root: ResolvedComponentResult = resolutionResult.root
            logger.quiet(configuration.name)
            traverseDependencies(0, root.dependencies)
        }

        private fun traverseDependencies(level: Int, results: Set<DependencyResult>) {
            results.forEach { result ->
                if (result is ResolvedDependencyResult) {
                    val componentResult: ResolvedComponentResult = result.selected
                    val componentIdentifier: ComponentIdentifier = componentResult.id
                    val node: String = "${calculateIndentation(level)}- ${componentIdentifier.displayName} (${componentResult.selectionReason})"
                    logger.quiet(node)
                    traverseDependencies(level + 1, componentResult.dependencies)
                } else if (result is UnresolvedDependencyResult) {
                    val componentSelector: ComponentSelector = result.attempted
                    val node: String = "${calculateIndentation(level)}- ${componentSelector.displayName} (failed)"
                    logger.quiet(node)
                }
            }
        }

        private fun calculateIndentation(level: Int) = "     ".repeat(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.

Example 4. Accessing a Maven module’s metadata artifact
build.gradle

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

build.gradle.kts

    import groovy.util.XmlSlurper

    plugins {
        `java-library`
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("com.google.guava:guava:18.0")
    }

    tasks.register("printGuavaMetadata") {
        dependsOn(configurations.compileClasspath)

        doLast {
            val query: ArtifactResolutionQuery = dependencies.createArtifactResolutionQuery()
                .forModule("com.google.guava", "guava", "18.0")
                .withArtifacts(MavenModule::class, MavenPomArtifact::class)
            val result: ArtifactResolutionResult = query.execute()

            result.resolvedComponents.forEach { component ->
                val mavenPomArtifacts: Set<ArtifactResult> = component.getArtifacts(MavenPomArtifact::class)
                val guavaPomArtifact =
                    mavenPomArtifacts.find { it is ResolvedArtifactResult && it.file.name == "guava-18.0.pom" } as ResolvedArtifactResult
                val xml = XmlSlurper().parse(guavaPomArtifact.file)
                println(guavaPomArtifact.file.name)
                println(xml.getProperty("name"))
                println(xml.getProperty("description"))
            }
        }
    }