skip to content

Declaring Subprojects with include

Registering subprojects with include in settings.gradle.kts, and how the project graph is assembled before any build script is evaluated. Asked because a directory missing from settings simply does not exist to Gradle.

on this pageshow

questions

5

How do you register subprojects in a Gradle multi-project build, and which file does that belong in?

level: juniorimportance: must knowfreq 75%

answer

  1. include lives in settings file
  2. logical path not file path
  3. leading colon = root-relative
  4. graph built before configuration
  5. folder alone does nothing

basics

~10 s

In 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 s

Subprojects 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
kotlin
// settings.gradle.kts
rootProject.name = "my-app"

include(":app", ":lib", ":services:auth")

go deeper

for a junior

Know that include() goes in settings.gradle.kts and registers subprojects; one root project always exists.

for a middle

Explain the logical-path grammar (leading vs interior colon) and that the settings file runs before configuration.

for a senior

Tie include to the two-phase lifecycle: settings assembles the graph, then each project is configured; explain default path→directory mapping.

for a principal

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.

context

open as a page

Explain the difference between a project's logical path (the include argument) and its directory on disk.

level: middleimportance: must knowfreq 55%

basics

~10 s

The include string (e.g. ':services:auth') is a logical project path used to identify the project. By default Gradle maps it to a matching folder (services/auth), but the path and the directory are separate concepts.

open as a page

What is the rootProject reference in settings.gradle.kts, and what would you commonly use it for?

level: middleimportance: should knowfreq 35%

basics

~20 s

rootProject is the ProjectDescriptor for the build's single root project, available in the settings file. The most common use is rootProject.name = "..." to give the build a stable name instead of defaulting to the root folder name.

open as a page

At what point in the Gradle lifecycle is the project graph assembled from include() calls, and why does that ordering matter?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The project graph is built during the settings (initialization) phase, when Gradle evaluates settings.gradle.kts and runs every include(). This happens before any build.gradle.kts is configured, so the full set of projects is known before configuration starts.

open as a page

When you include(":services:auth") but never include(":services"), does a configurable :services project exist?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Including only :services:auth creates the auth project and a :services container in the hierarchy, but :services has no build script by default, so it isn't a project you configure. To configure :services, include it explicitly.

open as a page