Chapter 19. Continuous build

Continuous build is an incubating feature. This means that it is incomplete and not yet at regular Gradle production quality. This also means that this Gradle User Guide chapter is a work in progress.

Typically, you ask Gradle to perform a single build by way of specifying tasks that Gradle should execute. Gradle will determine the the actual set of tasks that need to be executed to satisfy the request, execute them all, and then stop doing work until the next request. A continuous build differs in that Gradle will keep satisfying the initial build request (until instructed to stop) by executing the build when it is detected that the result of the previous build is now out of date. For example, if your build compiles Java source files to Java class files, a continuous build would automatically initiate a compile when the source files change. Continuous build is useful for many scenarios.

19.1. How do I start and stop a continuous build?

A continuous build can be started by supplying either the --continuous or -t switches to Gradle, along with the list of tasks, switches and arguments that define the work to do. For example, gradle build --continuous. This will have the same effect as running gradle build, but instead of Gradle exiting when done, it will wait for changes to the build inputs. When a change occurs, gradle build will be automatically executed again and the process repeats.

If Gradle is attached to an interactive input source, such as a terminal, the continuous build can be exited by pressing CTRL-D (On Microsoft Windows, it is required to also press ENTER or RETURN after CTRL-D). If Gradle is not attached to an interactive input source (e.g. is running as part of a script), the build process must be terminated (e.g. using the kill command or similar). If the build is being executed via the Tooling API, the build can be cancelled using the Tooling API's cancellation mechanism.

19.2. What will cause a subsequent build?

Task file inputs

Task implementations declare their file system inputs by annotating their properties with InputFiles and other similar annotations. Please see Example 14.24, “Declaring the inputs and outputs of a task” for more information.

At this time, only changes to input files to the previously executed tasks will initiate a build. Furthermore, only changes that occur after the build has completed (and Gradle has prompted that it is waiting for changes) are noticed. No other changes will initiate a build. For example, changes to build scripts and build logic will not initiate build. Likewise, changes to files that are read during the configuration of the build, not the execution, will not initiate a build. In order to incorporate such changes, the continuous build must be restarted manually.

Consider a typical build using the Java plugin, using the conventional filesystem layout. The following diagram visualizes the task graph for gradle build:

Figure 19.1. Java plugin task graph

Java plugin task graph

The following key tasks of the graph use files in the corresponding directories as inputs:

compileJava
src/main/java
processResources
src/main/resources
compileTestJava
src/test/java
processTestResources
src/test/resources

Assuming that the initial build is successful (i.e. the build task and its dependencies complete without error), changes to files in, or the addition/remove of files from, the locations listed above will initiate a new build. If a change is made to a Java source file in src/main/java, the build will fire and all tasks will be scheduled. Gradle's incremental build support ensures that only the tasks that are actually affected by the change are executed.

If the change to the main Java source causes compilation to fail, subsequent changes to the test source in src/test/java will not initiate a new build. As the test source depends on the main source, there is no point building until the main source has changed, potentially fixing the compilation error. After each build, only the inputs of the tasks that actually executed will be monitored for changes.

Continuous build is in no way coupled to compilation. It works for all types of tasks. For example, the processResources task copies and processes the files from src/main/resources for inclusion in the built JAR. As such, a change to any file in this directory will also initiate a build.

19.3. Limitations and quirks

There are several issues to be aware with the current implementation of continuous build. These are likely to be addressed in future Gradle releases.

19.3.1. Requires Java 7 or later

Gradle uses the JDK's WatchService to receive notification of changes to files between builds. This API was introduced in Java 7. As such, Gradle's continuous build is not currently supported when building with Java 6.

19.3.2. Performance and stability on Mac OS X

The JDK file watching facility relies on inefficient file system polling on Mac OS X (see: JDK-7133447). This can significantly delay notification of changes on large projects with many source files.

Additionally, the watching mechanism may deadlock under heavy load on Mac OS X (see: JDK-8079620). This will manifest as Gradle appearing not to notice file changes. If you suspect this is occurring, exit continuous build and start again.

19.3.3. Changes to symbolic links

  • Creating or removing symbolic link to files will initiate a build.

  • Modifying the target of a symbolic link will not cause a rebuild.

  • Creating or removing symbolic links to directories will not cause rebuilds.

  • Creating new files in the target directory of a symbolic link will not cause a rebuild.

  • Deleting the target directory will not cause a rebuild.

19.3.4. Changes to build logic are not considered

The current implementation does not recalculate the build model on subsequent builds. This means that changes to task configuration, or any other change to the build model, are effectively ignored.