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

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

ArtifactView 可以

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

  2. 执行宽松的构件选择和解析:将 ArtifactViewlenient=true 结合使用,您可以忽略缺失的依赖项和其他错误。

  3. 过滤选定的构件:将 ArtifactViewcomponentFilter 结合使用,您可以从选定的构件中排除特定的组件。

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

ArtifactView 可以生成 FileCollectionArtifactCollection 两种结果。 以下示例仅演示如何使用 FileCollection 作为输出。

1. 执行变体重新选择

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

考虑以下示例组件结构,描述了一个典型的本地 Java 库,其中包含源代码和 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'

解析 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

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

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