Gradle has the ability to check if a task is up to date and more importantly skip the task if it finds that the task input is unchanged compared to the latest build. This feature results in significant build time reductions, but as we will see in this blog post we can do even better if we know what module(s) we would like to build. A couple of years ago, I wrote a similar blog post about how you can work more efficiently with Maven by being selective when building your multi-module project. This blog post deals with the same issue, however this time Gradle is used.
Let us assume that we have a project structure with the modules below and that we will add a new feature to the
parent | ├── a │ ├── a1 │ └── a2 | ├── b │ ├── b1 (module under development, depends on a1) │ └── b2 | ├── itest (integration tests) | | │ ├── a-itest │ │ ├── a1-itest (depends on a1) │ │ └── a2-itest (depends on a2) │ │ │ └── b-itest │ ├── b1-itest (depends on b1) │ └── b2-itest (depends on b2)
If you open a terminal in the
parent folder and execute
gradle build, then all projects will be compiled, the tests will be executed, integration tests will be executed and so on. All is good, but it may take a while before all work has been completed. In the following example, we will see how it is possible to work on the
b1 module without rebuilding the non-dependent
When you update your code from version control before you start working on a new feature, it is a good idea to make sure that the module you will work with is in a good state. If you trust your continuous integration server, it should be sufficient to build just that specific module and the modules that it depends on. The Java plugin’s
build task does exactly that.
There are a few different ways to specify which module should be built. For example, if you would like to build
b1 you can target it by either its (sub)module name followed by the
$ gradle :b:b1:build
or you can target the module by its directory location if you use the
$ gradle -p b/b1 build
It is also possible to execute the
build.gradle file directly by using the
$ gradle -b b/b1/build.gradle build
This last approach is discouraged in multi-module projects (more information can be found in the Selecting which build to execute chapter in the Gradle user manual).
Please note that only a limited set of tasks necessary to generate the required dependencies are executed. In particular, no tests are executed on the upstream modules, i.e.
a1 will be built without verification of its accommodating unit tests.
If you would like to build more than one module, just add the modules and their corresponding tasks:
$ gradle :b:b1:build :b:b2:build
buildNeeded is similar to the
build task in that it builds a module and all upstream modules that it depends on. The difference is that it performs a full build of the upstream modules, including tests.
$ gradle b:b1:buildNeeded
b1 will be built fully.
When you have finished the implementation of your
b1 feature, you would probably like to make sure that all downstream modules that depend on
b1 still work. To build the
b1 project and its depending project(s), in this example represented by the
b1-itest project you can use the
$ gradle b:b1:buildDependents
Avoid Rebuilding Project Dependencies
What about those occasions when you are in the middle of a refactoring, there are no upstream project changes and you know for sure that you have broken downstream dependencies, but you would like to know if just the specific module compiles and its tests pass? Enter the
$ gradle -a b:b1:build
Consequently, this module build will fail if the upstream dependencies have not been built previously or if you have executed
gradle clean. Likewise, you may get a false positive build if an upstream dependency has been updated and you still build against the old binary version.