# Artifact Resolution

version 8.11

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

In some cases, it is desirable to customize the selection process. The `ArtifactView` API is the primary mechanism for influencing artifact selection in Gradle.

An `ArtifactView` can:

- Trigger artifact transforms

- Select alternative variants, such as sources or javadoc, for an entire resolution

- Perform lenient artifact selection and resolution

- Filter selected artifacts

| The `ArtifactView` can produce results as both a `FileCollection` and an `ArtifactCollection`. The below examples will only demonstrate using a `FileCollection` as the output.

### Triggering artifact transforms

An `ArtifactView` can be used to trigger artifact selection using attributes different from those used to resolve the graph.

For each node in the graph, artifact selection is performed for that node. Most commonly, this API is used to request attributes that are not present on any artifact set from the variant that artifacts are being selected from. When Gradle cannot find a matching artifact set from the node in question, it will attempt to satisfy the request by transforming the available artifact sets using the artifact transforms registered on the project.

Below, we use the unzip example from the artifact transforms chapter to demonstrate how to use the `ArtifactView` API to request attributes that trigger a transform:

build.gradle.kts

    tasks.register<ResolveFiles>("resolveTransformedFiles") {
        files.from(configurations.runtimeClasspath.map {
            it.incoming.artifactView {
                attributes {
                    attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES_AND_RESOURCES))
                    attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE)
                }
            }.files
        })
    }

build.gradle

    tasks.register("resolveTransformedFiles", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.artifactView {
            attributes {
                attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.CLASSES_AND_RESOURCES))
                attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE)
            }
        }.files)
    }

Gradle performs artifact selection using the graph resolution attributes specified on the configuration, concatenated with the attributes specified in the `attributes` block of the `ArtifactView`.

The task output shows that the artifacts have been transformed:

    junit-platform-commons-1.11.0.jar-unzipped
    junit-jupiter-api-5.11.0.jar-unzipped
    opentest4j-1.3.0.jar-unzipped

### Performing variant reselection

Standard artifact selection can only select between and transform artifact sets exposed by the node under selection. However, in some cases, it may be desirable to select artifacts from a variant parallel to the graph node being selected.

Consider the example component structure below, describing a typical local Java library with sources and javadoc:

    variant 'apiElements'
        artifact set 'jar'
        artifact set 'classes'
        artifact set 'resources'
    variant 'runtimeElements'
        artifact set 'jar'
        artifact set 'classes'
        artifact set 'resources'
    variant 'javadocElements'
        artifact set 'jar'
    variant 'sourcesElements'
        artifact set 'jar'

Resolving a Java runtime classpath will select the `runtimeElements` variant from the above example component. During standard artifact selection, Gradle will select solely from the artifact sets under `runtimeElements`.

However, it is common to want to select all sources or all javadoc for every node in the graph. Consider the following example which selects all sources for a given runtime classpath:

| This example uses incubating APIs.

build.gradle.kts

    tasks.register<ResolveFiles>("resolveSources") {
        files.from(configurations.runtimeClasspath.map {
            it.incoming.artifactView {
                withVariantReselection()
                attributes {
                    attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME));
                    attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION));
                    attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL));
                    attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES));
                }
            }.files
        })
    }

build.gradle

    tasks.register("resolveSources", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.artifactView {
            withVariantReselection()
            attributes {
                attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME));
                attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION));
                attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL));
                attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, DocsType.SOURCES));
            }
        }.files)
    }

Using the `ArtifactView#withVariantReselection()` API, Gradle will optionally perform graph variant selection again before performing artifact selection on the new selected variant. When Gradle selects artifacts for the `runtimeElements` node, it will use the attributes specified on the `ArtifactView` to reselect the graph variant, thus selecting the `sourcesElements` variant instead. Then, traditional artifact selection will be performed on the `sourcesElements` variant to select the `jar` artifact set.

As a result, the sources jar is resolved for each node:

    junit-platform-commons-1.11.0-sources.jar
    junit-jupiter-api-5.11.0-sources.jar
    opentest4j-1.3.0-sources.jar

When this API is used, the attributes used for variant reselection are specified solely by the `ArtifactView#getAttributes()` method. The graph resolution attributes specified on the configuration are completely ignored during variant reselection.

### Performing lenient artifact resolution

The `ArtifactView` API can also be used to perform lenient artifact resolution. This allows artifact resolution to be performed on a graph that contains failures — for example when a requested module was not found, the requested module version did not exist, or a conflict was not resolved. Furthermore, lenient artifact resolution can be used to resolve artifacts when the graph was successfully resolved, but the corresponding artifacts could not be downloaded.

Consider the following example, where some dependencies may not exist:

build.gradle.kts

    dependencies {
        implementation("does:not:exist")
        implementation("org.junit.jupiter:junit-jupiter-api:5.11.0")
    }

build.gradle

    dependencies {
        implementation("does:not:exist")
        implementation("org.junit.jupiter:junit-jupiter-api:5.11.0")
    }

Lenient resolution is performed by using the `ArtifactView#lenient()` method:

build.gradle.kts

    tasks.register<ResolveFiles>("resolveLenient") {
        files.from(configurations.runtimeClasspath.map {
            it.incoming.artifactView {
                isLenient = true
            }.files
        })
    }

build.gradle

    tasks.register("resolveLenient", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.artifactView {
            lenient = true
        }.files)
    }

We can see that the task succeeds with the failing artifact omitted:

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

    BUILD SUCCESSFUL in 0s

### Filtering artifacts

The `ArtifactView` API can be used to filter specific artifacts from the resulting `FileCollection` or `ArtifactCollection`.

`ArtifactViews` allow results to be filtered on a per-component basis. Using the `ArtifactView#componentFilter(Action)` method, artifacts from certain components may be filtered from the result. The action is passed the `ComponentIdentifier` of the component that owns the variant that artifacts are being selected for.

Consider the following example, where we have one project dependency and one external dependency:

build.gradle.kts

    dependencies {
        implementation(project(":other"))
        implementation("org.junit.jupiter:junit-jupiter-api:5.11.0")
    }

build.gradle

    dependencies {
        implementation(project(":other"))
        implementation("org.junit.jupiter:junit-jupiter-api:5.11.0")
    }

Using the `componentFilter` method, we can specify filters that select only artifacts of a certain type:

build.gradle.kts

    tasks.register<ResolveFiles>("resolveProjects") {
        files.from(configurations.runtimeClasspath.map {
            it.incoming.artifactView {
                componentFilter {
                    it is ProjectComponentIdentifier
                }
            }.files
        })
    }
    tasks.register<ResolveFiles>("resolveModules") {
        files.from(configurations.runtimeClasspath.map {
            it.incoming.artifactView {
                componentFilter {
                    it is ModuleComponentIdentifier
                }
            }.files
        })
    }

build.gradle

    tasks.register("resolveProjects", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.artifactView {
            componentFilter {
                it instanceof ProjectComponentIdentifier
            }
        }.files)
    }
    tasks.register("resolveModules", ResolveFiles) {
        files.from(configurations.runtimeClasspath.incoming.artifactView {
            componentFilter {
                it instanceof ModuleComponentIdentifier
            }
        }.files)
    }

Notice how we resolve project dependencies and module dependencies separately:

    > Task :resolveProjects
    other.jar

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