使用动态依赖项版本(例如1.+[1.0,2.0) )使构建不确定. 这会导致构建崩溃而没有任何明显的变化,更糟糕的是,可能由构建作者无法控制的传递依赖引起.

为了实现可复制的构建 ,必须锁定依赖项和传递性依赖项的版本,以便具有相同输入的构建将始终解析相同的模块版本. 这称为依赖锁定 .

除其他外,它启用以下方案:

  • 处理多个存储库的公司不再需要依赖-SNAPSHOT或更改依赖关系,当依赖关系引入错误或不兼容时,有时会导致级联失败. 现在可以针对主要或次要版本范围声明依赖关系,从而可以在CI上测试最新版本,同时利用锁定来稳定开发人员构建.

  • 希望始终使用最新依赖关系的团队可以使用动态版本,仅针对发行版锁定其依赖关系. release标签将包含锁定状态,从而在需要开发错误修复程序时可以完全重现该版本.

发布解析版本结合使用,您还可以在发布时替换声明的动态版本部件. 消费者将看到您的发行版已解决的版本.

每个依赖项配置均启用锁定. 启用后,您必须创建一个初始锁定状态. 这将使Gradle验证分辨率结果没有改变,即使生成了新版本,也会导致选择的依赖项相同. 对您的构建的修改可能会影响已解决的依赖关系集,将导致其失败. 这样可以确保发布的依赖项或构建定义中的更改不会在不调整锁定状态的情况下更改分辨率.

依赖关系锁定仅对于动态版本才有意义. 尽管内容可能会更改,但-SNAPSHOT坐标保持不变的更改版本 (例如-SNAPSHOT )没有影响. 当持久锁状态和解析结果中存在变化的依赖性时,Gradle甚至会发出警告.

Enabling locking on configurations

锁定配置是通过ResolutionStrategy进行的

示例1.锁定特定配置
build.gradle
configurations {
    compileClasspath {
        resolutionStrategy.activateDependencyLocking()
    }
}
build.gradle.kts
configurations.compileClasspath {
    resolutionStrategy.activateDependencyLocking()
}

或以下方法,作为锁定所有配置的一种方法:

例子2.锁定所有配置
build.gradle
dependencyLocking {
    lockAllConfigurations()
}
build.gradle.kts
dependencyLocking {
    lockAllConfigurations()
}

仅可解析的配置将具有锁定状态. 在不可解析的配置上应用锁定只是一个禁忌.

上面的代码将锁定所有项目配置,但不会锁定buildscript的配置.

您还可以禁用对特定配置的锁定. 如果某个插件配置为锁定所有配置,但是您碰巧添加了一个不应锁定的插件,则此功能很有用.

示例3.解锁特定配置
build.gradle
configurations {
    compileClasspath {
        resolutionStrategy.deactivateDependencyLocking()
    }
}
build.gradle.kts
configurations.compileClasspath {
    resolutionStrategy.deactivateDependencyLocking()
}

Locking buildscript classpath configuration

如果您将插件应用于构建,则可能还希望利用那里的依赖项锁定. 为了锁定用于脚本插件的classpath配置 ,请执行以下操作:

例子4.锁定buildscript类路径配置
build.gradle
buildscript {
    configurations.classpath {
        resolutionStrategy.activateDependencyLocking()
    }
}
build.gradle.kts
buildscript {
    configurations.classpath {
        resolutionStrategy.activateDependencyLocking()
    }
}

Generating and updating dependency locks

为了生成或更新锁定状态,除了会触发要解析配置的常规任务之外,还指定--write-locks命令行参数. 这将导致在该构建执行中为每个已解析的配置创建锁定状态. 请注意,如果先前存在锁定状态,则将其覆盖.

Lock all configurations in one build execution

锁定多个配置时,您可能希望在一次构建执行中一次锁定所有配置.

为此,您有两个选择:

  • 运行gradle dependencies --write-locks . 这将有效地锁定所有启用了锁定的可解析配置. 请注意,在多项目设置中, dependencies仅在一个项目上执行,在本例中为根.

  • 声明将解决所有配置的自定义任务

例子5.解决所有配置
build.gradle
task resolveAndLockAll {
    doFirst {
        assert gradle.startParameter.writeDependencyLocks
    }
    doLast {
        configurations.findAll {
            // Add any custom filtering on the configurations to be resolved
            it.canBeResolved
        }.each { it.resolve() }
    }
}
build.gradle.kts
tasks.register("resolveAndLockAll") {
    doFirst {
        require(gradle.startParameter.isWriteDependencyLocks)
    }
    doLast {
        configurations.filter {
            // Add any custom filtering on the configurations to be resolved
            it.isCanBeResolved
        }.forEach { it.resolve() }
    }
}

第二种选择是,通过正确选择配置,可以成为本机环境中的唯一选择,在本机环境中,并非所有配置都可以在单个平台上解决.

Lock state location and format

锁定状态将保存在项目或子项目目录内的gradle/dependency-locks文件夹中的文件中. 每个文件均由其锁定的配置命名,并具有扩展名lockfile . 该规则的一个例外是构建脚本本身的配置. 在这种情况下,配置名称将以buildscript-为前缀.

文件的内容是每行的模块表示法,标题提供了一些上下文. 模块符号按字母顺序排序,以简化差异.

gradle/dependency-locks/compileClasspath.lockfile
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.springframework:spring-beans:5.0.5.RELEASE
org.springframework:spring-core:5.0.5.RELEASE
org.springframework:spring-jcl:5.0.5.RELEASE

匹配以下依赖项声明:

例子6.动态依赖声明
build.gradle
dependencies {
    implementation 'org.springframework:spring-beans:[5.0,6.0)'
}
build.gradle.kts
dependencies {
    implementation("org.springframework:spring-beans:[5.0,6.0)")
}

Running a build with lock state present

The moment a build needs to resolve a configuration that has locking enabled and it finds a matching lock state, it will use it to verify that the given configuration still resolves the same versions.

成功的构建表示使用与锁定状态中存储的依赖项相同的依赖项,无论是否已生成与动态选择器匹配的新版本.

完整的验证如下:

  • 处于锁定状态的现有条目必须在构建中匹配

    • 版本不匹配或缺少已解决的模块会导致构建失败

  • 与锁定状态相比,解析结果不得包含其他依赖项

Selectively updating lock state entries

为了仅更新配置的特定模块,可以使用--update-locks命令行标志. 它以逗号( , )分隔的模块符号列表. 在这种模式下,现有的锁定状态仍将用作解决方案的输入,从而滤除更新目标的模块.

❯ gradle classes --update-locks org.apache.commons:commons-lang3,org.slf4j:slf4j-api

*表示的通配符可以在组名或模块名中使用. 它们可以是唯一字符,也可以分别出现在组或模块的末尾. 以下通配符表示法示例有效:

  • org.apache.commons:* :将让属于org.apache.commons组的所有模块更新

  • *:guava :无论名为guava所有模块,无论其guava组,都将对其进行更新

  • org.springframework.spring*:spring* :将让所有模块的组以org.springframework.spring开头,名称以spring更新开始

分辨率可能会导致其他模块版本更新,这取决于Gradle分辨率规则.

Disabling dependency locking

  1. 确保不再需要锁定的配置未配置锁定.

  2. 删除与不再需要锁定的配置相匹配的文件.

如果仅执行上面的第二步,则锁定将不再有效. 但是,如果将来在持久保留锁定状态时解决了该配置,它将再次被锁定.

Locking limitations

  • 锁定尚不能应用于源依赖项.