How can you cross-configure a single named subproject (or a filtered subset) from the root build, rather than every project?
answer
- project(":path"){} targets one
- configure(listOf(...)) for a set
- if(name...) filter inside subprojects
- missing path => could not be found
- prefer convention plugin for categories
basics
~10 sUse project(":path") { ... } to target one named subproject, or filter inside subprojects {} with a condition like if (name.startsWith("feature-")). allprojects/subprojects are the all-or-nothing versions.
solid answer
~40 sBeyond the blanket `allprojects {}`/`subprojects {}`, the root build can configure projects more selectively: - `project(":lib:core") { ... }` configures exactly that one project by path. - `configure(listOf(project(":a"), project(":b"))) { ... }` configures a chosen set. - A guard inside `subprojects {}`: `subprojects { if (path.startsWith(":services:")) { ... } }` filters by some project property. All of these are still cross-configuration from the root, so they carry the same coupling caveat — the child's behavior is defined in a file the child doesn't own. But targeting one project is sometimes pragmatic for a one-off (e.g. only the `:app` module needs the `application` plugin). For broad, repeatable conventions a convention plugin is cleaner; targeted `project(...)` blocks are best reserved for genuinely module-specific wiring.
code
kotlin · 14 lines// Configure just the app module from the root
project(":app") {
apply(plugin = "application")
dependencies {
"implementation"(project(":lib:core"))
}
}
// Apply Spring Boot only to *-service modules
subprojects {
if (name.endsWith("-service")) {
apply(plugin = "org.springframework.boot")
}
}go deeper
Know that project(":path") { } configures one specific module, versus the all-projects blocks.
Show the path form, the configure(listOf(...)) form, and a filtered subprojects block; mention the could-not-be-found error.
Discuss root-then-descend ordering, override precedence, and when targeted config beats a convention plugin.
Argue for explicit membership (convention plugins) over string-predicate filtering as the module count grows.
## The toolbox Gradle's root project exposes several ways to reach into children, not just the two blanket blocks: ```kotlin // One specific project by path project(":app") { apply(plugin = "application") configure<JavaApplication> { mainClass.set("com.example.Main") } } // A hand-picked set configure(listOf(project(":lib:core"), project(":lib:api"))) { apply(plugin = "java-library") } // All subprojects, filtered subprojects { if (name.endsWith("-service")) { apply(plugin = "org.springframework.boot") } } ``` ## Why the path form needs care `project(":app")` returns the `Project` for that path, and the trailing closure configures it. This works because, by configuration time, the project exists (it was `include`d in settings). If you reference a path that wasn't included, Gradle throws `Project with path ':app' could not be found`. Ordering can bite you: `project(":app") { ... }` runs when the root script reaches that line. If `:app`'s own `build.gradle.kts` later overrides the same property, the leaf script wins because Gradle configures the root first, then descends. Relying on that ordering is brittle. ## Filtering inside subprojects Filtering by `name`, `path`, or `projectDir` lets you apply a convention to a slice of the tree. It reads cleanly for small trees but becomes a tangle of `if`s as the build grows — each new module silently matches or misses a string predicate. That implicit matching is a maintenance hazard. ## When to use what - One-off, genuinely module-specific wiring → `project(":x") { }` is fine and explicit. - A convention shared by a *category* of modules → prefer a convention plugin the relevant modules apply, so membership is explicit and the typed DSL works. The common thread: every one of these still defines a child's behavior from outside the child, so reach for the most explicit, least-coupled option that does the job.
- What happens if you call `project(":missing") { ... }` for a path that was never included in settings?Gradle fails configuration with an error like `Project with path ':missing' could not be found`, because the project object doesn't exist.
- If the root configures `:app` and `:app`'s own script sets the same property, which wins?The leaf script — Gradle configures the root project first, then descends into each subproject, so the subproject's own configuration runs last and overrides. Relying on this ordering is brittle.
saying these in an interview costs you the question
- Claiming `project(":x")` can target a path that wasn't `include`d in settings.
- Treating filtered subprojects blocks as clean at scale — string predicates rot quickly.