在其他相依性管理引擎中,例如 Apache Maven™,相依性和人工製品會繫結到一個元件,而該元件會在特定 GAV(群組-人工製品-版本)座標中發佈。此元件的相依性集合始終相同,無論從元件中使用哪個人工製品。

如果元件確實有多個人工製品,則每個人工製品都會由一個繁瑣的分類器識別。沒有與分類器相關聯的共用語意,這使得難以保證全球一致的相依性圖。這表示沒有任何機制可以防止單一元件的(例如 jdk7jdk8 分類器)多個人工製品出現在類別路徑中,並造成難以診斷的問題。

Maven 元件模型

component model maven
圖 1. Maven 元件模型

Gradle 元件模型

component model gradle
圖 2. Gradle 組件模型

Gradle 的相依性管理引擎是變異感知的。

除了組件之外,Gradle 還有一個組件變異的概念。變異對應於組件可使用的不同方式,例如 Java 編譯、原生連結或文件。人工製品附加到變異,每個變異都可能有一組不同的相依性。

當有多個變異時,Gradle 如何知道要選擇哪個變異?變異的配對是透過使用屬性來完成的,屬性提供語意給變異,並協助引擎產生一致的解析結果。

Gradle 區分兩種組件

  • 從來源建置的本機組件(例如專案)

  • 發佈到儲存庫的外部組件

對於本機組件,變異會對應到可消耗組態。對於外部組件,變異是由發佈的 Gradle 模組元資料定義,或衍生自 Ivy/Maven 元資料

變異與組態

由於歷史原因,變異和組態有時在文件、DSL 或 API 中可以互換使用。

所有組件都提供變異,這些變異可能由可消耗組態支援。並非所有組態都是變異,因為它們可能用於宣告或解析相依性。

變異屬性

屬性是消費者(對於可解析組態)和生產者(對於每個變異)定義的類型安全鍵值對。

消費者可以定義任意數量的屬性。每個屬性有助於縮小可選的變異範圍。屬性值不需要完全相符。

變異也可以定義任意數量的屬性。這些屬性應描述變異的預期用途。例如,Gradle 使用一個名為 org.gradle.usage 的屬性來描述消費者如何使用組件(編譯、執行階段執行等)。變異擁有的屬性通常會多於消費者選擇變異時所提供的屬性。

變異屬性配對

關於生產者變異

變體名稱主要用於除錯目的和錯誤訊息。名稱不會參與變體比對,只有其屬性會參與。

元件可以定義的變體數量沒有限制。通常,元件至少有一個實作變體,但也可以公開測試固定裝置、文件或原始程式碼。元件也可以針對不同的使用者公開不同的變體,以供相同用途。例如,在編譯時,元件可以針對 Linux、Windows 和 macOS 使用不同的標頭。

Gradle 透過比對使用者要求的屬性與生產者定義的屬性,來執行變體感知選取選取演算法會在其他章節中詳細說明。

此規則有兩個例外,會繞過變體感知解析

  • 當生產者沒有變體時,會選取預設人工製品。

  • 當使用者明確依名稱選取組態時,會選取組態的人工製品。

一個簡單的範例

我們來考慮一個範例,其中使用者嘗試使用某個函式庫進行編譯。

首先,使用者需要說明它將如何使用相依解析的結果。這會透過在使用者的可解析組態上設定屬性來完成。

使用者想要解析與下列內容相符的變體:org.gradle.usage=java-api

其次,生產者需要公開元件的不同變體。

生產者元件公開 2 個變體

  • 其 API(命名為 apiElements),屬性為 org.gradle.usage=java-api

  • 其執行時間(命名為 runtimeElements),屬性為 org.gradle.usage=java-runtime

最後,Gradle 會透過查看變體屬性來選取適當的變體

  • 使用者想要一個屬性為 org.gradle.usage=java-api 的變體

  • 生產者有一個相符的變體(apiElements

  • 生產者有一個不相符的變體(runtimeElements

Gradle 會將 apiElements 變體的人工製品和相依項提供給使用者。

一個更複雜的範例

在真實世界中,使用者和生產者會有多個屬性。

Gradle 中的 Java 函式庫專案將包含幾個不同的屬性

  • org.gradle.usage 描述變體的使用方式

  • org.gradle.dependency.bundling 描述變體如何處理依賴項(shadow jar 與 fat jar 與一般 jar)

  • org.gradle.libraryelements 描述變體的封裝(類別或 jar)

  • org.gradle.jvm.version 描述此變體所針對的 Java 的最低版本

  • org.gradle.jvm.environment 描述此變體所針對的 JVM 類型

讓我們考慮一個範例,其中使用者想要在 Java 8 上執行測試,而生產者支援兩個不同的 Java 版本(Java 8 和 Java 11)。

首先,使用者需要說明它需要的 Java 版本。

使用者想要解析一個變體,這個變體

  • 可以在執行時期使用(具有 org.gradle.usage=java-runtime

  • 可以在至少 Java 8 上執行(org.gradle.jvm.version=8

其次,生產者需要公開元件的不同變體。

就像在簡單的範例中,同時存在 API(編譯)和執行時期變體。這些變體同時存在於元件的 Java 8 和 Java 11 版本中。

  • 其針對 Java 8 使用者的 API(稱為 apiJava8Elements),具有屬性 org.gradle.usage=java-apiorg.gradle.jvm.version=8

  • 其針對 Java 8 使用者的執行時期(稱為 runtime8Elements),具有屬性 org.gradle.usage=java-runtimeorg.gradle.jvm.version=8

  • 其針對 Java 11 使用者的 API(稱為 apiJava11Elements),具有屬性 org.gradle.usage=java-apiorg.gradle.jvm.version=11

  • 其針對 Java 11 使用者的執行時期(稱為 runtime11Elements),具有屬性 org.gradle.usage=java-runtimeorg.gradle.jvm.version=11

最後,Gradle 透過檢視所有屬性來選取最佳的相符變體

  • 使用者想要一個具有與 org.gradle.usage=java-runtimeorg.gradle.jvm.version=8 相容屬性的變體

  • 變體 runtime8Elementsruntime11Elements 具有 `org.gradle.usage=java-runtime

  • 變體 apiJava8ElementsapiJava11Elements 不相容

  • 變體 runtime8Elements 相容,因為它可以在 Java 8 上執行

  • 變體 runtime11Elements 不相容,因為它無法在 Java 8 上執行

Gradle 提供 runtime8Elements 變體的成品和依賴項給使用者。

變體相容性

如果使用者將 org.gradle.jvm.version 設定為 7 會如何?

依賴項解析會失敗,並顯示錯誤訊息,說明沒有合適的變體。Gradle 會辨識使用者想要相容於 Java 7 的函式庫,而生產者提供的 Java 最版本是 8。

如果使用者要求 org.gradle.jvm.version=15,Gradle 會知道 Java 8 或 Java 11 變體都可以使用。Gradle 會選取相容性最高的 Java 版本 (11)。

變體選取錯誤

在選取元件最相容的變體時,解析可能會失敗

  • 當生產者有多個變體符合使用者的屬性時 (模稜兩可錯誤)

  • 當生產者沒有任何變體符合使用者的屬性時 (不相容錯誤)

處理模稜兩可錯誤

模稜兩可的變體選取看起來像這樣

> Could not resolve all files for configuration ':compileClasspath'.
   > Could not resolve project :lib.
     Required by:
         project :ui
      > Cannot choose between the following variants of project :lib:
          - feature1ApiElements
          - feature2ApiElements
        All of them match the consumer attributes:
          - Variant 'feature1ApiElements' capability org.test:test-capability:1.0:
              - Unmatched attribute:
                  - Found org.gradle.category 'library' but wasn't required.
              - Compatible attributes:
                  - Provides org.gradle.dependency.bundling 'external'
                  - Provides org.gradle.jvm.version '11'
                  - Required org.gradle.libraryelements 'classes' and found value 'jar'.
                  - Provides org.gradle.usage 'java-api'
          - Variant 'feature2ApiElements' capability org.test:test-capability:1.0:
              - Unmatched attribute:
                  - Found org.gradle.category 'library' but wasn't required.
              - Compatible attributes:
                  - Provides org.gradle.dependency.bundling 'external'
                  - Provides org.gradle.jvm.version '11'
                  - Required org.gradle.libraryelements 'classes' and found value 'jar'.
                  - Provides org.gradle.usage 'java-api'

所有相容的候選變體都會顯示其屬性。

  • 不匹配的屬性會先顯示,因為它們可能是選取適當變體時遺漏的部分。

  • 相容的屬性會其次顯示,因為它們表示使用者想要什麼,以及這些變體如何符合該要求。

  • 不會有任何不相容的屬性,因為變體不會被視為候選變體。

在上面的範例中,修正方法不在於屬性匹配,而在於功能匹配,顯示在變體名稱旁邊。由於這兩個變體實際上提供相同的屬性和功能,因此無法消除模稜兩可。因此,在這種情況下,修正方法很可能是提供生產者端 (project :lib) 的不同功能,並在使用者端 (project :ui) 表達功能選擇。

處理沒有匹配變體的錯誤

沒有匹配變體的錯誤看起來像這樣

> No variants of project :lib match the consumer attributes:
  - Configuration ':lib:compile':
      - Incompatible attribute:
          - Required artifactType 'dll' and found incompatible value 'jar'.
      - Other compatible attribute:
          - Provides usage 'api'
  - Configuration ':lib:compile' variant debug:
      - Incompatible attribute:
          - Required artifactType 'dll' and found incompatible value 'jar'.
      - Other compatible attributes:
          - Found buildType 'debug' but wasn't required.
          - Provides usage 'api'
  - Configuration ':lib:compile' variant release:
      - Incompatible attribute:
          - Required artifactType 'dll' and found incompatible value 'jar'.
      - Other compatible attributes:
          - Found buildType 'release' but wasn't required.
          - Provides usage 'api'

或像這樣

> No variants of project : match the consumer attributes:
   - Configuration ':myElements' declares attribute 'color' with value 'blue':
       - Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'
   - Configuration ':myElements' variant secondary declares attribute 'color' with value 'blue':
       - Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'

取決於錯誤發生在變體選取演算法的哪個階段。

所有可能相容的候選變體都會顯示其屬性。

  • 不相容的屬性會先顯示,因為它們通常是了解為什麼無法選取變體的關鍵。

  • 其他屬性會其次顯示,這包括要求的相容的屬性,以及使用者未要求的所有額外生產者屬性。

與模稜兩可的變體錯誤類似,目標是了解應該選取哪個變體。在某些情況下,生產者可能沒有任何相容的變體 (例如,嘗試使用為 Java 11 建置的函式庫在 Java 8 上執行)。

處理不相容變體錯誤

不相容變體錯誤看起來像以下範例,使用者想要選取具有 color=green 的變體,但唯一可用的變體具有 color=blue

> Could not resolve all task dependencies for configuration ':resolveMe'.
   > Could not resolve project :.
     Required by:
         project :
      > Configuration 'mismatch' in project : does not match the consumer attributes
        Configuration 'mismatch':
          - Incompatible because this component declares attribute 'color' with value 'blue' and the consumer needed attribute 'color' with value 'green'

當 Gradle 無法選擇相依項的單一變體,因為明確要求的屬性值與相依項的任何變體上該屬性的值不符(且不相容)時,就會發生此情況。

此失敗的子類型發生於 Gradle 成功選擇同一個元件的多個變體,但所選的變體彼此不相容時。

這看起來像下列情況,其中消費者想要選擇元件的兩個不同變體,每個變體提供不同的功能,這是可以接受的。不幸的是,一個變體具有 color=blue,另一個變體具有 color=green

> Could not resolve all task dependencies for configuration ':resolveMe'.
   > Could not resolve project :.
     Required by:
         project :
      > Multiple incompatible variants of org.example:nyvu:1.0 were selected:
           - Variant org.example:nyvu:1.0 variant blueElementsCapability1 has attributes {color=blue}
           - Variant org.example:nyvu:1.0 variant greenElementsCapability2 has attributes {color=green}

   > Could not resolve project :.
     Required by:
         project :
      > Multiple incompatible variants of org.example:pi2e5:1.0 were selected:
           - Variant org.example:pi2e5:1.0 variant blueElementsCapability1 has attributes {color=blue}
           - Variant org.example:pi2e5:1.0 variant greenElementsCapability2 has attributes {color=green}

處理模稜兩可的轉換錯誤

ArtifactTransforms 可用於將一種類型的成品轉換為另一種類型,變更其屬性。變體選擇可以使用成品轉換結果中可用的屬性作為候選變體。

如果專案註冊多個成品轉換,需要使用成品轉換來產生與消費者要求相符的變體,且多個成品轉換都可以用於達成此目的,那麼 Gradle 會失敗,並產生類似下列的模稜兩可轉換錯誤

> Could not resolve all task dependencies for configuration ':resolveMe'.
   > Found multiple transforms that can produce a variant of project : with requested attributes:
       - color 'red'
       - shape 'round'
     Found the following transforms:
       - From 'configuration ':roundBlueLiquidElements'':
           - With source attributes:
               - color 'blue'
               - shape 'round'
               - state 'liquid'
           - Candidate transform(s):
               - Transform 'BrokenTransform' producing attributes:
                   - color 'red'
                   - shape 'round'
                   - state 'gas'
               - Transform 'BrokenTransform' producing attributes:
                   - color 'red'
                   - shape 'round'
                   - state 'solid'

視覺化變體資訊

傳出變體報告

報告任務 outgoingVariants 顯示專案消費者可選擇的變體清單。它顯示每個變體的功能、屬性和成品。

此任務類似於 dependencyInsight 報告任務

預設情況下,outgoingVariants 會列印所有變體的資訊。它提供選用參數 --variant <variantName> 來選擇單一變體顯示。它也接受 --all 旗標來納入舊版和已棄用的組態資訊,或 --no-all 來排除此資訊。

以下是 outgoingVariants 任務在新產生的 java-library 專案上的輸出

> Task :outgoingVariants
--------------------------------------------------
Variant apiElements
--------------------------------------------------
API elements for the 'main' feature.

Capabilities
    - new-java-library:lib:unspecified (default capability)
Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-api
Artifacts
    - build/libs/lib.jar (artifactType = jar)

Secondary Variants (*)

    --------------------------------------------------
    Secondary Variant classes
    --------------------------------------------------
        Description = Directories containing compiled class files for main.

        Attributes
            - org.gradle.category            = library
            - org.gradle.dependency.bundling = external
            - org.gradle.jvm.version         = 11
            - org.gradle.libraryelements     = classes
            - org.gradle.usage               = java-api
        Artifacts
            - build/classes/java/main (artifactType = java-classes-directory)

--------------------------------------------------
Variant mainSourceElements (i)
--------------------------------------------------
Description = List of source directories contained in the Main SourceSet.

Capabilities
    - new-java-library:lib:unspecified (default capability)
Attributes
    - org.gradle.category            = verification
    - org.gradle.dependency.bundling = external
    - org.gradle.verificationtype    = main-sources
Artifacts
    - src/main/java (artifactType = directory)
    - src/main/resources (artifactType = directory)

--------------------------------------------------
Variant runtimeElements
--------------------------------------------------
Runtime elements for the 'main' feature.

Capabilities
    - new-java-library:lib:unspecified (default capability)
Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-runtime
Artifacts
    - build/libs/lib.jar (artifactType = jar)

Secondary Variants (*)

    --------------------------------------------------
    Secondary Variant classes
    --------------------------------------------------
        Description = Directories containing compiled class files for main.

        Attributes
            - org.gradle.category            = library
            - org.gradle.dependency.bundling = external
            - org.gradle.jvm.version         = 11
            - org.gradle.libraryelements     = classes
            - org.gradle.usage               = java-runtime
        Artifacts
            - build/classes/java/main (artifactType = java-classes-directory)

    --------------------------------------------------
    Secondary Variant resources
    --------------------------------------------------
        Description = Directories containing the project's assembled resource files for use at runtime.

        Attributes
            - org.gradle.category            = library
            - org.gradle.dependency.bundling = external
            - org.gradle.jvm.version         = 11
            - org.gradle.libraryelements     = resources
            - org.gradle.usage               = java-runtime
        Artifacts
            - build/resources/main (artifactType = java-resources-directory)

--------------------------------------------------
Variant testResultsElementsForTest (i)
--------------------------------------------------
Description = Directory containing binary results of running tests for the test Test Suite's test target.

Capabilities
    - new-java-library:lib:unspecified (default capability)
Attributes
    - org.gradle.category              = verification
    - org.gradle.testsuite.name        = test
    - org.gradle.testsuite.target.name = test
    - org.gradle.testsuite.type        = unit-test
    - org.gradle.verificationtype      = test-results
Artifacts
    - build/test-results/test/binary (artifactType = directory)

(i) Configuration uses incubating attributes such as Category.VERIFICATION.
(*) Secondary variants are variants created via the Configuration#getOutgoing(): ConfigurationPublications API which also participate in selection, in addition to the configuration itself.

從這裡您可以看到 java 函式庫公開的兩個主要變體,apiElementsruntimeElements。請注意,主要差異在於 org.gradle.usage 屬性,其值為 java-apijava-runtime。正如它們所指示的,這是區分消費者 編譯 類別路徑上需要什麼與 執行時期 類別路徑上需要什麼的地方。

它還顯示了 次要 變體,這些變體專屬於 Gradle 專案,不會發布。例如,apiElements 中的次要變體 classes 允許 Gradle 在針對 java-library 專案 編譯時略過 JAR 建立。

有關無效可消耗組態的資訊

專案不能有多個具有相同屬性和功能的組態。在這種情況下,專案將無法建置。

為了能夠視覺化這些問題,傳出變體報告會以寬鬆的方式處理這些錯誤。這允許報告顯示有關問題的資訊。

可解析組態報告

Gradle 還提供了一個稱為 resolvableConfigurations 的補充報告任務,它顯示專案的 可解析 組態,這些組態可以新增相依性並加以解析。報告將列出它們的屬性以及它們延伸的任何組態。它還將列出在解析期間將受 相容性規則消除歧義規則 影響的任何屬性摘要。

預設情況下,resolvableConfigurations 會列印有關所有純可解析組態的資訊。這些組態標記為可解析,但標記為可消耗。儘管某些可解析組態也標記為可消耗,但這些是舊組態,不應該在建置指令碼中新增相依性。此報告提供選用參數 --configuration <configurationName> 來選取要顯示的單一組態。它也接受 --all 旗標來包含有關舊版和已棄用組態的資訊,或 --no-all 來排除此資訊。最後,它接受 --recursive 旗標,在延伸組態區段中列出以遞迴方式而非直接方式延伸的那些組態。或者,可以使用 --no-recursive 來排除此資訊。

以下是 resolvableConfigurations 任務在新產生的 java-library 專案上的輸出

> Task :resolvableConfigurations
--------------------------------------------------
Configuration annotationProcessor
--------------------------------------------------
Description = Annotation processors and their dependencies for source set 'main'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-runtime

--------------------------------------------------
Configuration compileClasspath
--------------------------------------------------
Description = Compile classpath for source set 'main'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = classes
    - org.gradle.usage               = java-api
Extended Configurations
    - compileOnly
    - implementation

--------------------------------------------------
Configuration runtimeClasspath
--------------------------------------------------
Description = Runtime classpath of source set 'main'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-runtime
Extended Configurations
    - implementation
    - runtimeOnly

--------------------------------------------------
Configuration testAnnotationProcessor
--------------------------------------------------
Description = Annotation processors and their dependencies for source set 'test'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-runtime

--------------------------------------------------
Configuration testCompileClasspath
--------------------------------------------------
Description = Compile classpath for source set 'test'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = classes
    - org.gradle.usage               = java-api
Extended Configurations
    - testCompileOnly
    - testImplementation

--------------------------------------------------
Configuration testRuntimeClasspath
--------------------------------------------------
Description = Runtime classpath of source set 'test'.

Attributes
    - org.gradle.category            = library
    - org.gradle.dependency.bundling = external
    - org.gradle.jvm.environment     = standard-jvm
    - org.gradle.jvm.version         = 11
    - org.gradle.libraryelements     = jar
    - org.gradle.usage               = java-runtime
Extended Configurations
    - testImplementation
    - testRuntimeOnly

--------------------------------------------------
Compatibility Rules
--------------------------------------------------
Description = The following Attributes have compatibility rules defined.

    - org.gradle.dependency.bundling
    - org.gradle.jvm.environment
    - org.gradle.jvm.version
    - org.gradle.libraryelements
    - org.gradle.plugin.api-version
    - org.gradle.usage

--------------------------------------------------
Disambiguation Rules
--------------------------------------------------
Description = The following Attributes have disambiguation rules defined.

    - org.gradle.category
    - org.gradle.dependency.bundling
    - org.gradle.jvm.environment
    - org.gradle.jvm.version
    - org.gradle.libraryelements
    - org.gradle.plugin.api-version
    - org.gradle.usage

從這裡您可以看到用於解析相依性的兩個主要組態,compileClasspathruntimeClasspath,以及它們對應的測試組態。

從 Maven/Ivy 對應到 Gradle 變體

Maven 和 Ivy 皆沒有變體的概念,這僅由 Gradle 模組元資料原生支援。Gradle 仍可透過使用不同的變體衍生策略,與 Maven 和 Ivy 搭配使用。

與 Gradle 模組元資料的關係

Gradle 模組元資料是針對在 Maven、Ivy 和其他類型儲存庫上發佈的模組的元資料格式。它類似於 pom.xmlivy.xml 元資料檔案,但此格式包含變體的詳細資料。

請參閱 Gradle 模組元資料規格 以取得更多資訊。

將 Maven POM 元資料對應到變體

在 Maven 儲存庫上發佈的模組會自動轉換成支援變體的模組。

Gradle 無法得知發佈的是哪種類型的元件

  • 代表 Gradle 平台的 BOM

  • 用作超級 POM 的 BOM

  • 同時是平台函式庫的 POM

Gradle 中 Java 專案所使用的預設策略是衍生 8 個不同的變體

  • 兩個「函式庫」變體(屬性 org.gradle.category = library

    • compile 變體對應 <scope>compile</scope> 相依性。此變體等同於 Java 函式庫外掛程式apiElements 變體。此範圍的所有相依性都被視為API 相依性

    • runtime 變體對應 <scope>compile</scope><scope>runtime</scope> 相依性。此變體等同於 Java 函式庫外掛程式runtimeElements 變體。這些範圍的所有相依性都被視為執行時期相依性

      • 在兩種情況下,<dependencyManagement> 相依性不會轉換為約束

  • 代表元件的來源 jar 的「來源」變體

  • 代表元件的 javadoc jar 的「javadoc」變體

  • <dependencyManagement> 區塊衍生的四個「平台」變體(屬性 org.gradle.category = platform

    • platform-compile 變體將 <scope>compile</scope> 相依性管理相依性對應為相依性約束

    • platform-runtime 變體將 <scope>compile</scope><scope>runtime</scope> 相依性管理相依性對應為相依性約束

    • enforced-platform-compile 類似於 platform-compile,但所有約束都是強制

    • enforced-platform-runtime 類似於 platform-runtime,但所有約束都是強制

您可以透過查看手冊的導入 BOM區段,進一步了解平台和強制平台變異的使用方式。預設情況下,每當您宣告對 Maven 模組的依賴時,Gradle 都會尋找library變異。然而,透過使用platformenforcedPlatform關鍵字,Gradle 現在會尋找其中一個「平台」變異,這讓您可以導入 POM 檔案的約束,而不是依賴項。

Ivy 檔案對變異的對應

Gradle 沒有針對 Ivy 檔案實作內建的衍生策略。Ivy 是一種彈性的格式,讓您可以發佈任意檔案,而且可以高度自訂。

如果您想針對 Ivy 的編譯執行時期變異實作衍生策略,您可以使用元件資料規則來執行。元件資料規則 API 讓您可以存取 Ivy 設定檔,並根據它們建立變異。如果您知道您使用的所有 Ivy 模組都是使用 Gradle 發佈的,而且沒有進一步自訂ivy.xml檔案,您可以將下列規則新增到您的建置中

build.gradle.kts
abstract class IvyVariantDerivationRule @Inject internal constructor(objectFactory: ObjectFactory) : ComponentMetadataRule {
    private val jarLibraryElements: LibraryElements
    private val libraryCategory: Category
    private val javaRuntimeUsage: Usage
    private val javaApiUsage: Usage

    init {
        jarLibraryElements = objectFactory.named(LibraryElements.JAR)
        libraryCategory = objectFactory.named(Category.LIBRARY)
        javaRuntimeUsage = objectFactory.named(Usage.JAVA_RUNTIME)
        javaApiUsage = objectFactory.named(Usage.JAVA_API)
    }

    override fun execute(context: ComponentMetadataContext) {
        // This filters out any non Ivy module
        if(context.getDescriptor(IvyModuleDescriptor::class) == null) {
            return
        }

        context.details.addVariant("runtimeElements", "default") {
            attributes {
                attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
                attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
                attribute(Usage.USAGE_ATTRIBUTE, javaRuntimeUsage)
            }
        }
        context.details.addVariant("apiElements", "compile") {
            attributes {
                attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
                attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
                attribute(Usage.USAGE_ATTRIBUTE, javaApiUsage)
            }
        }
    }
}

dependencies {
    components { all<IvyVariantDerivationRule>() }
}
build.gradle
abstract class IvyVariantDerivationRule implements ComponentMetadataRule {
    final LibraryElements jarLibraryElements
    final Category libraryCategory
    final Usage javaRuntimeUsage
    final Usage javaApiUsage

    @Inject
    IvyVariantDerivationRule(ObjectFactory objectFactory) {
        jarLibraryElements = objectFactory.named(LibraryElements, LibraryElements.JAR)
        libraryCategory = objectFactory.named(Category, Category.LIBRARY)
        javaRuntimeUsage = objectFactory.named(Usage, Usage.JAVA_RUNTIME)
        javaApiUsage = objectFactory.named(Usage, Usage.JAVA_API)
    }

    void execute(ComponentMetadataContext context) {
        // This filters out any non Ivy module
        if(context.getDescriptor(IvyModuleDescriptor) == null) {
            return
        }

        context.details.addVariant("runtimeElements", "default") {
            attributes {
                attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
                attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
                attribute(Usage.USAGE_ATTRIBUTE, javaRuntimeUsage)
            }
        }
        context.details.addVariant("apiElements", "compile") {
            attributes {
                attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
                attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
                attribute(Usage.USAGE_ATTRIBUTE, javaApiUsage)
            }
        }
    }
}

dependencies {
    components { all(IvyVariantDerivationRule) }
}

此規則會根據每個 ivy 模組的compile設定檔建立一個apiElements變異,並根據default設定檔建立一個runtimeElements變異。對於每個變異,它會設定對應的Java 生態系統屬性。變異的依賴項和人工製品會從基礎設定檔中取得。如果並非所有使用的 Ivy 模組都遵循此模式,則可以調整規則或只將其套用至選定的模組組。

對於所有沒有變異的 Ivy 模組,Gradle 有備用選擇方法。Gradle 不會執行變異感知解析,而是選擇default設定檔或明確命名的設定檔。