Michael HönnigMichael Hönnig

Listen to an audio recording of this blog post:

The gradle wrapper can download and verify a specified version of gradle for a project without the need of a local gradle executable. The gradle documentation is suggesting to check it into the VCS (version control system, e.g. git). This article discusses the advantages and disadvantages of keeping the gradle wrapper in a VCS and presents an alternative.

The Idea Behind the Gradle Wrapper

To build a gradle project, you need a gradle executable; to be more precise we need a jar-file which can be run by the JVM as an executable. The gradle-distribution is not just almost 100 MB in size (for gradle 6.7.1), but it’s also a binary file. Thus, we definitely don’t want to have it checked into our VCS.

Furthermore, our build file might need a specific version of gradle. Even if it was compatible with a relatively wide range of gradle versions, we usually want exact versions to be used in a build system for repeatability of the build. So, all developers should use exactly the same version of gradle.

That’s why the gradle wrapper was introduced. And, with just 55 KB (for gradle 6.7.1) it’s a relatively small jar-file. Thus, we could check it into our VCS; exactly how its suggested by the gradle team:

To make the Wrapper files available to other developers and execution environments you’ll need to check them into version control. All Wrapper files including the JAR file are very small in size. Adding the JAR file to version control is expected. Some organizations do not allow projects to submit binary files to version control. At the moment there are no alternative options to the approach.
— from the 'https://docs.gradle.org/current/userguide/gradle_wrapper.html[_gradle_ documentation]'

"No alternatives"? That’s not quite true, there is actually an alternative, or at least a variation, as I’ll show later.

Another big advantage of using the gradle wrapper is, that the project can even be build by developers without any gradle version installed on their machine at all. The approach presented below cannot fix that problem.

How the Gradle Wrapper Works

When the gradle wrapper is installed in a project, either a provided Windows batch (gradlew.bat) or shell script (gradlew) is used to start it in a locally installed JVM. It then reads the gradle/wrapper/gradle-wrapper.properties configuration file, and checks if the specified gradle distribution is available; if not, it’s downloaded into the specified gradle cache directory. Finally, it starts the actual build using this specific gradle version.

example gradle/wrapper/gradle-wrapper.properties file
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

The Problems with Binary Files

Git and most other VCS are designed to store line oriented text files, like typical source code. They can’t create diffs for new versions of binary files, thus each new version of a binary file results in a complete new copy, taking up a lot of space.

Other than picture files, for example, which are often checked into a VCS, despite being binary files as well, executable code has another issue: It could contain malicious code, but as the VCS cannot display the changes even less in a human readable format, such dangers are not very obvious. That’s why some companies even disallow checking in binary files with executable code.

Even wrappers from very old gradle versions can still be used for projects which require a new gradle version. Therefore, you usually don’t need to upgrade the wrapper binary itself too frequently.

An Alternative

Instead of checking in the gradle wrapper binary jar-file, you can generate it after checking out a copy of the project, .e.g. in a shell on a UNIXoid-system this is done with the following command:

gradle wrapper --gradle-version 6.7.1

But in this case the developer has to know the correct version to be used.

This problem can be fixed, though, by specifying the version for the wrapper task:

build.gradle
tasks.named('wrapper') {
    gradleVersion = "6.7.1"
}
build.gradle.kts
import org.gradle.api.tasks.wrapper.Wrapper

tasks.named<Wrapper>("wrapper") {
    gradleVersion = "6.7.1"
}

Now, the wrapper can be generated simply like this:

gradle wrapper

Additionally, the generated files should be excluded from your VCS. For git e.g. this could be done this way:

.gitignore
# Ignore Gradle-wrapper files
/gradlew
/gradlew.bat
/gradle/wrapper/

...
In either case, if you don’t check the gradle wrapper into your VCS, all developers need at least some gradle version installed on their machine.

Conclusion

The gradle wrapper is a helpful feature of the gradle build system. You could build a freshly checked out project right away, without the need of a locally installed gradle version at all.

If you don’t want to check in executable binary code, the gradle wrapper can still be utilized, but at least any gradle version must be locally installed.

Any comments about this article are welcome on Twitter.