跨模块任务依赖必须用完整路径:Gradle中子模块任务默认仅本项目可见,如api模块的generateOpenApi需被web模块build依赖,须写为dependsOn ':api:generateOpenApi',漏写冒号、大小写错误或模块未include均导致Task not found。

跨模块任务依赖必须用完整路径
Gradle 里一个子模块的任务默认只在本项目内可见。你在 api 模块里定义了 generateOpenApi,想让 web 模块的 build 任务等它跑完再执行,直接写 dependsOn 'generateOpenApi' 是无效的——Gradle 根本找不到这个任务。
正确写法是带上冒号前缀的全路径:dependsOn ':api:generateOpenApi'。这里的 : 表示根项目,:api: 是模块名,后面才是任务名。漏掉任意一段,都会报 Task not found 错误。
- 路径区分大小写,
:API:generateOpenApi和:api:generateOpenApi是两个不同任务 - 如果模块在嵌套目录下(比如
modules/api),include 'modules:api'后路径仍是:modules:api:xxx,不是:api:xxx - IDEA 刷新后可能缓存旧路径,命令行执行
./gradlew tasks --all可确认实际注册的任务名
test 任务不自动跨模块触发依赖
很多人以为在根目录运行 ./gradlew test 就会自动按依赖顺序执行所有子模块的测试,其实不是。Gradle 的 test 是每个子项目独立的任务,它不会因为 web 依赖 service,就让 web:test 等着 service:test 先跑完——两者并行,互不感知。
如果你需要这种串行控制(比如确保集成测试前基础模块已通过),得手动加依赖:
- 在
web/build.gradle里写:test.dependsOn ':service:test' - 若只想跑部分测试,别改
test本身,而是注册新任务(如integrationTest),再让它依赖其他模块的特定任务 - 注意:
test是生命周期任务,重写它的dependsOn会影响整个构建链,谨慎覆盖
自定义任务依赖 project(':x') 时要防循环
当你在 A 模块里写 myTask.dependsOn ':B:publishToMavenLocal',而 B 又依赖 A 的某个 task,Gradle 构建图就会检测到环,直接报错 Circular dependency between tasks。这不是配置写错了,是设计问题。
常见规避方式:
- 把共享逻辑抽成
buildSrc里的公共函数,避免任务级耦合 - 用
finalizedBy替代dependsOn:比如A:build完成后触发B:deploy,但不强制B:deploy等待A:build—— 方向相反,不构成环 - 对非关键流程(如文档生成、静态检查),用
shouldRunAfter控制顺序而非依赖,避免图结构被破坏
dependsOn 写在 build.gradle 还是 settings.gradle?
dependsOn 是任务关系,只能写在 build.gradle(或 build.gradle.kts)里。settings.gradle 负责项目发现和结构声明,它没任务概念,写进去会直接语法报错。
容易混淆的是:有些人在 settings.gradle 里误加 rootProject.afterEvaluate 块试图动态设依赖,这不仅无效,还会导致子项目未加载完成就尝试访问任务,引发 Unknown project 异常。
- 任务依赖必须在对应模块的
build.gradle中声明 - 如果依赖目标是其他模块的任务,确保那个模块已在
settings.gradle中被include,否则路径解析失败 - 多模块项目里,
afterEvaluate在build.gradle中可用,但应仅用于补全逻辑,不要用来绕过依赖声明规则
任务依赖不是“写了就生效”,它依赖 Gradle 对整个项目图的静态分析。路径写错、模块未 include、循环引用,都会让依赖静默失效或构建中断——最稳妥的做法,永远是先跑 ./gradlew :module:tasks 确认目标任务存在,再配依赖。











