升级Gradle 8.7后因sourceSets引发任务循环依赖问题求助
Gradle 8.7 升级后循环依赖问题解决
问题概述
老旧项目此前在Gradle 5.3、7.6.3版本运行正常,升级至Gradle 8.7后出现循环依赖:
:compileJava --- :schema --- :compileJava (*)
核心矛盾:
schema是JavaExec任务,负责从数据库表生成Java文件,这些文件是compileJava的编译必需项schema的classpath必须包含sourceSets.main.java.classesDirectory(主源码编译后的类文件),否则会报「Could not find or load main class ch.borm.live.init.SchemaGenerator」- 但
compileJava又依赖schema,形成逻辑上的循环依赖
为什么旧版本能运行?
Gradle 8.x对任务依赖的自动推断和循环检测机制做了强化。在7.x及更早版本中,当任务的classpath引用编译输出目录时,Gradle可能没有自动将输出目录对应的编译任务(这里是compileJava)添加为前置依赖,因此循环依赖没有被检测出来,得以“侥幸”运行。但这种写法本身就存在逻辑矛盾——要运行schema需要先编译主类,而编译主类又需要schema生成的代码。
解决方案
方案1:拆分源码到独立SourceSet(推荐)
把SchemaGenerator这类工具类从主源码中拆分出来,单独放到一个tools SourceSet,先编译工具类,再用工具类运行schema任务,彻底打破循环:
- 定义新的SourceSet:
sourceSets { main { resources { srcDirs = ["src/main/resources/${RES_DIR}", "..."] } java { srcDirs = ["src/main/java", "src/main/grpc"] } } // 新增工具类SourceSet tools { java { srcDirs = ["src/main/tools"] // 将SchemaGenerator移到这个目录下 } } }
- 重新配置
schema任务:
tasks.register('schema', JavaExec) {} [schema ...].each { task -> // 使用tools的运行时classpath,而非main的classesDirectory task.classpath = files(sourceSets.tools.runtimeClasspath, sourceSets.main.compileClasspath) task.mainClass = 'ch.borm.live.init.SchemaGenerator' // 确保先编译工具类和compileSchema task.dependsOn compileSchema, sourceSets.tools.classes }
- 保持
compileJava对schema的依赖:
compileJava { dependsOn schema dependsOn ... }
执行顺序变为:compileToolsJava → schema → compileJava,完全消除循环。
方案2:单独编译SchemaGenerator类(临时过渡)
如果不想移动代码,可以创建一个单独的编译任务,只编译SchemaGenerator相关类,再用这个任务的输出作为schema的classpath:
// 单独编译SchemaGenerator task compileSchemaGenerator(type: JavaCompile) { source = fileTree('src/main/java') { include 'ch/borm/live/init/SchemaGenerator.java' } classpath = sourceSets.main.compileClasspath destinationDir = file("$buildDir/tmp/schemaGeneratorClasses") } // 配置schema任务 [schema ...].each { task -> task.classpath = files(compileSchemaGenerator.destinationDir, sourceSets.main.compileClasspath) task.mainClass = 'ch.borm.live.init.SchemaGenerator' task.dependsOn compileSchema, compileSchemaGenerator }
这种方式无需移动代码,但维护性不如方案1,适合临时过渡。
内容的提问来源于stack exchange,提问作者Олег Берест
相关产品推荐
相关产品推荐

