如何在兼容配置缓存的Gradle中为JAR清单添加Class-Path条目?
如何在兼容配置缓存的Gradle中为JAR清单添加Class-Path条目?
我完全理解你的困扰——给JAR的Manifest加Class-Path是个非常常见的需求,之前用doFirst的老方法在配置缓存下失效确实头疼,不过现在Gradle的Provider API正好能完美解决这个问题,而且完全兼容配置缓存。
为什么之前的尝试会失败?
你遇到的配置不可变错误,核心原因是直接在配置阶段触发了依赖解析:当你调用configurations.runtimeClasspath.files时,Gradle会提前锁定这个配置,后续任何修改依赖的操作都会被拒绝。而配置缓存要求我们尽可能使用延迟计算的方式,避免在配置阶段触碰配置的可变状态。
兼容配置缓存的解决方案
我们可以用Gradle的Provider API来延迟计算Class-Path条目,这样既不会提前锁定配置,又能让配置缓存正常工作。下面是针对多项目Groovy构建脚本的实现:
subprojects { // 只对应用了Java插件的子项目生效 plugins.withType(JavaPlugin) { jar { // 用Provider延迟计算runtimeClasspath的JAR名称列表 def classPathEntries = providers.provider { configurations.runtimeClasspath.files.collect { it.name }.join(' ') } // 显式声明输入,确保增量构建和配置缓存正常追踪变化 inputs.files(configurations.runtimeClasspath) .withNormalizer(ClasspathNormalizer) .optional() // 允许子项目没有runtime依赖的情况 // 将Manifest的Class-Path属性绑定到延迟计算的Provider manifest { attributes( 'Class-Path': classPathEntries ) } } } }
关键细节解释
- 延迟计算的Provider:
providers.provider { ... }里的代码不会在配置阶段执行,只会在任务即将执行前的安全时机运行,这时候依赖解析已经完成,但不会提前锁定配置,后续修改依赖的操作依然有效。 - 输入声明:
inputs.files(configurations.runtimeClasspath)告诉Gradle,这个任务的输出(JAR的Manifest)依赖于runtimeClasspath的内容,确保增量构建和配置缓存能正确识别变化,避免不必要的重建。 - 多项目适配:通过
plugins.withType(JavaPlugin)确保只有Java子项目会应用这个配置,符合你的多项目构建场景。
如果你用Kotlin DSL
如果你的项目用的是Kotlin构建脚本,对应的实现如下:
subprojects { plugins.withType<JavaPlugin> { tasks.jar { val classPathEntries = providers.provider { configurations.runtimeClasspath.get().files.joinToString(" ") { it.name } } inputs.files(configurations.runtimeClasspath) .withNormalizer(ClasspathNormalizer::class) .optional() manifest { attributes( "Class-Path" to classPathEntries ) } } } }
验证效果
这个方案完全符合Gradle配置缓存的最佳实践:
- 不会再出现配置不可变的错误,后续添加/修改依赖的操作依然正常工作;
- 配置缓存能正确缓存这个任务的状态,大幅提升后续构建速度;
- 支持增量构建:只有当runtimeClasspath变化时,才会重新生成JAR的Manifest。
内容来源于stack exchange
相关产品推荐
相关产品推荐

