版本目录是精选的依赖项列表,可在构建脚本中引用,从而简化依赖管理。

除了直接使用字符串符号指定依赖项外,您还可以从版本目录中选择它们

build.gradle.kts
dependencies {
    implementation(libs.groovy.core)
}
build.gradle
dependencies {
    implementation(libs.groovy.core)
}

在此示例中,libs 表示目录,而 groovy 是目录中可用的依赖项。

其中定义 libs.groovy.core 的版本目录是 gradle 目录中的 libs.versions.toml 文件

gradle/libs.versions.toml
[libraries]
groovy-core = { group = "org.codehaus.groovy", name = "groovy", version = "3.0.5" }

版本目录提供了一些优点

  • 类型安全的访问器:Gradle 为每个目录生成类型安全的访问器,从而在 IDE 中实现自动完成。

  • 集中式版本管理:构建中的所有项目都可以看到每个目录。

  • 依赖项 Bundle:目录可以将常用依赖项分组到 bundle 中。

  • 版本分离:目录可以将依赖项坐标与版本信息分开,从而允许共享版本声明。

  • 冲突解决:与常规依赖项符号一样,版本目录声明了请求的 版本,但在 冲突解决 过程中不强制执行。

版本目录定义了版本,但它们不影响依赖解析过程。由于依赖图冲突或通过平台或依赖管理 API 应用的约束,Gradle 仍然可能选择不同的版本。

目录中声明的版本通常不强制执行,这意味着构建中使用的实际版本可能会因依赖解析而有所不同。

访问目录

要访问位于 gradle 目录中标准 libs.versions.toml 文件中定义的版本目录中的项,您可以在构建脚本中使用 libs 对象。例如,要引用库,可以使用 libs.<alias>;要引用插件,可以使用 libs.plugins.<alias>

使用版本目录声明依赖项

build.gradle.kts
dependencies {
    implementation(libs.groovy.core)
    implementation(libs.groovy.json)
    implementation(libs.groovy.nio)
}
build.gradle
dependencies {
    implementation libs.groovy.core
    implementation libs.groovy.json
    implementation libs.groovy.nio
}

与之相同的是

build.gradle.kts
dependencies {
    implementation("org.codehaus.groovy:groovy:3.0.5")
    implementation("org.codehaus.groovy:groovy-json:3.0.5")
    implementation("org.codehaus.groovy:groovy-nio:3.0.5")
}
build.gradle
dependencies {
    implementation 'org.codehaus.groovy:groovy:3.0.5'
    implementation 'org.codehaus.groovy:groovy-json:3.0.5'
    implementation 'org.codehaus.groovy:groovy-nio:3.0.5'
}

访问器直接映射到 TOML 文件中定义的别名和版本,提供对依赖项和插件的类型安全访问。这使得 IDE 能够提供自动完成、突出显示拼写错误并将缺失的依赖项标识为错误。

别名和类型安全的访问器

版本目录中的别名由破折号 (-)、下划线 (_) 或点 (.) 分隔的标识符组成。为每个别名生成类型安全的访问器,规范化为点符号

示例别名 生成的访问器

guava

libs.guava

groovy-core

libs.groovy.core

androidx.awesome.lib

libs.androidx.awesome.lib

创建目录

版本目录通常使用位于根构建的 gradle 子目录中的 libs.versions.toml 文件进行声明

gradle/libs.versions.toml
[versions]
groovy = "3.0.5"
checkstyle = "8.37"

[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }

[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]

[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }

TOML 目录格式

The TOML 文件有四个部分

  • [versions] – 声明版本标识符。

  • [libraries] – 将别名映射到 GAV 坐标。

  • [bundles] – 定义依赖项 Bundle。

  • [plugins] – 声明插件版本。

TOML 文件格式非常宽松,允许您将“点式”属性写为完整对象声明的快捷方式。

版本

版本可以声明为单个字符串(在这种情况下,它们被解释为 所需 版本)或 富版本 (rich version)

[versions]
other-lib = "5.5.0" # Required version
my-lib = { strictly = "[1.0, 2.0[", prefer = "1.2" } # Rich version

版本声明支持的成员有

每个库都映射到一个 GAV 坐标:group、artifact、version。它们可以声明为一个简单的字符串,在这种情况下它们被解释为坐标,或者分开声明 group 和 name

[versions]
common = "1.4"

[libraries]
my-lib = "com.mycompany:mylib:1.4"
my-lib-no-version.module = "com.mycompany:mylib"
my-other-lib = { module = "com.mycompany:other", version = "1.4" }
my-other-lib2 = { group = "com.mycompany", name = "alternate", version = "1.4" }
mylib-full-format = { group = "com.mycompany", name = "alternate", version = { require = "1.4" } }

[plugins]
short-notation = "some.plugin.id:1.4"
long-notation = { id = "some.plugin.id", version = "1.4" }
reference-notation = { id = "some.plugin.id", version.ref = "common" }

您还可以使用 strictlyprefer 定义严格或首选版本

[libraries]
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer = "3.9" } }

如果您想引用在 [versions] 部分中声明的版本,请使用 version.ref 属性

[versions]
some = "1.4"

[libraries]
my-lib = { group = "com.mycompany", name="mylib", version.ref="some" }

Bundle

Bundle 将多个库别名分组,以便它们可以在构建脚本中一起引用。

[versions]
groovy = "3.0.9"

[libraries]
groovy-core = { group = "org.codehaus.groovy", name = "groovy", version.ref = "groovy" }
groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version.ref = "groovy" }
groovy-nio = { group = "org.codehaus.groovy", name = "groovy-nio", version.ref = "groovy" }

[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]

这对于使用单个别名引入多个相关依赖项非常有用

build.gradle.kts
dependencies {
    implementation(libs.bundles.groovy)
}
build.gradle
dependencies {
    implementation libs.bundles.groovy
}

插件

本节通过将插件 ID 映射到版本号来定义插件及其版本。就像库一样,您可以使用 [versions] 部分中的别名或直接指定版本来定义插件版本。

[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }

可以使用 plugins {} 块在构建的任何项目中访问它。要引用目录中的插件,请使用 alias() 函数

build.gradle.kts
plugins {
    `java-library`
    checkstyle
    alias(libs.plugins.versions)
}
build.gradle
plugins {
    id 'java-library'
    id 'checkstyle'
    // Use the plugin `versions` as declared in the `libs` version catalog
    alias(libs.plugins.versions)
}
您不能在设置文件或设置插件中使用版本目录中声明的插件。

避免使用子组访问器

为避免生成子组访问器,请使用 camelCase (驼峰命名法)

别名 访问器

groovyCore

libs.groovyCore

groovyJson-core

libs.groovyJson.core

保留关键字

某些关键字,例如 extensionsclassconvention,是保留的,不能用作别名。此外,bundlesversionsplugins 不能作为依赖别名中的第一个子组。

例如,别名 versions-dependency 无效,但 versionsDependencydependency-versions 有效。

发布目录

在大多数情况下,gradle/libs.versions.toml 将被提交到仓库并可供使用。

然而,这并不能总是解决在组织内部或面向外部消费者共享目录的问题。共享目录的另一种选择是编写一个 settings 插件,将其发布到 Gradle 插件门户或内部仓库,并让消费者在其 settings 文件中应用该插件。

另一种方式是,Gradle 提供了一个 版本目录 插件,它具有声明和发布目录的能力。

为此,您需要应用 version-catalog 插件

build.gradle.kts
plugins {
    `version-catalog`
    `maven-publish`
}
build.gradle
plugins {
    id 'version-catalog'
    id 'maven-publish'
}

然后,此插件将暴露 catalog 扩展,您可以使用它来声明目录

build.gradle.kts
catalog {
    // declare the aliases, bundles and versions in this block
    versionCatalog {
        library("my-lib", "com.mycompany:mylib:1.2")
    }
}
build.gradle
catalog {
    // declare the aliases, bundles and versions in this block
    versionCatalog {
        library('my-lib', 'com.mycompany:mylib:1.2')
    }
}

插件必须以编程方式创建,详情请参阅 编程方式创建目录

然后可以通过应用 maven-publishivy-publish 插件并将发布配置为使用 versionCatalog 组件来发布此类目录

build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("maven") {
            from(components["versionCatalog"])
        }
    }
}
build.gradle
publishing {
    publications {
        maven(MavenPublication) {
            from components.versionCatalog
        }
    }
}

发布此类项目时,会自动生成(并上传)一个 libs.versions.toml 文件,然后可以被其他 Gradle 构建消费

导入已发布的目录

版本目录插件 生成的目录可以通过 Settings API 导入

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from("com.mycompany:catalog:1.0")
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from("com.mycompany:catalog:1.0")
        }
    }
}

从文件导入目录

Gradle 会自动导入 gradle 目录中名为 libs.versions.toml 的目录。

The 版本目录构建器 API 允许从外部文件导入目录,从而实现在构建的不同部分之间重用,例如与 buildSrc 共享主构建的目录。

例如,您可以将目录包含在 buildSrc/settings.gradle(.kts) 文件中,如下所示

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

The VersionCatalogBuilder.from(Object dependencyNotation) 方法只接受单个文件,这意味着像 Project.files(java.lang.Object…​) 这样的符号必须引用一个文件。否则,构建将失败。

请记住,如果名为 libs.versions.toml 的版本目录位于您的 gradle 文件夹中,则无需导入它。它将自动导入。

但是,如果您需要从多个文件导入版本目录,建议使用基于代码的方法,而不是依赖 TOML 文件。这种方法允许从不同文件声明多个目录

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        // declares an additional catalog, named 'testLibs', from the 'test-libs.versions.toml' file
        create("testLibs") {
            from(files("gradle/test-libs.versions.toml"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        // declares an additional catalog, named 'testLibs', from the 'test-libs.versions.toml' file
        testLibs {
            from(files('gradle/test-libs.versions.toml'))
        }
    }
}

导入多个目录

您可以使用 Settings API 声明多个目录,以便更好地组织依赖项

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("gradle/libs.versions.toml"))
        }
        create("tools") {
            from(files("gradle/tools.versions.toml"))
        }
    }
}
build.gradle.kts
dependencies {
    implementation(libs.someDependency)
    implementation(tools.someTool)
}

为了最大程度地减少命名冲突的风险,每个目录都会生成一个应用于所有项目的扩展,因此建议选择一个唯一的名称。一个有效的方法是选择一个以 Libs 结尾的名称。

更改目录名称

默认情况下,libs.versions.toml 文件用作 libs 目录的输入。但是,如果已存在同名扩展,您可以重命名默认目录

settings.gradle.kts
dependencyResolutionManagement {
    defaultLibrariesExtensionName = "projectLibs"
}
settings.gradle
dependencyResolutionManagement {
    defaultLibrariesExtensionName = 'projectLibs'
}

覆盖目录版本

导入目录时可以覆盖版本

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("amendedLibs") {
            from("com.mycompany:catalog:1.0")
            // overwrite the "groovy" version declared in the imported catalog
            version("groovy", "3.0.6")
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        amendedLibs {
            from("com.mycompany:catalog:1.0")
            // overwrite the "groovy" version declared in the imported catalog
            version("groovy", "3.0.6")
        }
    }
}

在上面的示例中,任何引用 groovy 版本的依赖项都将自动更新为使用 3.0.6

覆盖版本仅影响声明依赖项时导入和使用的内容。由于冲突解决,实际解析的依赖项版本可能会有所不同。

编程方式创建目录

版本目录可以在 settings.gradle(.kts) 文件中以编程方式声明。

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("groovy", "3.0.5")
            version("checkstyle", "8.37")
            library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
            library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
            library("groovy-nio", "org.codehaus.groovy", "groovy-nio").versionRef("groovy")
            library("commons-lang3", "org.apache.commons", "commons-lang3").version {
                strictly("[3.8, 4.0[")
                prefer("3.9")
            }
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('groovy', '3.0.5')
            version('checkstyle', '8.37')
            library('groovy-core', 'org.codehaus.groovy', 'groovy').versionRef('groovy')
            library('groovy-json', 'org.codehaus.groovy', 'groovy-json').versionRef('groovy')
            library('groovy-nio', 'org.codehaus.groovy', 'groovy-nio').versionRef('groovy')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
        }
    }
}
如果您的项目中有默认的 libs.versions.toml 文件,请不要使用 libs 作为编程方式创建的版本目录名称。

以编程方式创建版本目录使用 Settings API

settings.gradle.kts
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("groovy", "3.0.5")
            version("checkstyle", "8.37")
            library("groovy-core", "org.codehaus.groovy", "groovy").versionRef("groovy")
            library("groovy-json", "org.codehaus.groovy", "groovy-json").versionRef("groovy")
            library("groovy-nio", "org.codehaus.groovy", "groovy-nio").versionRef("groovy")
            library("commons-lang3", "org.apache.commons", "commons-lang3").version {
                strictly("[3.8, 4.0[")
                prefer("3.9")
            }
            bundle("groovy", listOf("groovy-core", "groovy-json", "groovy-nio"))
        }
    }
}
settings.gradle
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('groovy', '3.0.5')
            version('checkstyle', '8.37')
            library('groovy-core', 'org.codehaus.groovy', 'groovy').versionRef('groovy')
            library('groovy-json', 'org.codehaus.groovy', 'groovy-json').versionRef('groovy')
            library('groovy-nio', 'org.codehaus.groovy', 'groovy-nio').versionRef('groovy')
            library('commons-lang3', 'org.apache.commons', 'commons-lang3').version {
                strictly '[3.8, 4.0['
                prefer '3.9'
            }
            bundle('groovy', ['groovy-core', 'groovy-json', 'groovy-nio'])
        }
    }
}

buildSrc 中使用目录

版本目录提供了一种集中管理项目依赖项的方式。然而,buildSrc 不会自动继承主项目的版本目录,因此需要额外的配置。

要使用版本目录,您需要在 buildSrc/settings.gradle(.kts) 中显式导入它

buildSrc/settings.gradle.kts
// Add the version catalog to buildSrc using dependencyResolutionManagement
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}
buildSrc/settings.gradle
// Add the version catalog to buildSrc using dependencyResolutionManagement
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

版本目录导入后,可以使用一些技巧引用依赖项

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
    alias(libs.plugins.versions) // Access version catalog in builSrc build file for plugin
}

repositories {
    gradlePluginPortal()
}

dependencies {
    // Access version catalog in buildSrc build file for dependencies
    implementation(plugin(libs.plugins.jacocolog)) // Plugin dependency
    implementation(libs.groovy.core) // Regular library from version catalog
    implementation("org.apache.commons:commons-lang3:3.9") // Direct dependency
}

// Helper function that transforms a Gradle Plugin alias from a
// Version Catalog into a valid dependency notation for buildSrc
fun DependencyHandlerScope.plugin(plugin: Provider<PluginDependency>) =
    plugin.map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" }
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
    alias(libs.plugins.versions) // Access version catalog in buildSrc for plugin
}

repositories {
    gradlePluginPortal()
}

def catalogs = project.extensions.getByType(VersionCatalogsExtension)
def libs = catalogs.named("libs")

dependencies {
    // Access version catalog in buildSrc for dependencies
    implementation plugin(libs.findPlugin("jacocolog").get()) // Plugin dependency
    implementation libs.findLibrary("groovy-core").get() // Regular library from version catalog
    implementation "org.apache.commons:commons-lang3:3.9" // Direct dependency
}

// Helper function that transforms a Gradle Plugin alias from a
// Version Catalog into a valid dependency notation for buildSrc
def plugin(Provider<PluginDependency> plugin) {
    return plugin.map { it.pluginId + ":" + it.pluginId + ".gradle.plugin:" + it.version }
}

buildSrc 内部的预编译脚本插件中,可以使用 extensions.getByType(VersionCatalogsExtension) 访问版本目录,如本约定插件的依赖项块所示

buildSrc/src/main/kotlin/java-commons-convention.gradle.kts
plugins {
    id("java-library")

    //alias(libs.plugins.jacocolog) // Unfortunately it is not possible the version catalog in buildSrc code for plugins

    // Remember that unlike regular Gradle projects, convention plugins in buildSrc do not automatically resolve
    // external plugins. We must declare them as dependencies in buildSrc/build.gradle.kts.
    id("org.barfuin.gradle.jacocolog") // Apply the plugin manually as a workaround with the external plugin
                                       // version from the version catalog specified in implementation dependency
                                       // artifact in build file
}

repositories {
    mavenCentral()
}

// Access the version catalog
val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs")
//val libs = the<VersionCatalogsExtension>().named("libs")

dependencies {
    // Access version catalog in buildSrc code for dependencies
    implementation(libs.findLibrary("guava").get()) // Regular library from version catalog
    testImplementation(platform("org.junit:junit-bom:5.9.1")) // Platform dependency
    testImplementation("org.junit.jupiter:junit-jupiter") // Direct dependency
}

tasks.test {
    useJUnitPlatform()
}
buildSrc/src/main/groovy/java-commons-convention.gradle
plugins {
    id 'java-library'

    // alias(libs.plugins.jacocolog) - Unfortunately, it is not possible to use the version catalog for plugins in buildSrc

    // Unlike regular Gradle projects, convention plugins in buildSrc do not automatically resolve
    // external plugins. We must declare them as dependencies in buildSrc/build.gradle.
    id 'org.barfuin.gradle.jacocolog' // Apply the plugin manually as a workaround
    // The external plugin version comes from the implementation dependency
    // artifact in the build file
}

repositories {
    mavenCentral()
}

// Access the version catalog
def libs = project.extensions.getByType(VersionCatalogsExtension).named("libs")

dependencies {
    // Access version catalog in buildSrc for dependencies
    implementation libs.findLibrary("guava").get() // Regular library from version catalog
    testImplementation platform("org.junit:junit-bom:5.9.1") // Platform dependency
    testImplementation "org.junit.jupiter:junit-jupiter" // Direct dependency
}

tasks.withType(Test).configureEach {
    useJUnitPlatform()
}

但是,预编译脚本插件中的 plugins 块无法访问版本目录。