在建構相依性圖形之後,Gradle 可以在已解析的圖形上執行構件解析

Gradle API 可用於影響構件選擇的過程 — 將圖形映射到一組構件。

然後,Gradle 可以將構件選擇的結果公開為 ArtifactCollection。更常見的是,結果以 FileCollection 的形式公開,這是一個檔案的平面列表。

構件選擇

構件選擇在相依性圖形上以節點為單位運作。圖形中的每個節點可能會公開多組構件,但只能選擇其中一組。例如,Java 外掛的 runtimeElements 變體公開了 jarclassesresources 構件集。這三個構件集代表相同的可發行物,但形式不同。

對於圖形中的每個節點(變體),Gradle 會對該節點公開的每組構件執行屬性匹配,以確定最佳的構件集。如果沒有構件集符合請求的屬性,Gradle 將嘗試建構構件轉換鏈來滿足請求。

有關屬性匹配過程的更多詳細資訊,請參閱屬性匹配章節。

隱式構件選擇

預設情況下,用於構件選擇的屬性與圖形解析期間用於變體選擇的屬性相同。這些屬性由 Configuration#getAttributes() 屬性指定。

若要使用這些預設屬性執行構件選擇(以及隱式地,圖形解析),請使用 FileCollectionArtifactCollection API。

也可以從配置的 ResolvedConfigurationLenientConfigurationResolvedArtifactResolvedDependency API 存取檔案。但是,這些 API 處於維護模式,不建議在新開發中使用。這些 API 使用預設屬性執行構件選擇。

解析檔案

若要解析檔案,我們先定義一個接受 ConfigurableFileCollection 作為輸入的任務

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

然後,我們可以將可解析配置的檔案連接到任務的輸入。Configuration 直接實作 FileCollection,可以直接連接。或者,透過 Configuration#getIncoming() 連接是一種更明確的方法

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

執行這兩個任務,我們可以看見輸出是相同的

> 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

解析構件

與其直接從隱式構件選擇過程中使用檔案,不如使用構件,其中包含檔案和 metadata。

此過程稍微複雜一些,因為為了維持配置快取的相容性,我們需要將 ResolvedArtifactResult 的欄位拆分為兩個任務輸入

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

此任務的初始化方式與檔案解析任務類似

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

執行此任務,我們可以看見檔案 metadata 包含在輸出中

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

自訂構件選擇

ArtifactView 在已解析的相依性圖形(即 ResolutionResult)之上運作,但允許您套用不同的屬性

當您呼叫配置的 getFiles() 時,Gradle 會根據圖形解析期間使用的屬性選擇構件。但是,ArtifactView 更靈活。它允許您使用自訂屬性從圖形解析構件。

ArtifactView 允許您

  1. 使用不同的屬性查詢構件:

    • 假設圖形解析了相依性的 runtime 變體。您可以使用 ArtifactView 從其 api 變體中提取構件,即使它們最初不是已解析圖形的一部分。

  2. 提取特定類型的構件:

    • 您可以透過指定類似 artifactType 的屬性,僅請求 .jar 檔案或特定的構件類型(例如,sources、Javadoc)。

  3. 避免副作用:

    • 使用 ArtifactView 允許您提取構件,而無需變更底層的相依性解析邏輯或配置狀態。

在以下範例中,生產者專案建立一個具有以下變體及其屬性的函式庫

下一步: 了解構件視圖 >>