When to (not) use mavenLocal() in your Gradle build script

When to (not) use mavenLocal() in your Gradle build script

Technical Blog
 
Image
Author
Bruno Berstel
Reading Time
5 minutes
 

Gradle as build tool

If you’re like me and you work in a company that uses Gradle as its build tool, you may have wondered, while writing the build scripts for your project, what exactly should go into that repositories block. And in particular whether or not to include mavenLocal(), and if so where: as the first element? the last one? The short answer is that you don’t want to include it.

Repositories

As you most probably know if you’re reading this, Gradle can leverage the Maven system of components (such as Java libraries) and repositories (such as Sonatype's Nexus). Using those, you just need to declare a dependency like the following in the build script of your Java application:
dependencies {
  implementation "com.fasterxml.jackson.core:jackson-core:2.9.9"
  // ...
}
and Gradle will take care of retrieving version 2.9.9 of the core library of Jackson provided by FasterXML, and include it in the appropriate class paths when compiling or running your application.

This, provided Gradle knows where to look for this library. And that is precisely the point of the repositories block, that could typically read as follows:
repositories {
  maven {
    url "http://nexus.mycompany.loc/repository/internal"
    mavenCentral()
  }
}
In the repositories block above, you state that when looking for a dependency, Gradle should first query the internal Nexus repository of your company, then the central Maven repository at https://repo1.maven.org/maven2/ .
The central repository is where Gradle will find all publicly available libraries, such as Jackson. Your company may also use an internal repository to store the software components shared between projects, or between modules of a project.

Imagine that your application, in addition to Jackson, uses code from a shared library of DTOs for your project. Its dependencies could then read:
dependencies {
  implementation "com.mycompany.myproject:common-dtos:1.0.0-SNAPSHOT"
  implementation "com.fasterxml.jackson.core:jackson-core:2.9.9"
  // ...
}
When compiling your application code, Gradle will look for the common-dtos and jackson-core libraries, first in its dependency cache in your home directory, then in your company’s internal repository, then in the central Maven repository.

Now, what about mavenLocal?

Up to here, everything works as expected. Yet, you may come across some Gradle build scripts with a repositories block that would read as follows:
repositories {
  maven {
    mavenLocal()
    url "http://nexus.mycompany.loc/repository/internal"
    mavenCentral()
  }
}
At first, this looks sensible: don’t query the internal or central repository if the dependency is already present in the local repository. Whatever “the local repository” may actually mean…

It turns out that the mavenLocal() repository is not the Gradle dependency cache. It is a repository where you can deploy components with ./gradlew publishToMavenLocal (assuming you use the maven-publish Gradle plugin ). Now, why would you like to deploy to a local repository? My take is, you don’t.

The typical case where I would consider using a local repository is to test a change in a shared library without publishing it to a repository visible to other people than me, so that I’m sure of what I publish, when I publish it. However, there are better ways to achieve that.

First case: the shared library that I want to test is in the same Gradle build as the code that uses it. (Let me remind that in Gradle parlance, a 'build' is the code base that is considered when running a command such as ./gradlew build. This is what Eclipse refers to as a workspace, or IntelliJ IDEA as a project.) In that case, where the common DTOs library is built from, say, a common-dtos subfolder of the project root, specifying the dependencies of the application as a project dependency ) is enough:
dependencies {
  implementation project(":common-dtos")
  implementation "com.fasterxml.jackson.core:jackson-core:2.9.9"
  // ...
}
Second case: the shared library is in a different Gradle build, typically its source code is in a different Git repository (let us name it common-components). Then I will use the composite build feature to instruct Gradle to use the version of the common components that I cloned, rather than the one from the repository, when building my application. To this end, assuming that I have cloned the two Git repositories next to each other on my disk (if not, it’s just a matter of setting up the right relative path), I simply have to add the following line in the settings.gradle file at the root of of my application:
includeBuild "../common-components"


Conclusion

Although you may come across some Gradle build scripts that include mavenLocal() in the list of repositories where to look dependencies from, I cannot think of a good reason to do so. There are use cases where mavenLocal() could look like a solution, and maybe it was at a time when project dependencies and composite builds were not available in Gradle. But now they are. Even worse: the official Gradle documentation warns about different issues that could arise when using mavenLocal().

Originally published March 27, 2020, modified June 22, 2020

Keep up to date with our Medium blog .


Related Resources

Download the DOC Datasheet from DecisionBrain. Learn more about the important features included in DOC 4.0. this included components, customers and use cases....

ROADEF rewarded DecisionBrain twice with ‘Les pros de la RO’ for the best OR project of the year: In 2018 for our Bike Sharing Solution In 2015 for Multi-Vessel Optimization...

DecisionBrain participated as a Partner Company at ROADEF 2020 in Montpellier. ROADEF (Société Française de Recherche Opérationnelle et d'Aide à la Décision) is an association...