Core Java

How to manage dependencies in a Gradle multi-project build

I’ve been aĀ fanĀ of the Gradle build tool from quite early on. ItsĀ potential was clear even before the 1.0 version, whenĀ changesĀ were regularly breaking. Today,Ā upgrading rarely cause surprises. The tool has becomeĀ mature and performs well.

Gradle includes aĀ powerful dependency managementĀ system that can work with Maven and Ivy repositories as well as local file system dependencies.

During my work with Gradle I’ve come to rely onĀ a pattern for managing dependencies in a multi-project build that I want to share. This pattern consists of twoĀ key practices:

  1. Centralize dependency declarations in build.gradle
  2. Centralize dependency version declarations in gradle.properties

Both practices are examples of applyingĀ software development best practices like DRYĀ to the code that makes up the Gradle build. Let’s look at them in some more detail.

Centralize dependency declarations

In the root project’s build.gradle file,Ā declare a new configuration for each dependency used in the entire project. In each sub-project that uses the dependency, declare that the compile (or testCompile, etc) configuration extends the configuration for the dependency:
Root project build.gradle

subprojects {
  configurations {
    commonsIo
  }
 
  dependencies {
    commonsIo 'commons-io:commons-io:2.5'
  }
}

Sub-project build.gradle

configurations {
  compile.extendsFrom commonsIo
}

By putting all dependencyĀ declarations in a single place, we know where to look and we prevent multiple sub-projects from declaring the same dependency with different versions.

Furthermore, the sub-projects are now more declarative, specifying onlyĀ whatĀ logical components they depend on, rather than all the details ofĀ how a component is built up from individual jar files.Ā When there is a one-to-one correspondence, as in the commons IO example, that’s not such a big deal, butĀ the difference is pronounced when working with components that are made up of multiple jars, like the Spring framework or Jetty.

Centralize dependency version declarations

The next step is to replaceĀ all the version numbers from the root project’sbuild.gradle file by properties defined in the root project’s

gradle.properties:

build.gradle

dependencies {
  commonsIo "commons-io:commons-io:$commonsIoVersion"
}

gradle.properties

commonsIoVersion=2.5

This practice allows you to reuse the version numbers for related dependencies. For instance, if you’re using theĀ Spring framework, you may want to declare dependencies on spring-mvcĀ and spring-jdbcĀ with the same version number.

There is an additional advantage of this approach. Upgrading a dependency means updating gradle.properties, while adding a new dependency means updating build.gradle. This makes it easy to gaugeĀ from a commit feed what types of changes could have been made and thus to determine whether a closer inspection is warranted.

You can take thisĀ a step further and put the configurations anddependencies blocks in a separate file, e.g. dependencies.gradle.

And beyond…

Having all the dependencies declared in a single location is aĀ stepping stone to more advanced supply chain management practices.

The centrally declared configurations giveĀ a good overview of all the components that you use in your product, the so-called Bill of MaterialsĀ (BOM). You can use the above technique, or useĀ the Gradle BOM plugin.

The BOM makes it easier to use a tool likeĀ OWASP DependencyCheck to check for publicly disclosed vulnerabilities in the dependencies that you use. At EMC, about 80% of vulnerabilities reported against our products are caused by issues in 3rd party components, so it makes sense to keep a security eye on dependencies.

A solid BOM also makes it easier to review licenses and their compliance requirements. If you can’t afford a tool like BlackDuck Protex, you canĀ write something less advanced yourself with modestĀ effort.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Gopalan R C
Gopalan R C
8 years ago

what if have more than one common dependencies?

Back to top button