變體與屬性
變體代表組件的不同版本或方面,例如 api
與 implementation
。屬性定義根據消費者需求選擇哪個變體。
例如,一個函式庫可能具有 api
和 implementation
變體。在此,消費者想要外部 implementation
變體
configurations {
implementation {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
}
}
}
例如,一個建置可能具有 debug
和 release
變體。這會根據屬性選擇 debug
變體。
configurations {
compileClasspath {
attributes {
attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
}
}
}
屬性協助 Gradle 透過比較請求的屬性與可用的屬性來匹配正確的變體
attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
這將 TargetConfiguration.TARGET_ATTRIBUTE
設定為 "debug"
,表示 Gradle 將嘗試解析具有 "debug" 變體的相依性,而不是其他可用的變體(例如 "release")。
Gradle 定義的標準屬性
作為 Gradle 的使用者,屬性通常作為實作細節隱藏。但理解 Gradle 及其核心外掛定義的標準屬性可能很有用。
作為外掛作者,這些屬性以及定義它們的方式,可以作為在您的生態系統外掛中建立您自己的屬性集的基礎。
與生態系統無關的標準屬性
屬性名稱 | 描述 | 值 | 相容性和消除歧義規則 |
---|---|---|---|
指示變體的主要用途 |
從 Usage 中定義的常數建置的 |
遵循生態系統語義(例如, |
|
指示此軟體組件的類別 |
從 Category 中定義的常數建置的 |
遵循生態系統語義(例如, |
|
指示 |
從 LibraryElements 中定義的常數建置的 |
遵循生態系統語義(例如,在 JVM 世界中, |
|
指示 |
從 DocsType 中定義的常數建置的 |
無預設值,不相容 |
|
指示如何存取變體的相依性。 |
從 Bundling 中定義的常數建置的 |
遵循生態系統語義(例如,在 JVM 世界中, |
|
指示哪種驗證任務產生此輸出。 |
從 VerificationType 中定義的常數建置的 |
無預設值,不相容 |
當 Category
屬性在變體上以孵化值 org.gradle.category=verification
存在時,該變體被視為僅限於驗證時期的變體。
這些變體旨在僅包含執行驗證任務的結果,例如測試結果或程式碼覆蓋率報告。它們是不可發布的,如果添加到已發布的組件中,將產生錯誤。
屬性名稱 | 描述 | 值 | 相容性和消除歧義規則 |
---|---|---|---|
|
組件級別屬性,衍生 |
基於 狀態方案,預設方案基於來源儲存庫。 |
基於使用的方案 |
JVM 生態系統特定屬性
除了上面定義的與生態系統無關的屬性之外,JVM 生態系統還添加了以下屬性
屬性名稱 | 描述 | 值 | 相容性和消除歧義規則 |
---|---|---|---|
指示 JVM 版本相容性。 |
整數,Java 1.4 及更早版本使用 |
預設為 Gradle 使用的 JVM 版本,較低版本與較高版本相容,優先選擇最高相容版本。 |
|
指示變體針對特定 JVM 環境進行了最佳化。 |
常見值為 |
如果有多個變體可用,則該屬性用於優先選擇一個變體,但通常所有值都相容。預設值為 |
|
指示產生此輸出的 TestSuite 的名稱。 |
值為 Suite 的名稱。 |
無預設值,不相容 |
JVM 生態系統還包含許多關於不同屬性的相容性和消除歧義規則。有興趣了解更多的讀者可以查看 org.gradle.api.internal.artifacts.JavaEcosystemSupport
的程式碼。
原生生態系統特定屬性
除了上面定義的與生態系統無關的屬性之外,原生生態系統還添加了以下屬性
屬性名稱 | 描述 | 值 | 相容性和消除歧義規則 |
---|---|---|---|
指示二進制檔案是否使用偵錯符號建置 |
布林值 |
不適用 |
|
指示二進制檔案是否使用最佳化標誌建置 |
布林值 |
不適用 |
|
指示二進制檔案的目標架構 |
從 MachineArchitecture 中定義的常數建置的 |
無 |
|
指示二進制檔案的目標作業系統 |
從 OperatingSystemFamily 中定義的常數建置的 |
無 |
Gradle 外掛生態系統特定屬性
對於 Gradle 外掛開發,自 Gradle 7.0 起支援以下屬性。Gradle 外掛變體可以透過此屬性指定與 Gradle API 版本的相容性。
屬性名稱 | 描述 | 值 | 相容性和消除歧義規則 |
---|---|---|---|
指示 Gradle API 版本相容性。 |
有效的 Gradle 版本字串。 |
預設為目前執行的 Gradle,較低版本與較高版本相容,優先選擇最高相容版本。 |
使用標準屬性
對於此範例,假設您正在建立一個函式庫,其中針對不同的 JVM 版本具有不同的變體。
plugins {
id("java-library")
}
configurations {
named("apiElements") {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
plugins {
id 'java-library'
}
configurations {
apiElements {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
在消費者專案(使用該函式庫的專案)中,您可以在宣告相依性時指定 JVM 版本屬性。
plugins {
id("application")
}
dependencies {
implementation(project(":lib")) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
plugins {
id 'application'
}
dependencies {
implementation(project(':lib')) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
透過定義和使用 JVM 版本屬性,您可以確保您的函式庫及其消費者與指定的 JVM 版本相容。從本質上講,這確保 Gradle 解析為與所需 JVM 版本匹配的變體。
檢視與偵錯屬性
dependencyInsight
任務對於檢查特定相依性及其屬性(包括它們的解析方式)非常有用
$ ./gradlew dependencyInsight --configuration compileClasspath --dependency com.example:your-library
> Task :dependencyInsight
com.example:your-library:1.0 (compileClasspath)
variant "apiElements" [
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-api]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-runtime]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.JavaLanguageVersion = [1.8]
]
variant "runtimeElements" [
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-runtime]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.JavaLanguageVersion = [1.8]
]
Selection reasons:
- By constraint from configuration ':compileClasspath'
- Declared in build.gradle.kts
Resolved to:
com.example:your-library:1.0 (runtime)
Additional Information:
- Dependency declared in the 'implementation' configuration
- No matching variants found for the requested attributes in the 'compileClasspath' configuration
宣告自訂屬性
當使用自訂屬性擴展 Gradle 時,務必考慮其長期影響,尤其是在您計劃發布函式庫時。自訂屬性允許您在您的外掛中整合變體感知相依性管理,但使用這些屬性的函式庫也必須確保消費者可以正確解釋它們。這通常透過應用相應的外掛來完成,該外掛定義了相容性和消除歧義規則。
如果您的外掛是公開可用的,並且函式庫發布到公共儲存庫,則引入新屬性將成為一項重大責任。已發布的屬性必須保持支援,或在外掛的未來版本中具有相容性層,以確保向後相容性。
以下是在 Gradle 外掛中宣告和使用自訂屬性的範例
// Define a custom attribute
val myAttribute = Attribute.of("com.example.my-attribute", String::class.java)
configurations {
create("myConfig") {
// Set custom attribute
attributes {
attribute(myAttribute, "special-value")
}
}
}
dependencies {
// Apply the custom attribute to a dependency
add("myConfig","com.google.guava:guava:31.1-jre") {
attributes {
attribute(myAttribute, "special-value")
}
}
}
// Define a custom attribute
def myAttribute = Attribute.of("com.example.my-attribute", String)
// Create a custom configuration
configurations {
create("myConfig") {
// Set custom attribute
attributes {
attribute(myAttribute, "special-value")
}
}
}
dependencies {
// Apply the custom attribute to a dependency
add("myConfig", "com.google.guava:guava:31.1-jre") {
attributes {
attribute(myAttribute, "special-value")
}
}
}
在此範例中: - 定義了自訂屬性 my-attribute
。 - 該屬性在自訂配置 (myConfig
) 上設定。 - 新增相依性時,會應用自訂屬性以匹配配置。
如果發布具有此屬性的函式庫,請確保消費者應用理解和處理 my-attribute
的外掛。
在建置腳本或外掛中建立屬性
屬性是類型化的。可以透過 Attribute<T>.of
方法建立屬性
// An attribute of type `String`
val myAttribute = Attribute.of("my.attribute.name", String::class.java)
// An attribute of type `Usage`
val myUsage = Attribute.of("my.usage.attribute", Usage::class.java)
// An attribute of type `String`
def myAttribute = Attribute.of("my.attribute.name", String)
// An attribute of type `Usage`
def myUsage = Attribute.of("my.usage.attribute", Usage)
屬性類型支援大多數 Java 原始類別;例如 String
和 Integer
。或任何擴展 org.gradle.api.Named
的類別。
屬性應始終在 dependencies
處理器上的屬性架構中宣告
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
為了使用相容性和消除歧義規則(可以解決屬性匹配期間多個可選變體之間的歧義),需要向架構註冊屬性。
每個配置都有一個屬性容器。可以配置屬性以設定值
configurations {
create("myConfiguration") {
attributes {
attribute(myAttribute, "my-value")
}
}
}
configurations {
myConfiguration {
attributes {
attribute(myAttribute, 'my-value')
}
}
}
對於類型擴展 Named
的屬性,屬性的值必須透過物件工廠建立
configurations {
"myConfiguration" {
attributes {
attribute(myUsage, project.objects.named(Usage::class.java, "my-value"))
}
}
}
configurations {
myConfiguration {
attributes {
attribute(myUsage, project.objects.named(Usage, 'my-value'))
}
}
}
處理屬性匹配
在 Gradle 中,屬性匹配和屬性消除歧義是用於解析具有不同屬性的相依性的關鍵機制。
屬性匹配允許 Gradle 根據預定義的規則選擇相容的相依性變體,即使沒有完全匹配的變體可用。另一方面,當存在多個相容選項時,屬性消除歧義有助於 Gradle 選擇最合適的變體。
屬性相容性規則
屬性讓引擎選擇相容的變體。在某些情況下,生產者可能沒有消費者完全要求的東西,但有一個可以使用的變體。
此範例定義並註冊自訂相容性規則,以確保根據相依性與特定 Java 版本的相容性來選擇相依性
// Define the compatibility rule class
class TargetJvmVersionCompatibilityRule : AttributeCompatibilityRule<Int> {
// Implement the execute method which will check compatibility
override fun execute(details: CompatibilityCheckDetails<Int>) {
// Switch case to check the consumer value for supported Java versions
when (details.consumerValue) {
8, 11 -> details.compatible() // Compatible with Java 8 and 11
else -> details.incompatible()
}
}
}
// Register the compatibility rule within the dependencies block
dependencies {
attributesSchema {
// Add the compatibility rule for the TargetJvmVersion attribute
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) {
// Add the defined compatibility rule to this attribute
compatibilityRules.add(TargetJvmVersionCompatibilityRule::class.java)
}
}
}
// Define the compatibility rule
class TargetJvmVersionCompatibilityRule implements AttributeCompatibilityRule<Integer> {
@Override
void execute(CompatibilityCheckDetails<Integer> details) {
switch (details.consumerValue) {
case 8:
case 11:
details.compatible() // Compatible with Java 8 and 11
break
default:
details.incompatible("Unsupported Java version: ${details.consumerValue}")
}
}
}
// Register a compatibility rule
dependencies {
attributesSchema {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) {
compatibilityRules.add(TargetJvmVersionCompatibilityRule)
}
}
}
Gradle 提供了可以為每個屬性定義的 屬性相容性規則。相容性規則的作用是根據消費者的要求解釋哪些屬性值是相容的。
屬性相容性規則必須透過 屬性架構 註冊。
屬性消除歧義規則
當相依性的多個變體與消費者請求的屬性相容時,Gradle 需要決定選擇哪個變體。在相容選項中確定「最佳」候選者的此過程稱為屬性消除歧義。
在 Gradle 中,不同的變體可能滿足消費者的請求,但並非所有變體都相同。例如,您可能有多個與消費者請求的 Java 版本相容的函式庫版本。消除歧義有助於 Gradle 根據其他標準選擇最合適的版本。
您可以定義消除歧義規則,以指導 Gradle 在找到多個候選者時選擇最合適的變體。這可以透過實作 屬性消除歧義規則 來完成
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeMatchingStrategy
// Define custom attribute
val javaLanguageVersion = Attribute.of("org.gradle.jvm.version", String::class.java)
// Register disambiguation rules
configurations.all {
attributes {
// Define the attribute matching strategy
attribute(javaLanguageVersion, "1.8") {
// Set up disambiguation logic
disambiguationStrategy {
// Example disambiguation: Prefer newer versions
preferNewer()
}
}
}
}
-
屬性定義:建立或參考您要應用消除歧義規則的屬性。在這裡,使用
javaLanguageVersion
。 -
註冊消除歧義規則:使用
attributes
區塊中的disambiguationStrategy
應用消除歧義策略。此範例設定了一個簡單的規則來優先選擇較新版本。 -
消除歧義邏輯:
preferNewer()
方法是您的自訂邏輯的預留位置。您可以根據您的需求實作更複雜的規則。
屬性消除歧義規則必須透過您可以從 屬性架構 獲得的 屬性匹配策略 註冊,屬性架構是 DependencyHandler 的成員。
從 Maven/Ivy 映射到 Gradle 變體
Maven 和 Ivy 都沒有變體的概念,變體僅由 Gradle 模組元數據原生支援。Gradle 仍然可以透過使用不同的變體衍生策略來與 Maven 和 Ivy 協同工作。
Gradle 模組元數據是用於在 Maven、Ivy 和其他類型的儲存庫上發布模組的元數據格式。它類似於 pom.xml
或 ivy.xml
元數據檔案,但此格式包含有關變體的詳細資訊。
有關更多資訊,請參閱 {metadata-file-spec}[Gradle 模組元數據規範]。
Maven POM 元數據到變體的映射
發布到 Maven 儲存庫的模組在由 Gradle 解析時會自動轉換為變體感知模組。
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 的「sources」變體
-
代表組件的 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
變體。但是,透過使用 platform
或 enforcedPlatform
關鍵字,Gradle 現在正在尋找其中一個「平台」變體,這允許您從 POM 檔案匯入約束,而不是相依性。
Ivy 檔案到變體的映射
Gradle 沒有為 Ivy 檔案實作內建的衍生策略。Ivy 是一種靈活的格式,允許您發布任意檔案並且可以高度自訂。
如果您想為 Ivy 實作 compile 和 runtime 變體的衍生策略,您可以使用 組件元數據規則。組件元數據規則 API 允許您 存取 Ivy 配置 並根據它們建立變體。如果您知道您正在使用的所有 Ivy 模組都已使用 Gradle 發布,並且沒有進一步自訂 ivy.xml
檔案,您可以將以下規則添加到您的建置中
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>() }
}
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) }
}
該規則基於 compile
配置建立 apiElements
變體,並基於每個 ivy 模組的 default
配置建立 runtimeElements
變體。對於每個變體,它都設定了相應的 Java 生態系統屬性。變體的相依性和成品取自底層配置。如果並非所有使用的 Ivy 模組都遵循此模式,則可以調整規則或僅將其應用於選定的模組集。
對於所有沒有變體的 Ivy 模組,Gradle 都有後備選擇方法。Gradle 不執行變體感知解析,而是選擇 default
配置或顯式命名的配置。