skip to content

Walk through what happens, step by step, when a developer runs ./gradlew build on a fresh clone.

level: seniorimportance: should knowfreq 40%

answer

  1. script finds Java
  2. runs GradleWrapperMain
  3. reads properties for pinned version
  4. cache ~/.gradle/wrapper/dists
  5. download once, then delegate

basics

~10 s

gradlew finds Java and runs gradle-wrapper.jar; the jar reads gradle-wrapper.properties, downloads the named Gradle distribution to the cache if absent, then delegates to that Gradle to execute the build task.

solid answer

~40 s

On a fresh clone, `./gradlew build` chains through the wrapper: 1. The `gradlew` shell script resolves a Java runtime (via `JAVA_HOME`/`PATH`) and computes the classpath to `gradle/wrapper/gradle-wrapper.jar`. 2. It runs the jar's main class, `org.gradle.wrapper.GradleWrapperMain`, passing your arguments (`build`). 3. The bootstrap reads `gradle/wrapper/gradle-wrapper.properties` to learn exactly which Gradle distribution is pinned. 4. It checks the local cache (default `~/.gradle/wrapper/dists`). If the distribution is absent, it downloads and unpacks it there; if present, it reuses it. 5. It hands off to the real Gradle in that distribution, which finally runs `build`. The download/unpack happens only on first use of a given version; subsequent runs are cache hits, so the wrapper adds negligible overhead. This is exactly how a JDK-only machine ends up running the project's pinned Gradle.

code

bash · 6 lines
bash
$ ./gradlew build
Downloading https://services.gradle.org/distributions/gradle-8.7-bin.zip
...........10%...........30%...........60%...........100%

BUILD SUCCESSFUL
# Second run of any project pinning 8.7: no download (cache hit)

go deeper

for a junior

Roughly: gradlew runs the jar, the jar downloads Gradle if needed, then runs your task.

for a middle

Name the ordered steps and that the properties file supplies the pinned version.

for a senior

Detail the cache in the Gradle user home, download-once behavior, and where each step can fail.

for a principal

Discuss tuning GRADLE_USER_HOME for CI cache persistence and the bootstrap-vs-distribution separation as a design choice.

## The chain of hand-offs Running `./gradlew build` is a small relay. Understanding each hop demystifies wrapper errors. ### Step 1 — the script finds Java `gradlew` is a launcher, not Gradle. It locates a JVM (preferring `JAVA_HOME`, else `java` on `PATH`) and builds the command to run the wrapper jar. (`gradlew.bat` does the same on Windows.) No JDK ⇒ it fails here. ### Step 2 — run the bootstrap jar The script executes the tiny `gradle/wrapper/gradle-wrapper.jar`, whose entry point is `org.gradle.wrapper.GradleWrapperMain`. If the jar is missing (e.g. not committed), you get *'Could not find or load main class org.gradle.wrapper.GradleWrapperMain'* right here. ### Step 3 — read the pin The bootstrap reads `gradle/wrapper/gradle-wrapper.properties` to find the **exact** distribution it must use. This is where the per-project version pin is honored. ### Step 4 — resolve from the cache Gradle distributions live in the **Gradle user home**, by default `~/.gradle/wrapper/dists` (overridable via `GRADLE_USER_HOME`). The bootstrap: - computes the cache slot for the pinned distribution, - if absent: downloads the archive, unpacks it, and marks it ready, - if present: skips straight to using it. This caching is why only the *first* build on a machine pays the download cost; every later build of any project pinning that version is a cache hit. ### Step 5 — delegate to real Gradle Finally the bootstrap invokes the actual Gradle runtime from the resolved distribution, which configures the project and executes the `build` task. ```text ./gradlew build └─ gradlew (find Java) └─ gradle-wrapper.jar (GradleWrapperMain) └─ reads gradle-wrapper.properties (pinned version) └─ ~/.gradle/wrapper/dists (download if missing, else reuse) └─ real Gradle runs 'build' ``` ## Why this design Separating a *tiny committed bootstrap* from a *large cached distribution* keeps the repo small while still pinning the version. The cache shared across projects means versions are downloaded at most once per machine. ## Failure-mode map - Java not found → fails at step 1. - Missing/renamed jar → 'GradleWrapperMain' error at step 2. - Unreadable/wrong properties → can't determine the version at step 3. - No network on first run → download fails at step 4 (later runs are fine once cached).

  • Why does only the first build on a machine download Gradle?
    The distribution is unpacked into the shared Gradle user home cache (~/.gradle/wrapper/dists). Later runs of any project pinning that version reuse the cached copy, so there's no re-download.
  • Where is the downloaded distribution stored, and can it be changed?
    By default under ~/.gradle/wrapper/dists in the Gradle user home; set GRADLE_USER_HOME to relocate it, which is common on CI to enable cache persistence.

saying these in an interview costs you the question

  • Thinking the wrapper re-downloads Gradle on every run.
  • Believing the jar runs the build itself rather than delegating to a downloaded distribution.
  • Forgetting that no JDK means the script fails before any download.

context