# Artifact Views

version 9.8.0-milestone-2

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

An `ArtifactView` operates on top of the resolved graph but allows you to apply different attributes. It lets you retrieve artifacts that match a new set of criteria, even if they were not part of the original graph resolution.

An `ArtifactView` can:

1. Select Alternative Variants such as sources or javadoc, for an entire resolution: Normally, an artifact must match both the graph’s attributes and the `ArtifactView` attributes. With `withVariantReselection`, you can select artifacts from any available variants within a component.

2. Perform Lenient Artifact Selection and resolution: Using `ArtifactView` with `lenient=true` allows you to ignore missing dependencies and other errors.

3. Filter Selected Artifacts: Using `ArtifactView` with `componentFilter` lets you exclude specific components from the selected artifacts.

4. Trigger Transforms: Trigger an `ArtifactTransform` to change an artifact from one type to another.

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

## 1. Performing variant reselection

Standard artifact selection can only select between variants of the component selected by the result of graph 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.

| Variant reselection operates on the components already present in the resolved graph: it re-picks a variant of each component in place. It does not re-run graph resolution. As a result, any dependencies declared on the reselected variant — whether ordinary dependencies on other modules or `available-at` pointers to a different component — are not followed, and the artifacts of those additional components do not appear in the view. To include such artifacts, declare a separate `Configuration` whose request attributes already select the target variant; graph resolution will then traverse the variant’s dependencies as usual.

## 2. Performing lenient artifact selection and 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

## 3. Filtering selected 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 the selected variant can be filtered from the resolved 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

## 4. 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 the selected artifact. When Gradle cannot find a matching set of artifacts from the node in question, it will attempt to satisfy the request by transforming the available artifact 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

The transform code (`ArtifactTransform`) used uncompress the JAR file (from `ZIP` to `UNZIP`) can be seen in the next chapter.

Next Step: Learn about Artifact Transforms >>