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

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

ArtifactView 可以

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

  2. 执行宽松的构件选择和解析:使用 ArtifactView 并设置 lenient=true 允许您忽略缺失的依赖项和其他错误。

  3. 过滤选定的构件:使用 ArtifactView 并设置 componentFilter 允许您从选定的构件中排除特定组件。

  4. 触发转换:触发 ArtifactTransform 以将构件从一种类型转换为另一种类型。

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

1. 执行变体重新选择

标准构件选择只能在通过图选择结果选定的组件的变体之间进行选择。但是,在某些情况下,可能需要从与正在选择的图节点并行的变体中选择构件。

考虑下面的示例组件结构,它描述了一个典型的带有源和 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 变体。在标准构件选择期间,Gradle 将仅从 runtimeElements 下的构件集中进行选择。

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

此示例使用孵化中的 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 将在对新选定的变体执行构件选择之前,可选地再次执行图变体选择。当 Gradle 为 runtimeElements 节点选择构件时,它将使用 ArtifactView 上指定的属性重新选择图变体,从而选择 sourcesElements 变体。然后,将对 sourcesElements 变体执行传统的构件选择,以选择 jar 构件集。

结果是,为每个节点解析源 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. 执行宽松的构件选择和解析

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

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

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

我们可以看到任务成功了,失败的构件被省略了

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

ArtifactView API 可用于从生成的 FileCollectionArtifactCollection 中过滤掉特定的构件。

ArtifactViews 允许按组件过滤结果。使用 ArtifactView.componentFilter(Action) 方法,可以从已解析的结果中过滤掉选定变体的构件。该动作会传入拥有正在选择构件的变体的组件的 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 方法,我们可以指定只选择特定类型构件的过滤器

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

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

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

下面,我们使用构件转换一章中的解压示例来演示如何使用 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 块中指定的属性连接起来,执行构件选择。

任务输出显示构件已转换

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)可以在下一章中看到。

下一步: 了解构件转换 >>