# Artifact Resolution

version 8.13

After constructing a dependency graph, Gradle can perform artifact resolution on the resolved graph.

Gradle APIs can be used to influence the process of artifact selection— the mapping of a graph to a set of artifacts.

Gradle can then expose the results of artifact selection as an `ArtifactCollection`. More commonly, the results are exposed as a `FileCollection`, which is a flat list of files.

## Artifact selection

Artifact selection operates on the dependency graph on a node-by-node basis. Each node in the graph may expose multiple sets of artifacts, but only one of those sets may be selected. For example, the `runtimeElements` variant of the Java plugins exposes a `jar`, `classes`, and `resources` artifact set. These three artifact sets represent the same distributable, but in different forms.

For each node (variant) in a graph, Gradle performs attribute matching over each set of artifacts exposed by that node to determine the best artifact set. If no artifact sets match the requested attributes, Gradle will attempt to construct an artifact transform chain to satisfy the request.

For more details on the attribute matching process, see the attribute matching section.

## Implicit artifact selection

By default, the attributes used for artifact selection are the same as those used for variant selection during graph resolution. These attributes are specified by the `Configuration#getAttributes()` property.

To perform artifact selection (and implicitly, graph resolution) using these default attributes, use the `FileCollection` and `ArtifactCollection` APIs.

| Files can also be accessed from the configuration’s `ResolvedConfiguration`, `LenientConfiguration`, `ResolvedArtifact` and `ResolvedDependency` APIs. However, these APIs are in maintenance mode and are discouraged for use in new development. These APIs perform artifact selection using the default attributes.

### Resolving files

To resolve files, we first define a task that accepts a `ConfigurableFileCollection` as input:

build.gradle.kts

    abstract class ResolveFiles : DefaultTask() {

        @get:InputFiles
        abstract val files: ConfigurableFileCollection

        @TaskAction
        fun print() {
            files.forEach {
                println(it.name)
            }
        }
    }

build.gradle

    abstract class ResolveFiles extends DefaultTask {

        @InputFiles
        abstract ConfigurableFileCollection getFiles()

        @TaskAction
        void print() {
            files.each {
                println(it.name)
            }
        }
    }

Then, we can wire up a resolvable configuration’s files to the task’s input. The `Configuration` directly implements `FileCollection` and can be wired directly. Alternatively, wiring through `Configuration#getIncoming()` is a more explicit approach:

build.gradle.kts

    tasks.register<ResolveFiles>("resolveConfiguration") {
        files.from(configurations.runtimeClasspath)
    }
    tasks.register<ResolveFiles>("resolveIncomingFiles") {
        files.from(configurations.runtimeClasspath.map { it.incoming.files })
    }

build.gradle

    tasks.register("resolveConfiguration", ResolveFiles) {
        files.from(configurations.runtimeClasspath)
    }
    tasks.register("resolveIncomingFiles", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.files)
    }

Running both of these tasks, we can see the output is identical:

    > Task :resolveConfiguration
    junit-platform-commons-1.11.0.jar
    junit-jupiter-api-5.11.0.jar
    opentest4j-1.3.0.jar

    > Task :resolveIncomingFiles
    junit-platform-commons-1.11.0.jar
    junit-jupiter-api-5.11.0.jar
    opentest4j-1.3.0.jar

### Resolving artifacts

Instead of consuming the files directly from the implicit artifact selection process, we can consume the artifacts, which contain both the files and the metadata.

This process is slightly more complicated, as in order to maintain Configuration Cache compatibility, we need to split the fields of `ResolvedArtifactResult` into two task inputs:

build.gradle.kts

    data class ArtifactDetails(
        val id: ComponentArtifactIdentifier,
        val variant: ResolvedVariantResult
    )

    abstract class ResolveArtifacts : DefaultTask() {

        @get:Input
        abstract val details: ListProperty<ArtifactDetails>

        @get:InputFiles
        abstract val files: ListProperty<File>

        fun from(artifacts: Provider<Set<ResolvedArtifactResult>>) {
            details.set(artifacts.map {
                it.map { artifact -> ArtifactDetails(artifact.id, artifact.variant) }
            })
            files.set(artifacts.map {
                it.map { artifact -> artifact.file }
            })
        }

        @TaskAction
        fun print() {
            assert(details.get().size == files.get().size)
            details.get().zip(files.get()).forEach { (details, file) ->
                println("${details.variant.displayName}:${file.name}")
            }
        }
    }

build.gradle

    class ArtifactDetails {
        ComponentArtifactIdentifier id
        ResolvedVariantResult variant

        ArtifactDetails(ComponentArtifactIdentifier id, ResolvedVariantResult variant) {
            this.id = id
            this.variant = variant
        }
    }

    abstract class ResolveArtifacts extends DefaultTask {

        @Input
        abstract ListProperty<ArtifactDetails> getDetails()

        @InputFiles
        abstract ListProperty<File> getFiles()

        void from(Provider<Set<ResolvedArtifactResult>> artifacts) {
            details.set(artifacts.map {
                it.collect { artifact -> new ArtifactDetails(artifact.id, artifact.variant) }
            })
            files.set(artifacts.map {
                it.collect { artifact -> artifact.file }
            })
        }

        @TaskAction
        void print() {
            List<ArtifactDetails> allDetails = details.get()
            List<File> allFiles = files.get()

            assert allDetails.size() == allFiles.size()
            for (int i = 0; i < allDetails.size(); i++) {
                def details = allDetails.get(i)
                def file = allFiles.get(i)
                println("${details.variant.displayName}:${file.name}")
            }
        }
    }

This task is initialized similarly to the file resolution task:

build.gradle.kts

    tasks.register<ResolveArtifacts>("resolveIncomingArtifacts") {
        from(configurations.runtimeClasspath.flatMap { it.incoming.artifacts.resolvedArtifacts })
    }

build.gradle

    tasks.register("resolveIncomingArtifacts", ResolveArtifacts) {
        from(configurations.runtimeClasspath.incoming.artifacts.resolvedArtifacts)
    }

Running this task, we can see that file metadata is included in the output:

    org.junit.platform:junit-platform-commons:1.11.0 variant runtimeElements:junit-platform-commons-1.11.0.jar
    org.junit.jupiter:junit-jupiter-api:5.11.0 variant runtimeElements:junit-jupiter-api-5.11.0.jar
    org.opentest4j:opentest4j:1.3.0 variant runtimeElements:opentest4j-1.3.0.jar

## Customizing artifact selection

An `ArtifactView` operates on top of the resolved dependency graph (i.e., `ResolutionResult`) but allows you to apply different attributes.

When you call a configuration’s `getFiles()`, Gradle selects artifacts based on the attributes used during graph resolution. However, an `ArtifactView` is more flexible. It allows you to resolve artifacts from the graph with custom attributes.

An `ArtifactView` allows you to:

1. Query artifacts with different attributes:

   - Suppose the graph resolved a dependency’s `runtime` variant. You can use an `ArtifactView` to extract artifacts from its `api` variant instead, even if they weren’t originally part of the resolved graph.

2. Extract specific types of artifacts:

   - You can request only the `.jar` files or a specific artifact type (e.g., sources, Javadoc) by specifying an attribute like `artifactType`.

3. Avoid side effects:

   - Using an `ArtifactView` allows you to extract artifacts without changing the underlying dependency resolution logic or configuration state.

In the following example, a producer project creates a library with the following variants and their attributes:

Next Step: Learn about Artifact Views >>