skip to content

Resolution Result and Artifact View

Inspecting a resolved graph programmatically through the resolution result, and filtering or reshaping artifacts with an artifactView. Asked because it is how build logic reports on dependencies without forcing eager resolution.

on this pageshow

questions

5

What is the ResolutionResult API in Gradle, and how does it differ from simply listing the files of a configuration?

level: middleimportance: must knowfreq 55%

answer

  1. incoming.resolutionResult
  2. allComponents / allDependencies
  3. graph vs files — decoupled
  4. selectionReason metadata-only
  5. getRoot() for true traversal

basics

~10 s

ResolutionResult gives the resolved dependency graph as components and dependencies, including versions chosen and reasons. Listing files gives only the resolved artifacts on disk, with no graph structure or selection metadata.

solid answer

~40 s

`configuration.incoming.resolutionResult` exposes the **resolved dependency graph** — the model of which module versions were selected and why. Through `allComponents` and `allDependencies` you can walk every node (a `ResolvedComponentResult` with its `ModuleVersionIdentifier`, selection reason, and variant) and every edge (`DependencyResult`, which may be resolved or unresolved). This is purely the graph: it answers "what got selected and why," including conflict-resolution and constraint reasons. Resolving the **files** of a configuration (e.g. iterating `configuration` or using an `artifactView`) instead downloads/locates the actual artifacts. The two are decoupled: you can inspect the graph without downloading artifacts, and you can fetch a filtered set of artifacts via `artifactView` without touching the graph API. Use ResolutionResult for reporting/diagnostics; use files/artifactView when you need the bytes.

code

kotlin · 8 lines
kotlin
tasks.register("printGraph") {
    val rr = configurations.getByName("runtimeClasspath").incoming.resolutionResult
    doLast {
        rr.allComponents {
            println("${'$'}{id.displayName} <- ${'$'}{selectionReason}")
        }
    }
}

go deeper

for a junior

Know that ResolutionResult describes the resolved dependency graph (which versions were picked), distinct from the actual files.

for a middle

Explain incoming.resolutionResult, allComponents/allDependencies, ResolvedComponentResult vs DependencyResult, and that graph and files are decoupled.

for a senior

Discuss selectionReason descriptors, true traversal from getRoot(), and the performance cost of eager access at configuration time.

for a principal

Frame ResolutionResult as the foundation for org-wide dependency governance/reporting tooling (license scans, version dashboards) built without forcing artifact downloads.

## Two distinct outputs of resolution When Gradle resolves a `Configuration`, it produces two related but separate things: 1. **A dependency graph** — the abstract model of which module versions were selected, the edges between them, and *why* each was chosen (requested version, conflict resolution, constraint, forced, etc.). 2. **A set of artifacts** — the actual files (jars, AARs, etc.) produced by resolving the variants of those components. The `ResolutionResult` API exposes the **graph**, not the files. ## Accessing the graph ```kotlin val result = configurations.getByName("runtimeClasspath") .incoming.resolutionResult result.allComponents { // ResolvedComponentResult println("${'$'}{id} selected because ${'$'}{selectionReason}") } result.allDependencies { // DependencyResult when (this) { is ResolvedDependencyResult -> println("resolved -> ${'$'}{selected.id}") is UnresolvedDependencyResult -> println("FAILED ${'$'}{requested}: ${'$'}{failure.message}") } } ``` Key types: - **`ResolvedComponentResult`** — a node. Carries `id` (a `ComponentIdentifier`), `moduleVersion` (`ModuleVersionIdentifier`), `selectionReason` (`ComponentSelectionReason`, with descriptors explaining conflict resolution/constraint/forced), and the `variants` selected. - **`DependencyResult`** — an edge; subtype `ResolvedDependencyResult` (has `selected` node + `requested`) or `UnresolvedDependencyResult` (has `failure`). - **`getRoot()`** — the synthetic root component representing the configuration itself; you can traverse `dependencies` from it for a true graph walk instead of the flat `allComponents`/`allDependencies` callbacks. ## Why it is decoupled from files The graph can be queried **without downloading any artifact** — selection is metadata-only. This makes ResolutionResult ideal for dependency-report tasks (`dependencies`, `dependencyInsight` are built on top of it), license scanners, and build-logic that needs to reason about versions. Conversely, fetching files (iterating the configuration or using an `artifactView`) can require downloading and may fail for reasons unrelated to graph selection (e.g. a missing classifier). ## Laziness The callbacks (`allComponents`, etc.) trigger resolution when accessed, so accessing them during configuration time forces eager resolution and hurts build performance. Prefer accessing them at **execution time** inside a task action, or wrap the work in a `Provider` so it stays lazy. ## When to use which - Reporting "what version won and why" → ResolutionResult graph. - Need the actual jars (e.g. to build a fat jar, copy, classpath) → resolve files / `artifactView`.

  • Why might accessing resolutionResult.allComponents in the configuration phase hurt build performance?
    It forces the configuration to resolve eagerly during configuration time, defeating lazy resolution and slowing every build invocation, even ones that don't need that configuration.
  • How would you find why a particular version was selected?
    Walk allComponents, find the matching ResolvedComponentResult, and read selectionReason.descriptions — each ComponentSelectionDescriptor states the cause (conflict resolution, constraint, forced, etc.). dependencyInsight uses the same data.

saying these in an interview costs you the question

  • Claiming ResolutionResult returns the jar files — it returns the graph model, not artifacts.
  • Saying you must download artifacts to inspect selected versions — graph inspection is metadata-only.

context

open as a page

What is an artifactView in Gradle, and why would you set lenient = true on it?

level: middleimportance: should knowfreq 45%

basics

~10 s

An artifactView lazily produces a filtered/transformed set of resolved artifacts from a configuration. lenient = true makes it ignore resolution and artifact failures instead of throwing, returning whatever resolved successfully.

open as a page

How do you wire the artifacts of a configuration into a custom task lazily, and why does laziness matter here?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Use configuration.incoming.artifactView{...}.artifacts.artifactFiles (a lazy FileCollection) as the task's @InputFiles. Resolution then happens at execution time, not configuration time, keeping builds fast and configuration-cache compatible.

open as a page

How would you programmatically determine why Gradle selected a particular version of a dependency, using the ResolutionResult API?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Walk the graph via incoming.resolutionResult.allComponents, find the ResolvedComponentResult for the module, and read its selectionReason — its descriptors explain the cause (conflict resolution, constraint, forced, etc.).

open as a page

Using artifactView's componentFilter, how would you separate project (local) artifacts from external module artifacts, and when is that useful?

level: juniorimportance: nice to knowfreq 20%

basics

~10 s

Set componentFilter { id -> id is ProjectComponentIdentifier } to keep only local project artifacts, or ModuleComponentIdentifier for external ones. Useful for fat-jar/relocation logic that should treat your own code differently from third-party libs.

open as a page