Gradle: Installation
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Gradle 9.8.1 (released on 7 October 2026), with 8.14.6, the last 8.x release, mentioned where it
differs. Every example marked as run was executed with the 9.8.1 binary distribution on JDK 26 (Oracle java version "26") on
2026-10-16, on macOS (Apple silicon), in a scratch directory. Output that comes from a run is shown as printed, with two
exceptions that are labelled: the scratch directory the archive was unpacked into is shown as /opt/gradle, and other scratch
paths are shortened to <scratch>. Gradle 8.14.x was not downloaded, so what is said about it comes from the official release
notes and is labelled "not run here"; so are the package managers (SDKMAN!, Homebrew, MacPorts, Chocolatey, Scoop, winget),
which were not installed or run, and Windows. The commands use a POSIX shell; the Windows equivalents come from the official
page. The build files that gradle init generates are shown in the Kotlin DSL first and in the Groovy DSL where they differ.
A Gradle installation is a directory: a launcher script, about two hundred JARs and an empty init.d directory. Nothing else is
installed on the machine. Everything Gradle needs while it works (the dependencies and plugins of a build, the distributions the
wrapper downloads, the daemon logs) goes into the Gradle User Home, which is created on first use and is the subject of
Directories & Logging. The most important
sentence of the whole installation story is the first one in the official page: you do not need to install Gradle at all to run
an existing project that carries the wrapper, and the usual reason to install it is to run gradle init once, which creates the
wrapper. The model that explains what the installed program then does is in
Core Concepts; the choice of release is in
Versions, Compatibility & Release History.
The running scenario (tidepool) is described on the
section landing page; for the other two tools see
Ant: Installation and
Maven: Installation.
Do you need to install Gradle?
The official page answers it with one test: the Gradle Wrapper is identifiable by the presence of the gradlew and gradlew.bat
files in the root of the project. If they are there, you do not install Gradle; you only need a suitable JDK, and you run
./gradlew (or gradlew.bat on Windows). The wrapper downloads the Gradle version the project asks for into the Gradle User
Home the first time and uses it from then on, so every developer and every CI job runs the same version. That mechanism, the
files it consists of and how to upgrade it are in
Gradle Wrapper. Two more cases need no installation:
Android Studio ships with a working Gradle, and the IDEs that import Gradle builds drive Gradle through the Tooling API (see
CI & IDE Integration).
You need an installed Gradle when you start a project from scratch on the command line, because gradle init is what creates the
wrapper files, and for the occasional task that is not tied to a project (running gradle --version, trying a release candidate,
or gradle wrapper in a project that has none). After that the installed program is a convenience: with a project that has a
wrapper, ./gradlew is the command to use, and gradle runs whatever version happens to be first on the PATH, which can be
the wrong one.
Source: Gradle: Installing Gradle
Prerequisites
Gradle needs a Java Development Kit, version 17 or higher, to run. The compatibility page states the range as a JVM between 17 and 27 for 9.8.1, with 28 and later "not yet supported", and it adds that the wrapper, the Gradle client, the Tooling API client and the TestKit client are compatible with JVM 8. The 17 floor is the change of Gradle 9.0.0: the release notes say the JVM is required "to start the Gradle daemon", and that building with older JVMs is done by naming a separate JDK in a toolchain. Gradle 8.14.x still ran on Java 8 (not run here; the compatibility table lists 8 as supported for running Gradle through 8.14.x).
Gradle does not need Kotlin, Groovy or Ant installed: it ships its own, and an existing installation of them is ignored. The
gradle --version output below shows the embedded versions for 9.8.1: Kotlin 2.4.10, Groovy 4.0.33 and Ant 1.10.17.
The Java that runs Gradle and the Java that compiles your project are two different settings. The first is the one found by the
gradle script (below); the second is chosen by the project, normally with a toolchain, which is the reason a build can compile
for Java 8 on a Gradle that itself needs 17. See Toolchains and
the way the daemon picks its own JVM in
Gradle Daemon. To check what you have:
java -version
java version "26" 2026-03-17
Java(TM) SE Runtime Environment (build 26+35-2893)
Java HotSpot(TM) 64-Bit Server VM (build 26+35-2893, mixed mode, sharing)
The official page lists three places Gradle looks for a JDK: the PATH, the JDK an IDE uses, and the one the project
specifies. For the command line it is the JAVA_HOME environment variable if set, otherwise the java on the PATH; the
script’s logic is shown in What the launcher script does. Running a JDK older than 17 (a JDK 11, for example) was not run here, because
no such JDK is installed; no output is invented for it.
Getting Gradle
The distributions
Every release is published as two ZIP files. The binary-only distribution (-bin) contains the program; the complete one (-all)
adds the documentation and the sources. The official recommendation is the -bin file: it is smaller and quick to download, and
the documentation is online. For 9.8.1 the -bin archive is 151,620,101 bytes (about 145 MB) and unpacks to 165 MB. The same
choice, -bin or -all, returns for the wrapper, which downloads one of these two files; why -bin is the better default is in
Gradle Wrapper.
Gradle publishes the list of releases on https://gradle.org/releases/ and the same information in a small JSON document that tools use. The "current" endpoint is a handy way to ask which release is current, where its archive is, and which SHA-256 it should have; it is metadata, not a download of the distribution:
curl -s https://services.gradle.org/versions/current
{
"version" : "9.8.1",
"buildTime" : "20261007165746+0000",
"commitId" : "39daf39feb60a18b1b6ddccde53366522fef516f",
"current" : true,
"snapshot" : false,
"nightly" : false,
"releaseNightly" : false,
"activeRc" : false,
"rcFor" : "",
"milestoneFor" : "",
"broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions/gradle-9.8.1-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions/gradle-9.8.1-bin.zip.sha256",
"checksum" : "dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions/gradle-9.8.1-wrapper.jar.sha256",
"wrapperChecksum" : "3b8a25775a69158b5ad2b1d17a80a88dc7b40a352a73eb27d06c612a7ce68e98",
"releasedOrActiveRc" : true,
"publicationSlot" : "9.8.1",
...
The listing is cut after publicationSlot (an ellipsis line marks the cut). The fields activeRc, snapshot and nightly
exist because the same family of endpoints also describes release candidates and nightly builds.
Source: Gradle: Releases
Source: Gradle: Install Gradle
Verifying the download
Gradle publishes a SHA-256 checksum for every distribution and for every gradle-wrapper.jar on its
release-checksums page, and next to the file itself (a .sha256 file that services.gradle.org
answers with a redirect to downloads.gradle.org). A checksum protects you from a damaged or substituted download only as far as
you trust the channel the checksum came from: fetch it from gradle.org over HTTPS, not from the place you got the ZIP. Gradle
also publishes a detached PGP signature (gradle-9.8.1-bin.zip.asc is served next to the archive, and the manual says Gradle
publishes PGP signatures for all distribution artifacts), as Apache does for Ant and Maven; checking it needs Gradle’s public key,
and that check was not run here. The checksum file holds the bare hexadecimal
value and no file name, so the shasum -c form needs the name added to the line. The first check below is the real archive; the
second appends one byte to a copy of it:
curl -sL https://services.gradle.org/distributions/gradle-9.8.1-bin.zip.sha256 -o gradle-9.8.1-bin.zip.sha256
cat gradle-9.8.1-bin.zip.sha256
echo "$(cat gradle-9.8.1-bin.zip.sha256) gradle-9.8.1-bin.zip" | shasum -a 256 -c -
cp gradle-9.8.1-bin.zip tampered.zip && printf 'x' >> tampered.zip
echo "$(cat gradle-9.8.1-bin.zip.sha256) tampered.zip" | shasum -a 256 -c -
echo "exit status of the second check: $?"
dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f
gradle-9.8.1-bin.zip: OK
tampered.zip: FAILED
shasum: WARNING: 1 computed checksum did NOT match
exit status of the second check: 1
The value is the one in the JSON above and the one on the release-checksums page; it is also the distributionSha256Sum that a
wrapper for 9.8.1 pins. On Linux use sha256sum -c in place of shasum -a 256 -c; on Windows certutil -hashfile
gradle-9.8.1-bin.zip SHA256 prints the digest to compare by eye (not run here). Checking the wrapper JAR, validating the
wrapper on CI and the other supply-chain controls are in
Supply-Chain Security; the wrapper properties that pin
the checksum are in Gradle Wrapper.
Source: Gradle: release checksums
Installing manually
The manual installation has the same three steps on every system: download, unpack, put the bin directory on the PATH.
Unpacking and the directory layout
The official examples unpack into /opt/gradle on Linux and /usr/local/gradle on macOS. Any directory works, and a directory
per version (gradle-9.8.1) lets several versions live side by side. The run below unpacked the verified archive into a scratch
directory that is shown as /opt/gradle:
mkdir -p /opt/gradle
unzip -q gradle-9.8.1-bin.zip -d /opt/gradle
cd /opt/gradle/gradle-9.8.1
ls
ls bin
ls lib | wc -l
ls lib/plugins | wc -l
ls -d lib/*/
du -sh .
LICENSE
NOTICE
README
bin
init.d
lib
gradle
gradle.bat
430
279
lib/agents/
lib/api/
lib/plugins/
165M
The layout is small:
| Entry | Purpose |
|---|---|
|
The launcher scripts, for POSIX shells and for Windows. The script is generated by Gradle’s own start-script generator, the same
one behind the |
|
The core JARs (209 of them, with a |
|
Initialization scripts run for every build of every user of this installation. It contains one file, |
|
The Apache 2.0 licence and the notices. The |
There is no conf directory and no global settings.xml-style file as in Maven: the configuration lives in the Gradle User Home
and in the projects, and the installation only contributes the two optional files described in Files in the installation directory.
The PATH and GRADLE_HOME
Put the bin directory on the PATH:
export PATH=$PATH:/opt/gradle/gradle-9.8.1/bin
The official page offers an alternative: define GRADLE_HOME and put $GRADLE_HOME/bin on the PATH, so that upgrading means
changing one variable.
export GRADLE_HOME=/opt/gradle/gradle-9.8.1
export PATH=${GRADLE_HOME}/bin:${PATH}
Put the lines in the shell start-up file (.bash_profile, .zshrc, /etc/profile.d/) so that every new shell has them. On
Windows the official steps are the System Properties dialog (Advanced System Settings, Environment Variables, edit Path, add
C:\Gradle\gradle-9.8.1\bin) or the same GRADLE_HOME and %GRADLE_HOME%\bin pair (not run here).
GRADLE_HOME is only a convenience for building the PATH: neither launcher script reads it. The POSIX script finds its own
directory from the path it was started with, following symbolic links, and the Windows batch file does the same (a search for
GRADLE_HOME in bin/gradle.bat finds nothing; the batch file was not run). The run below sets GRADLE_HOME to a directory that
does not exist and still gets the version, and starts the script through a symbolic link in another directory:
GRADLE_HOME=/nonexistent gradle --version | sed -n 3p
mkdir -p ln && ln -sf /opt/gradle/gradle-9.8.1/bin/gradle ln/gradle
ln/gradle --version | sed -n 3p
Gradle 9.8.1
Gradle 9.8.1
This is why advice that tells you to set GRADLE_HOME as a requirement is out of date, and why the PATH entry (or a symbolic
link such as /usr/local/bin/gradle) is all that matters. The GRADLE_HOME that Gradle documents as a configuration location
is the installation directory itself; see Files in the installation directory.
What the launcher script does
The 248-line bin/gradle script (wc -l on the 9.8.1 distribution) is a small POSIX program; reading it explains most installation problems. In order, it resolves
its own directory (APP_HOME, through symbolic links), then picks the Java: $JAVA_HOME/bin/java when JAVA_HOME is set (and
fails with an error if it is not a directory with a java in it), otherwise the java found on the PATH (and fails if there is
none). It then builds the command line of the client JVM from three sources, in this order: the script’s defaults,
JAVA_OPTS and GRADLE_OPTS. The defaults are -Xmx64m -Xms64m and the instrumentation agent, which is a lightweight client:
the real work happens in the daemon, a separate JVM that the client starts or reuses, whose options are not these. The last line
runs java -jar lib/gradle-gradle-cli-main-9.8.1.jar with the arguments you gave.
The consequence people trip over: JAVA_OPTS and GRADLE_OPTS set the options of the small client JVM; the daemon’s heap is
org.gradle.jvmargs in gradle.properties (default -Xmx512m; see
Build Environment & Properties), and
which JVM the daemon uses is the subject of Gradle Daemon.
The environment variables Gradle reads are listed in the build-environment page of the manual.
Package managers
The official page lists these, and warns that the version a package manager distributes "is not controlled by Gradle, Inc." For Linux it adds that the distribution packages "may distribute a modified version of Gradle that is incompatible or incomplete". None of the commands below was run here.
| System | Command | Notes |
|---|---|---|
SDKMAN! (Linux, macOS, Windows through WSL) |
|
The one manager the official page says Gradle itself deploys to ("Gradle is deployed and maintained by SDKMAN!"). A specific
version is |
Homebrew (macOS, Linux) |
|
The formula tracks the current release (the Homebrew API listed 9.8.1 on 2026-10-16) and depends on |
MacPorts (macOS) |
|
Listed on the official page. |
Chocolatey (Windows) |
|
|
Scoop (Windows) |
|
|
winget (Windows) |
|
SDKMAN! is the choice when you switch between Gradle versions on one machine, and it manages JDKs the same way
(sdk install java); a Homebrew or distribution package is convenient but may lag or change what is shipped. In every case the
project’s wrapper is what pins the version for the team, so the installed version matters only for gradle init and for
projects without a wrapper.
Source: SDKMAN!: Usage
Source: Homebrew: gradle formula
Verifying the installation
Open a new shell and run gradle -v (the same as gradle --version):
gradle --version
------------------------------------------------------------
Gradle 9.8.1
------------------------------------------------------------
Build time: 2026-10-07 16:57:46 UTC
Revision: 39daf39feb60a18b1b6ddccde53366522fef516f
Kotlin: 2.4.10
Groovy: 4.0.33
Ant: Apache Ant(TM) version 1.10.17 compiled on April 6 2026
Launcher JVM: 26 (Oracle Corporation 26+35-2893)
Daemon JVM: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home (no Daemon JVM specified, using current Java home)
OS: Mac OS X 27.0.1 aarch64
What each line tells you:
-
Build timeandRevisionidentify the exact build; the revision is the Git commit and matches thecommitIdof the JSON above. -
Kotlin,GroovyandAntare the versions embedded in this Gradle: the language versions your build scripts are compiled with and the Ant thatant.importBuildand the Ant builder use. Gradle 9.8.1 embeds Kotlin 2.4.10 and Groovy 4.0.33; Gradle 9.0.0 moved to Kotlin 2.2 and Groovy 4, 8.14.x embeds Kotlin 2.0 with language version 1.8 and Groovy 3 (not run here; the 9.0.0 release notes state the change). -
Launcher JVMis the JVM that ran the client, chosen byJAVA_HOMEor thePATH. -
Daemon JVMis the JVM the daemon would use. Outside a project that declares a daemon toolchain it is the same Java home, and the output says so ("no Daemon JVM specified, using current Java home"). In a project withgradle/gradle-daemon-jvm.propertiesthe line shows the criteria from that file instead; creating the file is covered with the daemon. -
OSis the platform, which matters when a build downloads platform-specific binaries.
JAVA_HOME changes the first two JVM lines. The run below points it at a JetBrains Runtime 17, the oldest JDK Gradle 9.8.1
accepts (the home directory is shown as <home>):
JAVA_HOME=<home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home gradle --version
...
Launcher JVM: 17.0.14 (JetBrains s.r.o. 17.0.14+1-b1367.22)
Daemon JVM: <home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home (no Daemon JVM specified, using current Java home)
OS: Mac OS X 27.0.1 aarch64
The first lines are the same as above and are cut (an ellipsis line marks the cut). gradle --version does not need a project,
and it does not start a daemon: gradle --status run right after it, with a fresh Gradle User Home, reported that no Gradle
daemons were running.
Files in the installation directory
Two things placed inside the installation affect every build that uses it, for every user of the machine. Both are rarely
right (a project should not depend on how a machine was set up), but they explain surprising behaviour on shared build servers
and they are the answer to "what is that init.d directory".
gradle.properties. The build-environment page lists the installation directory (GRADLE_HOME) as the lowest-precedence
location of gradle.properties, below the Gradle User Home and the project. The probe below reads a property named
tidepool.origin through a provider. The build file is the same project in both DSLs.
// build.gradle.kts
val tidepoolOrigin = providers.gradleProperty("tidepool.origin").orElse("not set")
tasks.register("showOrigin") {
val origin = tidepoolOrigin
doLast { println("tidepool.origin = ${origin.get()}") }
}
// build.gradle
def tidepoolOrigin = providers.gradleProperty('tidepool.origin').orElse('not set')
tasks.register('showOrigin') {
def origin = tidepoolOrigin
doLast { println "tidepool.origin = ${origin.get()}" }
}
Both projects have a one-line settings file (rootProject.name = "probe" or rootProject.name = 'probe'). They were run with
--configuration-cache, and the example is compatible with it: the value is captured as a provider in a local variable, never
through project inside the task action. Three runs per DSL, with a gradle.properties file added to and removed from the
installation directory between them (Kotlin DSL shown; the Groovy DSL project printed the same three lines):
gradle showOrigin -q --configuration-cache
echo "tidepool.origin=installation" > /opt/gradle/gradle-9.8.1/gradle.properties
gradle showOrigin -q --configuration-cache
echo "tidepool.origin=project" > gradle.properties # in the project directory
gradle showOrigin -q
tidepool.origin = not set
tidepool.origin = installation
tidepool.origin = project
The project’s value wins over the installation’s, as the documented precedence says. The Groovy DSL project printed
installation with the file present and not set after it was removed. (The file was removed after the experiment.)
init.d. Every .gradle file in init.d is an initialization script and runs at the start of each build; the folder of the
Gradle User Home (~/.gradle/init.d) works the same way for one user. Initialization scripts can be written in either
language; a Kotlin one is named *.init.gradle.kts. A one-line script in each language, placed in the installation’s init.d for one run:
// init.d/hello.init.gradle.kts
gradle.rootProject {
println("Kotlin init script from the installation ran for $name")
}
// init.d/hello.gradle
gradle.rootProject {
println "init.d script from the installation ran for ${it.name}"
}
Kotlin init script from the installation ran for probe
init.d script from the installation ran for probe
(The two scripts were run separately; each printed its own line once before the task output.) Init scripts, their use for organisation-wide repositories and plugins, and the Tooling API are in Init Scripts & the Tooling API.
Source: Gradle: Initialization Scripts
Bootstrapping a project with gradle init
Once Gradle is installed, gradle init creates a new build. The official description of the generated files: a build script, a
settings file, the Gradle Wrapper files and, for the non-basic project types, sample source code. The init task belongs to the
Build Init plugin, which is applied automatically to the root project of every build, so no build file is needed to run it. Its
options are listed by Gradle itself:
mkdir h && cd h
gradle help --task init
> Task :help
Detailed task information for init
Path
:init (registered by plugin 'org.gradle.build-init')
Type
InitBuild (org.gradle.buildinit.tasks.InitBuild)
Options
--comments Include clarifying comments in files.
--no-comments Disables option --comments.
--dsl Set the build script DSL to be used in generated scripts.
Available values are:
groovy
kotlin
--incubating Allow the generated build to use new features and APIs.
--no-incubating Disables option --incubating.
--insecure-protocol How to handle insecure URLs used for Maven Repositories.
Available values are:
ALLOW
FAIL
UPGRADE
WARN
--into Set the directory where the project is generated.
--java-version Provides java version to use in the project.
--overwrite Allow existing files in the build directory to be overwritten?
--no-overwrite Disables option --overwrite.
--package Set the package for source files.
--project-name Set the project name.
--split-project Split functionality across multiple subprojects?
--no-split-project Disables option --split-project.
--test-framework Set the test framework to be used.
Available values are:
cpptest
junit
junit-jupiter
kotlintest
scalatest
spock
testng
xctest
--type Set the type of project to generate.
Available values are:
basic
cpp-application
cpp-library
groovy-application
groovy-gradle-plugin
groovy-library
java-application
java-gradle-plugin
java-library
kotlin-application
kotlin-gradle-plugin
kotlin-library
pom
scala-application
scala-library
swift-application
swift-library
--use-defaults Use default values for options not configured explicitly
--no-use-defaults Disables option --use-defaults.
--rerun Causes the task to be re-run even if up-to-date.
Description
Initializes a new Gradle build.
Group
Build Setup
BUILD SUCCESSFUL in 295ms
1 actionable task: 1 executed
The options, with the behaviour the manual documents:
| Option | Meaning |
|---|---|
|
The kind of build. Without it Gradle infers |
|
|
|
|
|
The project name (default: the name of the directory) and the package of the generated sources (default: the project name). |
|
The Java version of the generated toolchain (see the toolchain block in the output below). For the Java types it is asked
for interactively; with |
|
Whether the sample is spread over several subprojects with shared convention plugins, or is a single application project. |
|
The directory to generate into. It is created if it does not exist. New in Gradle 9.5.0; before that you created the directory
and ran |
|
Take the default for every option not given, without prompting. |
|
Allow incubating APIs in the generated scripts, include or drop the explanatory comments, overwrite existing files, and (for the
|
Source: Build Init Plugin: Options
Interactive and non-interactive runs
gradle init with no options is meant for a terminal: Gradle lists the project types and asks about the DSL, the test framework
and the names. When there is no interactive console, Gradle does not ask; it uses what it can infer. Running it in an empty
directory with its input piped from a script and --console=plain, which is how a CI job or a script would run it, shows what
that gives:
mkdir interactive && cd interactive
printf '2\n1\n1\n1\nio.tidepool\n\n1\n' | gradle init --console=plain
find . -type f | sort
> Task :init
Learn more about Gradle by exploring our Samples at https://docs.gradle.org/9.8.1/samples
BUILD SUCCESSFUL in 294ms
1 actionable task: 1 executed
./.gitattributes
./.gitignore
./build.gradle.kts
./gradle.properties
./gradle/libs.versions.toml
./gradle/wrapper/gradle-wrapper.jar
./gradle/wrapper/gradle-wrapper.properties
./gradlew
./gradlew.bat
./settings.gradle.kts
The piped answers were ignored (no questions were printed) and the result is the basic type with the Kotlin DSL: an empty build
script, a settings file naming the project after the directory, the wrapper, an empty version catalog and a
gradle.properties whose only content is org.gradle.configuration-cache=true. So a scripted gradle init must pass its choices
as options; the interactive questions were not exercised here, because they need a terminal.
Two details of the generated files are worth knowing. The wrapper properties come out pointing at the official distribution:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
There is no distributionSha256Sum: init does not pin the checksum, so adding it (gradle wrapper
--gradle-distribution-sha256-sum=…) is the first hardening step; see
Gradle Wrapper. And gradle.properties already turns the
configuration cache on, the preferred mode since Gradle 9.0, so projects bootstrapped by 9.8.1 start with it enabled (it is still
off by default for projects that do not set it; Gradle 10 is announced to turn it on).
Source: Build Init Plugin: Sample usage
Bootstrapping tidepool: Kotlin DSL
The running scenario is a multi-module Java build (a model, an engine and a command-line interface). gradle init cannot produce
that exact layout, but the Java application type with --split-project produces the same shape (an application that depends on
libraries) and is the way the official tutorial starts. Non-interactive, with every choice given:
gradle init --type java-application --dsl kotlin --test-framework junit-jupiter \
--package io.tidepool --project-name tidepool --split-project --java-version 26 --into tidepool
find tidepool -type f -not -path '*/.gradle/*' | sort
> Task :init
Learn more about Gradle by exploring our Samples at https://docs.gradle.org/9.8.1/samples/sample_building_java_applications_multi_project.html
BUILD SUCCESSFUL in 275ms
1 actionable task: 1 executed
tidepool/.gitattributes
tidepool/.gitignore
tidepool/app/build.gradle.kts
tidepool/app/src/main/java/io/tidepool/app/App.java
tidepool/app/src/main/java/io/tidepool/app/MessageUtils.java
tidepool/app/src/test/java/io/tidepool/app/MessageUtilsTest.java
tidepool/buildSrc/build.gradle.kts
tidepool/buildSrc/settings.gradle.kts
tidepool/buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts
tidepool/buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts
tidepool/buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts
tidepool/gradle.properties
tidepool/gradle/libs.versions.toml
tidepool/gradle/wrapper/gradle-wrapper.jar
tidepool/gradle/wrapper/gradle-wrapper.properties
tidepool/gradlew
tidepool/gradlew.bat
tidepool/list/build.gradle.kts
tidepool/list/src/main/java/io/tidepool/list/LinkedList.java
tidepool/list/src/test/java/io/tidepool/list/LinkedListTest.java
tidepool/settings.gradle.kts
tidepool/utilities/build.gradle.kts
tidepool/utilities/src/main/java/io/tidepool/utilities/JoinUtils.java
tidepool/utilities/src/main/java/io/tidepool/utilities/SplitUtils.java
tidepool/utilities/src/main/java/io/tidepool/utilities/StringUtils.java
What was generated, with the explanatory comments left out for brevity:
// settings.gradle.kts
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "tidepool"
include("app", "list", "utilities")
// app/build.gradle.kts
plugins {
id("buildlogic.java-application-conventions")
}
dependencies {
implementation("org.apache.commons:commons-text")
implementation(project(":utilities"))
}
application {
mainClass = "io.tidepool.app.App"
}
// buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts
plugins {
java
}
repositories {
mavenCentral()
}
dependencies {
constraints {
implementation("org.apache.commons:commons-text:1.14.0")
}
testImplementation("org.junit.jupiter:junit-jupiter:6.0.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(26)
}
}
tasks.named<Test>("test") {
useJUnitPlatform()
}
Several things here differ from the conventions this section recommends, and are worth noticing because gradle init is where
most people meet Gradle first. The shared build logic lives in buildSrc, the older of the two ways to share it; the
recommended place is an included build named build-logic (see
Convention Plugins & Sharing Build Logic).
Repositories are declared in the convention plugin, not once in the settings file, and the dependency versions are in the
convention plugin (a constraint, and the JUnit coordinate) while the version catalog is empty. The toolchain is the --java-version
you gave (26 here), and the settings file applies the Foojay resolver so that a missing JDK can be downloaded for the toolchain.
The versions in the templates (JUnit 6.0.1, commons-text 1.14.0, the Foojay plugin 1.0.0) are fixed inside Gradle 9.8.1 and are
not refreshed at generation time, so they may be behind the latest releases: update them after generating. The next pages of the
section rework this layout into the conventions used for tidepool.
The generated project builds with the installed Gradle (the first run downloaded JUnit and commons-text from Maven Central). It
was built with --configuration-cache, and is compatible with it: the first build stored an entry and the later one reused it.
cd tidepool
gradle build --configuration-cache
gradle run --quiet
gradle build --configuration-cache
...
> Task :list:build
> Task :app:test
> Task :app:check
> Task :app:build
BUILD SUCCESSFUL in 7s
22 actionable tasks: 22 executed
Configuration cache entry stored.
Hello World!
> Task :app:assemble UP-TO-DATE
> Task :app:test UP-TO-DATE
> Task :app:check UP-TO-DATE
> Task :app:build UP-TO-DATE
BUILD SUCCESSFUL in 507ms
13 actionable tasks: 13 up-to-date
Configuration cache entry reused.
Only the last lines of the first build are shown (the ellipsis line marks the cut), then the output of gradle run --quiet, then
the last lines of the repeat build. The first build executed 22 tasks (a first-ever run is slower, because it also downloads the
dependencies and the build-logic plugins), the repeat build found everything up to date and reused the cache entry, and run
printed Hello World!. Running the generated
./gradlew instead would use the wrapper properties above, which download the 9.8.1 distribution from services.gradle.org;
it was not run here, because the installed 9.8.1 is the same version and no second download was made.
Bootstrapping tidepool: Groovy DSL
Only --dsl groovy changes; everything else is the same command:
gradle init --type java-application --dsl groovy --test-framework junit-jupiter \
--package io.tidepool --project-name tidepool --split-project --java-version 26 --into tidepool
find tidepool -name '*.gradle' | sort
tidepool/app/build.gradle
tidepool/buildSrc/build.gradle
tidepool/buildSrc/settings.gradle
tidepool/buildSrc/src/main/groovy/buildlogic.java-application-conventions.gradle
tidepool/buildSrc/src/main/groovy/buildlogic.java-common-conventions.gradle
tidepool/buildSrc/src/main/groovy/buildlogic.java-library-conventions.gradle
tidepool/list/build.gradle
tidepool/settings.gradle
tidepool/utilities/build.gradle
The Groovy convention plugins live in src/main/groovy instead of src/main/kotlin. The same files in Groovy syntax (single
quotes and no parentheses for method calls are the visible differences):
// settings.gradle
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
rootProject.name = 'tidepool'
include('app', 'list', 'utilities')
// app/build.gradle
plugins {
id 'buildlogic.java-application-conventions'
}
dependencies {
implementation 'org.apache.commons:commons-text'
implementation project(':utilities')
}
application {
mainClass = 'io.tidepool.app.App'
}
// buildSrc/src/main/groovy/buildlogic.java-common-conventions.gradle
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
constraints {
implementation 'org.apache.commons:commons-text:1.14.0'
}
testImplementation 'org.junit.jupiter:junit-jupiter:6.0.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(26)
}
}
tasks.named('test') {
useJUnitPlatform()
}
This project was built and run too, with --configuration-cache and the installed Gradle: the build succeeded (20 actionable
tasks executed), run printed Hello World! and the repeat build reported 13 up-to-date tasks and Configuration cache entry
reused. The differences between the DSLs are the subject
of Kotlin DSL and
Groovy DSL & Migrating to Kotlin.
Without --split-project, and with --use-defaults
Without --split-project the sample is one subproject. Even then the build has a subproject, named app, and a root project with
no code (Kotlin DSL; the Groovy output has the same shape):
gradle init --type java-application --dsl kotlin --test-framework junit-jupiter \
--package io.tidepool --project-name tidepool --no-split-project --java-version 26 --into tidepool
find tidepool -type f -not -path '*/.gradle/*' -not -name 'gradlew*' | sort
cat tidepool/gradle/libs.versions.toml
tidepool/.gitattributes
tidepool/.gitignore
tidepool/app/build.gradle.kts
tidepool/app/src/main/java/io/tidepool/App.java
tidepool/app/src/test/java/io/tidepool/AppTest.java
tidepool/gradle.properties
tidepool/gradle/libs.versions.toml
tidepool/gradle/wrapper/gradle-wrapper.jar
tidepool/gradle/wrapper/gradle-wrapper.properties
tidepool/settings.gradle.kts
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/version_catalogs.html#sec::toml-dependencies-format
[versions]
guava = "33.5.0-jre"
junit-jupiter = "6.0.1"
[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
Here the generated build uses a version catalog (libs.guava, libs.junit.jupiter), the layout the section recommends, and
declares the repository in the subproject. (The catalog is shown as plain text because the documentation viewer has no TOML
highlighting.) Version catalogs are covered in
Version Catalogs.
--use-defaults fills every option not given with its default and does not prompt. With only a type and a directory:
gradle init --use-defaults --type java-library --into lib
grep -rn "languageVersion" lib --include='*.kts'
lib/lib/build.gradle.kts:34: languageVersion = JavaLanguageVersion.of(21)
The default Java version of the generated toolchain is 21 in Gradle 9.8.1, not the JDK that runs Gradle (26 here), so the build
will need JDK 21 or let the Foojay resolver download one. Pass --java-version to choose.
Failure modes
gradle init refuses to write over a directory that already has files, and rejects unknown types. Both messages are as printed
(the scratch path is shortened to <scratch>):
gradle init --type java-application --dsl groovy --test-framework junit-jupiter \
--package io.tidepool --project-name tidepool --split-project --java-version 26 --into tidepool
> Task :init FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':init' (registered by plugin 'org.gradle.build-init').
> Aborting build initialization due to existing files in the project directory: '<scratch>/tidepool'.
* Try:
> Remove any existing files in the project directory and run the init task again.
> Enable the --overwrite option to allow existing files to be overwritten.
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
BUILD FAILED in 261ms
gradle init --type nonsense --into x
> Task :init FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':init' (registered by plugin 'org.gradle.build-init').
> The requested build type 'nonsense' is not supported. Supported types:
- 'basic'
- 'cpp-application'
- 'cpp-library'
- 'groovy-application'
- 'groovy-gradle-plugin'
- 'groovy-library'
- 'java-application'
- 'java-gradle-plugin'
- 'java-library'
- 'kotlin-application'
- 'kotlin-gradle-plugin'
- 'kotlin-library'
- 'pom'
- 'scala-application'
- 'scala-library'
- 'swift-application'
- 'swift-library'
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
BUILD FAILED in 600ms
--overwrite replaces existing files, which is the reason to run
init only in an empty directory or in a Git working tree where you can see what changed.
Converting a Maven build
In a directory with a pom.xml, gradle init infers the pom type and converts the effective POM (so inheritance, dependency
management and properties are resolved) to Gradle files; single and multi-module builds are supported. The manual states the
limits: Maven and Gradle differ fundamentally, so the result "should be considered a starting point for further refinement".
Compile-scope dependencies become api dependencies of the java-library plugin, because Maven exposes them to consumers, and
the manual encourages moving as many as possible to implementation. Plain http repository URLs are handled according to
--insecure-protocol. The conversion is walked through, with tidepool’s Maven build, in
Migrating from Maven; the comparison of the three
tools' migration paths is in Migrating Between Tools. The
conversion was not run on this page.
Troubleshooting an installation
The official troubleshooting page covers three installation problems. Each is reproduced here where it can be, with the message Gradle 9.8.1 prints.
gradle is not found. A shell without the bin directory on the PATH:
env PATH=/usr/bin:/bin sh -c 'gradle --version'
echo "exit status: $?"
sh: gradle: command not found
exit status: 127
Exit status 127 is the shell’s, not Gradle’s. The fix is the PATH entry above, in a new shell.
JAVA_HOME is set to an invalid directory. The launcher checks that $JAVA_HOME/bin/java exists:
JAVA_HOME=/nonexistent gradle --version
echo "exit status: $?"
ERROR: JAVA_HOME is set to an invalid directory: /nonexistent
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.
exit status: 1
The fix is to point JAVA_HOME at the directory that contains bin/java (on macOS, /usr/libexec/java_home -v 17 prints the home
of an installed JDK 17; here it printed the JetBrains Runtime used above), or to unset it. A frequent variant is a JAVA_HOME that points at a JDK that was upgraded or removed.
No Java at all. With neither JAVA_HOME nor a java on the PATH (the run uses a PATH that contains only a uname
link, which the script calls first):
env -i PATH=$PWD/nojava /bin/sh /opt/gradle/gradle-9.8.1/bin/gradle --version
echo "exit status: $?"
ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.
exit status: 1
Permission denied. The scripts must be executable; an installation copied with a tool that drops the mode bits fails like this
(a copy of the installation with chmod -x bin/gradle):
cp -R /opt/gradle/gradle-9.8.1 perm-copy
chmod -x perm-copy/bin/gradle
bash -c './perm-copy/bin/gradle --version'
echo "exit status: $?"
bash: ./perm-copy/bin/gradle: Permission denied
exit status: 126
The message comes from the shell (sh -c printed the same line with an sh: prefix); 126 is the shell’s "found but not
executable". The fix is chmod +x.
gradle --version works but every build fails. Then the installation is fine and a build script is broken. The official page
suggests gradle help, which runs configuration but no tasks: if it fails the same way, the problem is in the build
configuration; if not, it is in a task. A Java too old for Gradle 9 (a JDK 11, for instance) fails at start-up with an error that
names the Java version; that case was not run here. The wider catalogue of build failures, the dependency-resolution problems and
how to debug a build are in
Best Practices & Troubleshooting.
Installing the other two tools
| Apache Ant | Apache Maven | Gradle | |
|---|---|---|---|
Needs |
Java 8 or later |
Java 8 or later (Maven 4 release candidate: 17) |
Java 17 or later (the wrapper and clients: 8) |
What you install |
A ZIP or tar of scripts and JARs |
A ZIP or tar of scripts and JARs |
A ZIP of scripts and JARs |
Home variable |
|
None; computed from the script location ( |
|
Verification |
SHA-512 and PGP signature |
SHA-512 and PGP signature |
SHA-256 and PGP signature |
Per-project pinning |
None built in |
Maven Wrapper |
Gradle Wrapper (the normal way to run) |
Bootstrap a project |
Write |
|
|
The pages linked in the first row of the intro give the details; the pinning mechanisms are Maven Wrapper and Gradle Wrapper, and the broader comparison is Comparing Ant, Maven & Gradle.
Where the Gradle in Action book is outdated
Gradle in Action (Manning, 2014) covers installation for Gradle 1.7 in its second chapter. Four things in that start have
changed. Gradle 1.x ran on Java 5 or later; Gradle 9 needs Java 17 to run, and older JDKs are reached through toolchains. The book
sets GRADLE_HOME; the launcher does not read it and the PATH entry is enough. The book’s gradle setupBuild task was
incubating and was replaced by gradle init (the Build Init plugin), which now defaults to the Kotlin DSL (since 8.2) rather
than Groovy, supports many project types and a Maven conversion, and writes the wrapper. And the daemon, which the book presents
as opt-in with --daemon, has been on by default since Gradle 3.0. The release history is in
Versions, Compatibility & Release History.
Source: Gradle 3.0 release notes