02 September 2014

One of the great things (IMHO) about Gradle is that it does a pretty good job of choosing sane conventions. That being said, one place that seems to not always be a great choice is Gradle's convention to visit sub-projects in alpha-numerical order. Let’s say that you have the following project structure:

 |_ A
 |_ B
    |_ C
    |_ D
 |_ E

Based on the alpha-numerical ordering, you would probably expect Gradle to visit the projects in the following order: A, B, B:C, B:D, E. However, this is not the case. Gradle will visit the sub-projects in alpha-numerical order, within each level. What you end up with is: A, B, E, B:C, B:D. Normally, this probably isn’t a big deal. This does become a big deal when one of those sub-projects builds a distribution that needs to include output from all of the other sub-projects. Such is the case when building a fully-repackaged, executable JAR file with Spring Boot. The repackaged JAR built by the Spring Boot Gradle Plugin includes all dependency JAR files required to execute the application. Let’s suppose in our example above, the sub-project E’s build.gradle script includes the following dependency block:

dependencies {
    compile module(':A')
    compile module(':B:C')
    compile module(':B:D')

What we would like to happen is that when we build project "E", the JAR file should include the JAR’s produced by building "A", "B:C", and "B:D". What actually happens is that only "A"'s JAR gets included, because of the ordering outlined earlier in this post. The way to correct this behavior and to ensure that all other sub-projects build their JAR files prior to "E"'s JAR task executing is to add the following to "E"'s build.gradle script:

// Ensure that all other JARs are built before the service is packaged.
project.tasks.jar.dependsOn(rootProject.getSubprojects().findAll { subproject -> subproject.name != project.name }.collect { subproject -> "${subproject.getPath()}:build" })

The logic above finds every other sub-project in the project and adds that sub-project’s "build" task as a dependency that first must execute before the current project’s JAR task can execute. This ensures that all other projects have built their JAR files before Spring Boot can create the re-packaged JAR. You can apply this same trick to any other scenario where the execution of tasks in other sub-projects (regardless of depth) must occur before a certain sub-project’s task(s).

comments powered by Disqus