Make the Gradle wrapper more convenient to use from the command-line

Currently, there are two ways that you can run Gradle from the command-line:

One option is to run the ‘gradle’ command. The problem with this command is that it is not aware of the wrapper settings, and may end up running the build with an incompatible version of Gradle to what the build expects. Another downside of using this command is that you have to manually upgrade your Gradle installation whenever the version that your builds use changes. This becomes complicated when you need to run multiple builds, each of which uses a different version of Gradle.

The other option is to run the ‘gradlew’ command checked into the root of the source tree. This addresses the problems with the ‘gradle’ command, as now the correct version of Gradle is automatically downloaded and installed for you. However, this approach has its own downsides, as now it is awkward to work in subdirectories of the build or to work with multiple builds.

For example, now rather than cd-ing to some subdirectory ‘some/sub/dir’ and running ‘gradle test’, you need to run ‘…/…/…/gradlew test’ from the subdirectory or ‘./gradlew -p some/sub/dir test’ from the root directory.

This feature is to add a third command that will eventually become a replacement for the ‘gradle’ command. It will live in the Gradle distribution, so you can add it to your ‘$PATH’ environment variable, which means that it will be convenient to use. It will understand the wrapper settings, so that it always uses the correct version of Gradle.

See GRADLE-1378 for more details.

Implementation

This command should be called ‘gradlew’ and should live in the ‘bin’ directory of the distribution, alongside the ‘gradle’ command. When invoked, this command should do the roughly the same as the tooling API’s ‘DistributionFactory’ to load up the wrapper settings and download the distribution. It can then use the ‘WrapperExecuter’ to invoke Gradle.

Extra credit

Create a minimal Gradle distribution that includes just this command and it’s runtime dependencies. This means that I can download and install a small Gradle launcher that is a few megabytes in size. I add this launcher to my path and it takes care of locating, downloading and installing all the other things that are required to run a Gradle build.

1 Like

I have a question. I might be misinterpreting things but please bear with me. Suppose this gets done for version 1.7. This means that I download, say, gradle-1.7-boostrapper which contains the minimum jars and a single script - gradlew - that calls into these jars to find the gradle-wrapper.properties somewhere in the project tree, read it, download the correct distro and calls it to execute the build. My concern is - this means that the 1.7 version of the script and the jars will be responsible for discovering the newer wrapper configurations, downloading the newer distributions, and calling them. What if the newer versions, say, gradle-2.5, change the *.properties file syntax, or ditch the file altogether in favor of some Groovy script, or do whatever you guys come up with that is better than now but different (like the url doesn’t contain the md5 hash encoded as base-32 number, as it is now)? The ‘old’ 1.7 gradlew command in my PATH will probably break trying to resolve gradle-2.5, right? Maybe in a nice way - when the exception and its message is good or it bangs loud enough - but maybe in a really obscure way, probably depending on the changes in the wrapper process. The ‘fix’ will be to download and install the new gradle-x.y-bootstrapper so that the gradlew command in PATH is the new one and knows how to deal with the ‘new’ way of configuring things.

Wasn’t the wrapper all about getting rid of such versioning problems? As far as I understand it, the current wrapper doesn’t have such problems - the script, the jar and the properties file is generated for a specific version of Gradle by a specific version, and so it will always know how to correctly parse the settings, how to download the correct distro and call it. The wrapper files for a project might configure an older version than the version that generates them (gradle-1.6 generates wrapper files for version 1.2, but I guess this is pretty unusual?), but it is easy to support in a backward compatible way - the newer gradle-wrapper.jar knows how to download / call the older version. My concern with the proposal is about forwad-(in)compatibility. The current way seems more reliable?

The gradle-minimal installation could also replace itself during the process, but I’m not sure how well it will work in systems like Windows that don’t allow deleting / replacing currently open files (as far as I know, I might be wrong here and would like to be corrected!). Having always the very latest version shouldn’t be a problem as newer versions will most likely be able to call into older ones.

On the other hand, a script like the one by Chris Beams that only searches for the gradlew script upward in the project structure seems much simpler an alternative, that won’t have the versioning issues. Of course, it may be that at some point there will be no gradlew script or it will be called differently, but it seems much less probable that incompatible changes in the future that older gradle distros can’t possibly know about today. (I do understand that each user would have to download and install this script himself…).

Am I making any sense, or have I misunderstood the whole feature?

wujek

Good points.

There are a few problems with the searching for scripts approach:

  • It doesn’t really solve the problem. For example, we might change to use a different naming scheme for the scripts, then the solution stops working. - It doesn’t help when people cannot check executable files into source control, but can check in the wrapper meta-data. - It doesn’t solve the problem for the tooling API, which also is affected by the same problem. - It doesn’t allow a build to be run embedded in the launching process, as the cross-version API is to run a script. - Doesn’t allow any kind of rich interaction between build and the launcher process.

Instead, we intend to solve this problem using stable APIs and protocols between the pieces. The plan is to bust the Gradle startup code into several smaller pieces that can each be separately installed, downloaded or checked-in, so that you can compose your build runtime in a way that makes sense for your environment.

More details are available here: https://github.com/gradle/gradle/blob/master/design-docs/custom-build-runtimes.md

Is there any chance we could have something that works in the meantime with the current wrapper implementation? I am using something like this: https://gist.github.com/ramonza/7229387

The point is that it allows me to invoke ‘gradlew’ without horrible relative paths to my ‘gradlew’ script in the project. Actually, I have this in a function in my ‘.bashrc’, if implemented like that it also gives us the opportunity to add Bash completion to the command. I haven’t tried this though.

I think a simple addition where gradlew is added which will honour the gradlew settings if present is a good start. Providing a ‘minimal’ gradle install that can create projects and wrapper configuration would make a lot of sense.

I would also like to suggest a wrapper setting that will specify a minimum version but use later version if present.