在某些情况下,需要自定义 artifact 解析过程。ArtifactView API 是影响 Gradle 中 artifact 选择的主要机制。

ArtifactView 在已解析的图之上操作,但允许您应用不同的属性。它使您可以检索符合新一组条件的 artifact,即使它们不是原始图解析的一部分。

ArtifactView 可以

  1. 选择替代变体,例如整个解析过程的 sources 或 javadoc:通常,artifact 必须同时匹配图的属性和 ArtifactView 的属性。使用 withVariantReselection,您可以从组件内的任何可用变体中选择 artifact。

  2. 执行宽松的 Artifact 选择和解析:使用带有 lenient=trueArtifactView 允许您忽略缺失的依赖项和其他错误。

  3. 过滤选定的 Artifact:使用带有 componentFilterArtifactView 允许您从选定的 artifact 中排除特定的组件。

  4. 触发转换:触发 ArtifactTransform 将一个 artifact 从一种类型转换为另一种类型。

ArtifactView 可以生成 FileCollectionArtifactCollection 两种结果。下面的示例仅演示了将 FileCollection 作为输出。

1. 执行变体重新选择

标准 artifact 选择只能在图选择结果所选组件的变体之间进行选择。然而,在某些情况下,可能需要从与被选图节点并行的变体中选择 artifact。

考虑下面的示例组件结构,描述了一个典型的带有 sources 和 javadoc 的本地 Java 库

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'

解析 Java 运行时类路径将从上述示例组件中选择 runtimeElements 变体。在标准 artifact 选择过程中,Gradle 将仅从 runtimeElements 下的 artifact 集合中进行选择。

然而,通常希望为图中的每个节点选择所有 sources 或所有 javadoc。考虑以下示例,它为给定的运行时类路径选择所有 sources

此示例使用了孵化中的 API。
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)
}

使用 ArtifactView.withVariantReselection() API,Gradle 会在对新选定的变体执行 artifact 选择之前,可选地再次执行图变体选择。当 Gradle 为 runtimeElements 节点选择 artifact 时,它将使用在 ArtifactView 上指定的属性重新选择图变体,从而选择 sourcesElements 变体。然后,将在 sourcesElements 变体上执行传统的 artifact 选择以选择 jar artifact 集合。

结果是,为每个节点解析了 sources jar

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

使用此 API 时,用于变体重新选择的属性仅由 ArtifactView.getAttributes() 方法指定。在变体重新选择期间,配置上指定的图解析属性将完全被忽略。

2. 执行宽松的 artifact 选择和解析

ArtifactView API 也可用于执行宽松的 artifact 解析。这允许在包含失败的图上执行 artifact 解析,例如请求的模块未找到、请求的模块版本不存在或冲突未解决时。此外,当图已成功解析但相应的 artifact 无法下载时,也可以使用宽松的 artifact 解析来解析 artifact。

考虑以下示例,其中一些依赖项可能不存在

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

通过使用 ArtifactView.lenient() 方法执行宽松解析

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

我们可以看到任务成功了,并省略了失败的 artifact

> 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. 过滤选定的 artifact

ArtifactView API 可用于从结果 FileCollectionArtifactCollection 中过滤特定的 artifact。

ArtifactViews 允许按组件过滤结果。使用 ArtifactView.componentFilter(Action) 方法,可以从已解析的结果中过滤所选变体的 artifact。该 action 将收到拥有正在为其选择 artifact 的变体的 ComponentIdentifier

考虑以下示例,其中我们有一个项目依赖项和一个外部依赖项

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

使用 componentFilter 方法,我们可以指定仅选择特定类型 artifact 的过滤器

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

注意我们如何分别解析项目依赖项和模块依赖项

> 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. 触发 artifact 转换

ArtifactView 可用于使用与解析图所用属性不同的属性来触发 artifact 选择。

对于图中的每个节点,都会对该节点执行 artifact 选择。最常见的是,此 API 用于请求选定 artifact 上不存在的属性。当 Gradle 无法从相关节点找到匹配的 artifact 集合时,它将尝试使用项目中注册的 artifact 转换来转换可用 artifact,以满足请求。

下面,我们使用 artifact 转换章节中的解压缩示例来演示如何使用 ArtifactView API 请求触发转换的属性

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 使用配置上指定的图解析属性以及 ArtifactViewattributes 块中指定的属性串联起来执行 artifact 选择。

任务输出显示 artifact 已被转换

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

用于解压 JAR 文件(从 ZIPUNZIP)的转换代码(ArtifactTransform)可以在下一章中看到。

下一步: 了解 Artifact 转换 >>