根项目 settings.gradle 中不能使用 apply plugin,因其仅为聚合容器而非可执行模块;插件必须在子项目中声明,根项目 build.gradle 仅用于统一版本、依赖约束与共享配置,且需用 subprojects 或 allprojects 包裹 Java 相关 DSL 并加 plugins.withId("java") 防护。

根项目 settings.gradle 里不能有 apply plugin
Gradle 的根项目(root project)本质是聚合容器,不是可执行模块。如果在根项目的 build.gradle 或 build.gradle.kts 里写 apply plugin: 'java' 或 plugins { id("java") },它会尝试编译源码、生成 jar —— 但根项目默认没有 src/ 目录,构建会直接失败,报错类似:Could not resolve all files for configuration ':compileClasspath' 或 Cannot create task ':compileJava' because the source set 'main' does not exist。
正确做法是只在子项目中声明插件。根项目的 build.gradle 应只做三件事:统一版本管理、公共依赖约束、共享配置闭包。例如:
ext {
versions = [
kotlin: "1.9.24",
junit: "5.10.2"
]
}
subprojects {
apply plugin: "maven-publish"
repositories { mavenCentral() }
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter:${versions.junit}"
}
}
子项目必须显式 include,不能靠目录自动发现
settings.gradle 是唯一决定哪些项目被纳入构建的文件。即使你建了 app/ 和 lib/ 目录,不写 include ":app", ":lib",Gradle 就完全“看不见”它们——运行 ./gradlew projects 也只会显示 root,不会列出子项目。
常见错误包括:
- 用了
includeFlat "app"但子项目实际路径是./app(应改用include ":app") - 路径名含空格或特殊字符(如
my-service-v2),没加引号导致解析失败 - 在 Kotlin DSL 的
settings.gradle.kts中漏掉include(":app"),却误以为pluginManagement块能触发包含
最简安全写法(Groovy):
rootProject.name = "my-system"
include ":app", ":core", ":infra"
project(":app").projectDir = file("services/app")
project(":core").projectDir = file("shared/core")
根项目 build.gradle 里禁止写 sourceSets 或 tasks.withType(JavaCompile)
这些 DSL 属于 Java 插件作用域,根项目没应用该插件时强行配置,会导致 Could not get unknown property 'sourceSets' 这类运行时异常。Gradle 不会在解析阶段报错,而是在执行某 task 时才崩溃,排查成本高。
如果你需要统一 Java 编译选项(如 -Xlint、JDK 版本),必须包裹在 subprojects { … } 或 allprojects { … } 里,并且确保子项目已应用对应插件:
subprojects {
plugins.withId("java") {
java {
toolchain.languageVersion = JavaLanguageVersion.of(17)
}
tasks.withType(JavaCompile) {
options.compilerArgs += ["-Xlint:unchecked", "-Xlint:deprecation"]
}
}
}
注意:plugins.withId() 是必要防护,避免对非 Java 子项目(比如纯 Kotlin 脚本项目)抛出异常。
发布聚合文档或打包所有子项目产物要用 buildSrc 或自定义 task
根项目本身不产出 jar/aar,但你可能想打包所有子项目的 fat jar,或生成统一 API 文档。这类操作不能依赖默认生命周期 task(如 assemble 在根项目上什么也不做),必须手动定义。
推荐方式是用 buildSrc/src/main/groovy/AggregateTasks.groovy 定义可复用逻辑,再在根 build.gradle 中引用:
task assembleAll(type: Copy) {
from subprojects*.tasks.named("assemble").get().map { it.outputs }
into "$buildDir/assembled"
}
更健壮的做法是过滤出有 jar task 的子项目:
task assembleAllJars(type: Sync) {
from subprojects.findAll { it.tasks.findByName("jar") }*.tasks.named("jar").get().map { it.archiveFile }
into "$buildDir/fat-jars"
}
这里容易忽略的是:archiveFile 是 lazy property,必须用 .get() 解包;否则会报 Cannot cast object 'fileCollection' with class 'org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection'。











