skip to content

Inside an init script the delegate is the Gradle object. Which lifecycle hooks does it expose, and how would you use allprojects vs settingsEvaluated?

level: middleimportance: should knowfreq 40%

answer

  1. Gradle delegate exposes invocation hooks
  2. settingsEvaluated fires once, gives Settings
  3. allprojects = per-project sugar
  4. beforeProject/afterProject/projectsLoaded
  5. order: settings → projectsLoaded → beforeProject

basics

~10 s

The Gradle delegate exposes lifecycle hooks like settingsEvaluated, projectsLoaded, beforeProject/afterProject, and the allprojects {} shortcut. Use allprojects {} to configure every project; use settingsEvaluated {} to act once the settings are known.

solid answer

~40 s

Because an init script's delegate is the `Gradle` object, it can register the invocation-wide lifecycle callbacks: `settingsEvaluated { settings -> }` (settings parsed, project structure known), `projectsLoaded { gradle -> }` (Project instances created), `beforeProject { project -> }` / `afterProject { project -> }`, plus the convenience `allprojects {}`/`rootProject {}` which schedule configuration for projects. `allprojects {}` is sugar that runs your closure against every project as it is configured — perfect for injecting shared repositories or plugins. `settingsEvaluated {}` fires exactly once, after `settings.gradle(.kts)` has been evaluated but before projects are built, so it's where you'd tweak the resolved `Settings` (e.g. inspect `settings.dependencyResolutionManagement` or the included builds). The distinction: `allprojects` is per-project and project-centric; `settingsEvaluated` is once-per-build and settings-centric.

code

kotlin · 10 lines
kotlin
// ~/.gradle/init.d/00-corp.init.gradle.kts
settingsEvaluated { settings ->
    require(settings.rootProject.name.isNotBlank())
}

allprojects {
    repositories {
        maven { url = uri("https://nexus.corp.example/maven-public") }
    }
}

go deeper

for a junior

Recognize allprojects {} as the way to apply config to every project, and that the delegate is the Gradle object.

for a middle

Name the main hooks (settingsEvaluated, projectsLoaded, beforeProject/afterProject) and articulate allprojects vs settingsEvaluated.

for a senior

Discuss firing order, when to read the resolved Settings, and the configuration-cache implications of cross-project injection.

for a principal

Weigh init-script injection vs convention plugins as the org pattern, and the maintenance/CC cost of each.

## The Gradle delegate An init script's `this` is an `org.gradle.api.invocation.Gradle` instance. Methods you call without a receiver are resolved against it, which is why init scripts can register *invocation-wide* lifecycle callbacks that ordinary build scripts cannot reach as cleanly. ## The lifecycle hooks, in firing order 1. **`settingsEvaluated { settings -> }`** — fires once after `settings.gradle(.kts)` has been evaluated. At this point the multi-project structure, `pluginManagement`, `dependencyResolutionManagement`, and included builds are all known. You receive the `Settings` object. 2. **`projectsLoaded { gradle -> }`** — fires once after the `Project` objects have been created (but not yet configured). Good place to call `gradle.rootProject { ... }`. 3. **`beforeProject { project -> }`** / **`afterProject { project -> }`** — fire around the configuration of *each* project. 4. **`projectsEvaluated { gradle -> }`** — once, after all projects are configured. 5. **`buildFinished { result -> }`** — once, when the build ends (now often replaced by build services / flow actions, but still available). ## allprojects / rootProject `allprojects { ... }` and `rootProject { ... }` are shortcuts on `Gradle`. They schedule the given configuration block to run against, respectively, every project and the root project once it's available. This is the idiomatic way to inject shared config from an init script: ```kotlin // ~/.gradle/init.gradle.kts settingsEvaluated { // runs once: settings are fully resolved here println("Building project: " + rootProject.name) } allprojects { // runs for EVERY project as it's configured repositories { mavenCentral() } } ``` ## allprojects vs settingsEvaluated — when to use which - Reach for **`allprojects {}`** when the thing you're configuring is *per-project*: repositories, common plugins, conventions applied to each module. - Reach for **`settingsEvaluated {}`** when you need the *resolved settings exactly once*: e.g. to read or adjust `dependencyResolutionManagement`, inspect included builds, or fail the build early if a policy isn't met. It's the earliest point where the full project graph and centralized repo/dependency config exist. ## Caveat with newer Gradle Gradle is steadily nudging users away from mutating other projects from outside ("cross-project configuration") and toward convention plugins. Init-script injection still works and is widely used for org/CI concerns, but for project-owned conventions a precompiled convention plugin is the more configuration-cache-friendly choice.

  • Why might settingsEvaluated be a better place than allprojects to enforce a repository policy?
    Because by settingsEvaluated the centralized dependencyResolutionManagement is already resolved, so you can inspect/adjust it once rather than reacting per project, and you can fail fast before any project is configured.
  • Does allprojects in an init script override allprojects declared in the root build script?
    No — both run. They are additive; the init-script block and the build-script block each configure the projects, with the init script's contribution applied first.

saying these in an interview costs you the question

  • Claiming settingsEvaluated fires per project — it fires exactly once.
  • Thinking allprojects gives you the Settings object; it gives you each Project.

context