Introducing Continuous Build Execution

Optimizing the build-edit-build loop

In the past, we’ve recommended that you enable the Gradle Daemon (and parallel execution, with some caveats) to get the best performance out of Gradle. We’ve also talked about using incremental builds to speed up your build-edit-build feedback loop by skipping unnecessary work. Now there’s another optimization available—one that allows you to get out of the way and let Gradle start the build for you.

As of 2.5, Gradle supports continuous build execution, which will automatically re-execute builds when changes are detected to its inputs. There have been a few community plugins that add support for a Gradle “watch” mode that do something similar.

With Maven, the same watch functionality needs to be implemented for each plugin or you have to use a plugin that has a predefined set of goals. We wanted to do better than that. We wanted any plugin to be able to leverage the power of continuous builds without having to supply additional information. We also wanted the set of tasks to execute to be completely ad-hoc. Since Gradle needs to know a task’s inputs and outputs for incremental builds, we had all the information necessary to start watching for changes.

Using continuous build

Continuous build can be used with any task or set of tasks that have defined inputs and outputs. If you’re using well-behaved tasks, this shouldn’t be a problem for most builds. If you find that your build isn’t rebuilding with continuous build as you think it should, it could point to a problem in your build script.

Command-line option

You enable continuous build with the -t or --continuous command-line option along with whichever tasks you want to run (we call these task selectors). At least one task that runs needs to define inputs to enter continuous build mode.

For example, on a typical Java project,

$ gradle -t test 

would enable continuous build and re-run tests any time the main sources or test sources change.

We’re not limited to a single task, so we could also re-run tests and FindBugs on the main sources using 

$ gradle -t test findBugsMain.

Determining when to run another build

When you run Gradle with the continuous build option, Gradle executes the build as usual, except Gradle also registers the inputs to all tasks with a file watch service. Even tasks that are UP-TO-DATE will have their inputs recorded, so all inputs can be considered when triggering a new build. This means that you don’t have to start from a clean build for Gradle to know which inputs could change in continuous build mode.

After the end of the build, Gradle will start watching for file system changes based on the collected inputs. The Gradle command-line interface will display the message Waiting for changes to input files of tasks on the console and will wait for changes to inputs. If any of the input files are changed or deleted, Gradle will execute another build with the identical set of task selectors. Gradle can detect changes to simple files (deleted, modified) and changes to directories (deleted or new files).

See a demo of this in action:

Exiting continuous build

Once Gradle is running in continuous build, it will not exit, even if the build is not successful. To get out of continuous build, you should use Ctrl-D to cancel the build. On Microsoft Windows, you must also press ENTER or RETURN after Ctrl-D.

If you use Ctrl-C, Gradle will exit abruptly and also kill the Gradle Daemon.

UPDATE: As of Gradle 3.1, Ctrl-C no longer kills the Gradle Daemon.

Limitations

The User Guide chapter describes all limitations and quirks with continuous build.

Requires Java 7 or better

Gradle uses Java 7’s WatchService to watch for changes to inputs. This functionality is only available on JDK 7 or later.

Mac OS X performance

For GNU/Linux and Microsoft Windows, the file system change events are provided through a kernel service. For Mac OS X, Java falls back to a polling-based system. This means on Mac OS X only, change detection on a very, very large number of input files may be delayed and, in some cases, cause a deadlock. Both of these issues are tracked as JDK bugs: JDK-7133447 and JDK-8079620.

Changes to build scripts

Gradle doesn’t consider changes to your build logic when in continuous build mode. Build logic is created from build.gradle, settings.gradle, gradle.properties and other sources. If you make changes to your build scripts, you’ll have to exit continuous build and restart Gradle. Future versions of Gradle will make it easier to describe inputs to your build logic so that continuous build can work with this as well.

Future improvements

In addition to mitigating some of the limitations with the current implementation, there are other interesting things we can use continuous build to accomplish.

Right now, there are not any supported, public ways of managing a process started by Gradle that needs to exist between builds. Gradle expects that a process started (e.g., via Exec) will exit as part of the build.

In the next release (2.6), Play support is coming to Gradle, and with that you’ll be able to start Play applications in a separate JVM for local development. With continuous build enabled, Gradle will hot-reload the Play application whenever classes or assets are changed. The Play plugin accomplishes this by registering the Play JVM with Gradle in a way that survives between builds.

We want to eventually evolve this Play specific reload functionality into a general feature, so plugins can have their own “hot-reload”-like behavior.

Another opportunity for improvement is up-to-date checking. For very large projects, up-to-date checking can be time consuming for the no-op case. When looking for out-of-date files, Gradle must scan entire directories or recalculate file checksums. When using continuous build, Gradle must already keep track of file and directory changes, so in some cases, Gradle may be able to skip checks for files that are known to have not changed.

Feedback

Please let us know on the forums if you run into any surprises with this new feature.