Chapter 6. Multiproject builds

 

This chapter covers

  • Organizing a project’s source code into subprojects
  • Modeling the build for a multiproject hierarchy
  • Configuring project behavior with the Project API
  • Declaring dependencies between projects
  • Customizing your build with the Settings API

The code base of every active software project will grow over time. What started as a small project with a handful of classes may quickly become a collection of packages and classes with different responsibilities. To improve maintainability and prevent tight coupling, you’ll want to group code into modules based on particular functionality and logical boundaries. Modules are usually organized hierarchically and can define dependencies on each other. The build tool needs to be able to cope with these requirements.

Gradle provides powerful support for building modularized projects. Because every module in Gradle is a project, we call them multiproject builds (as opposed to Maven’s use of multimodule builds). This chapter explains techniques for modeling and executing a multiproject build with Gradle. By the end of the chapter, you’ll know how to apply the technique that best fits the needs of your own project and model your build appropriately.

6.1. Modularizing a project

In enterprise projects, the package hierarchy and class relationships can become highly complex. Separating code into modules is a difficult task, because it requires you to be able to clearly identify functional boundaries—for example, separating business logic from data persistence logic.

6.2. Assembling a multiproject build

The overarching project located in the top-level directory is called the root project, and it has its own right to exist in a multiproject build. It coordinates building the subprojects and can define common or specific behavior for them. Figure 6.3 gives you a graphical overview of the hierarchical project structure you’re going to achieve.

So far we’ve only dealt with the Gradle configuration of single-project builds. You saw that separating your code into multiple projects wasn’t all that hard. What’s missing is the build support that represents the root project and its subprojects. The declaration of subprojects in a multiproject build is done via the settings file.

6.3. Configuring subprojects

The web subproject is the only project that declares external dependencies. The project type derives from the other subprojects in that it needs to build a WAR archive instead of a JAR and uses the Jetty plugin to run the application.

In this section, you’ll learn how to define specific and common behaviors for projects in a multiproject build, a powerful way to avoid having to repeat configuration. Some of the subprojects may depend on the compiled source code of other projects—in your application, the code from the project model is used by the repository project. By declaring project dependencies, you can make sure imported classes are available on the classpath. Before you fill your empty build.gradle file with life, we’ll review methods of the Project API I haven’t shown you yet but that are relevant in the context of multiproject builds.

6.4. Individual project files

The multiproject build you’ve defined so far only consists of a single build.gradle file and the settings.gradle file. As you add new subprojects and tasks to your build.gradle file, code maintainability will suffer. Having to wade through pages and pages of code to extend or modify your build logic is no fun. You can drive the separation of concerns even further by creating individual build.gradle files for each of the projects.

6.5. Customizing projects

The standard Gradle build filename is build.gradle. In a multiproject build with many subprojects, you may want to be more expressive when it comes to naming your build files. Editing multiple build.gradle files in parallel and constantly switching between them easily becomes confusing when you’re using an IDE. This section will explain how to configure your project to use custom build filenames.

The key to making this project structure work again lies in the settings file. It provides more functionality than just telling your build which of the subprojects should be included. In fact, it’s a build script itself that’s executed during the initialization phase of the build lifecycle. With the help of the Settings API outlined in section 6.2.2, you have direct access to the root project and its children. The following listing shows how to iterate over all subprojects to assign a custom build filename. In addition, you also set a custom name for the root project.

6.6. Summary

Gradle treats every module as a separate project. Every project can declare dependencies on other projects. Gradle’s toolbox provides extensive support for modeling and executing multiproject builds either as hierarchical or flat project structures. You learned that the settings file, executed during the initialization phase of the build lifecycle, determines which of the projects should be part of the build.

The organization of your multiproject build code is very flexible. You can choose to use a single master build script, individual build scripts per project, or a mixed approach. The route you take depends on the requirements of your project. However, organizing build logic into individual scripts improves maintainability of your code the more subprojects you add to your build.

sitemap