How do you register subprojects in a Gradle multi-project build, and which file does that belong in?
answer
- include lives in settings file
- logical path not file path
- leading colon = root-relative
- graph built before configuration
- folder alone does nothing
basics
~10 sIn settings.gradle.kts you call include(":app", ":lib"). Each path becomes a project in the build. Subprojects are NOT declared in build.gradle.kts — only the settings file defines which projects exist.
solid answer
~40 sSubprojects are declared in the **settings file** (`settings.gradle.kts`), not in any `build.gradle.kts`. You call `include(":app", ":lib")` — each string is a logical project path. The leading colon means "relative to root"; a colon inside a path (`:services:auth`) denotes nesting. Gradle reads the settings file first, builds the *project graph* from those `include` calls, then locates each project's directory (by default the path mapped to folders) and its `build.gradle.kts`. Only projects named in `include` participate in the build — adding a folder on disk does nothing until it's included. The settings file is evaluated once, before any project is configured, which is why `include` lives there and not in a build script.
code
kotlin · 4 lines// settings.gradle.kts
rootProject.name = "my-app"
include(":app", ":lib", ":services:auth")go deeper
Know that include() goes in settings.gradle.kts and registers subprojects; one root project always exists.
Explain the logical-path grammar (leading vs interior colon) and that the settings file runs before configuration.
Tie include to the two-phase lifecycle: settings assembles the graph, then each project is configured; explain default path→directory mapping.
Frame project membership as the deterministic, single source of truth the configuration cache, IDE import, and tooling all depend on.
## What `include` does A Gradle build is a tree of **projects**. There is always exactly one *root project*, and every other project is a *subproject*. The set of projects is fixed entirely by the **settings file** — `settings.gradle.kts` (Kotlin DSL) or `settings.gradle` (Groovy) — which sits at the build root. You register subprojects with the `include` method on the `Settings` object: ```kotlin // settings.gradle.kts rootProject.name = "my-app" include(":app", ":lib", ":services:auth") ``` Each argument is a **logical project path**, not a file path. The path grammar: - A leading `:` means "relative to the root project". - An interior `:` denotes one level of nesting in the project hierarchy. So `:services:auth` declares a project `auth` whose parent is `services`. Including `:services:auth` does **not** implicitly create a configurable `:services` project unless you also `include(":services")` — but the path segment still exists in the hierarchy. ## Settings file vs build script This is the single most common confusion: **you never declare which projects exist inside a `build.gradle.kts`.** Build scripts configure a project that already exists; the settings file decides the membership of the build. The lifecycle is: 1. Gradle evaluates the **settings file** once, executing every `include` to assemble the project graph. 2. It then *configures* each project by evaluating its build script. Because step 1 runs entirely before step 2, the full set of projects is known before any build logic runs. ## Directory mapping By default Gradle maps the project path to a directory under the root: `:services:auth` → `<root>/services/auth`. The `build.gradle.kts` is expected there. (You can override the directory with `project(":auth").projectDir = file(...)` — covered by a sibling topic.) A folder with a build script that is *not* included is invisible to Gradle. ## The rootProject reference Inside the settings file, `rootProject` is the `ProjectDescriptor` for the root. `rootProject.name` sets the build's name (defaults to the root directory name). You can also reach included projects as descriptors via `project(":app")` to tweak their `projectDir`, `name`, or `buildFileName` before configuration begins. ## Why it matters Keeping project membership in one declarative place (the settings file) means the build graph is deterministic and discoverable. Tooling, the configuration cache, and IDE import all read it to know what to materialize.
- If I create a folder `payments` with a build.gradle.kts but never call include(":payments"), what happens?Nothing — Gradle has no knowledge of it. It isn't a project in the build, won't be configured, and can't be a dependency. You must add `include(":payments")` to the settings file.
- Does include go in the root build.gradle.kts instead?No. Project membership is determined solely by the settings file. Build scripts configure existing projects; they cannot add new ones.
saying these in an interview costs you the question
- Saying subprojects are declared in build.gradle.kts.
- Treating the include argument as a filesystem path instead of a logical project path.
- Assuming a folder on disk is automatically a subproject.