Stupid Maven Build Cache Tricks
This living blog post provides useful tips and tricks to make working with the Maven Build Cache easier.
Leveraging the Maven Build Cache significantly improves Maven builds by providing Gradle-like incremental build functionality. The results are convention-driven builds that require less project-specific build expertise than other technologies (like Gradle) while still running expediently. This is especially true of sizeable polyglot enterprise builds.
Because the Maven Build Cache is relatively new, it can be hard to find examples and documentation to help get the most out of it. This living blog post captures our tips and tricks to get the most out of the Maven Build Cache.
The following topics are covered in this post:
Skip Cache While Still Building Cache Results
Incude Correct Cache Inputs
SNAPSHOT Plugin Cache Invalidation
Skip Build While Still Building Cache Results
It is sometimes useful to force build a component to debug or simply better understand what is happening. The most common approach for this is executing a build with -Dmaven.build.cache.enabled=false
flag.
However, using the -Dmaven.build.cache.skipCache=true
flag is generally preferable as it will force the build while still caching the results so they are available during subsequent builds.
Include Correct Cache Inputs
Challenge
We often see projects struggling to get the correct set of input files configured for cache consideration. For example, a project may wish to include some files outside the typical Maven src/main
or src/test
paths (which are both included by default in the cache configuration). Traditional Maven fileset patterns are often unsuccessfully attempted, such as:
<input>
<global>
...
<includes>
<include>mypath/**</include>
<include>yetanotherpath/*.py</include>
</global>
</input>
Solution
The input
for the build cache does not use fileset
patterns. Instead, you need to consider each entry as a recursive directory listed (though you can formally set recursive
to be false
on it as well) or single file name. You may also use the glob
setting on each include to set file patterns in the directories it will capture.
Example
The following example covers valid configurations. You can also validate that your files are being included by checking in the resulting ~/.m2/build-cache/v1.1/…/build-info.xml
file’s items
elements.
<input>
<global>
...
<includes>
<include>mypath</include>
<include recursive="false">nosubdirectories</include>
<include glob="*.py">yetanotherpath</include>
<include>rootDirectoryFile.txt</include>
</global>
</input>
SNAPSHOT Plugin Cache Invalidation
Challenge
When leveraging custom Maven Plugins as part of your build, you will likely want your build cache to invalidate portions of the build to reflect SNAPSHOT plugin updates. With Maven Build Cache 1.2.0, this is not easily supported through the plugin configuration sections of your maven-build-cache-config.xml
file. However, there is an easy workaround that is minimally invasive to your build.
Solution
Add a dependency to your pom.xml
to allow the Maven Build Cache’s support for dependency cache invalidation. By leveraging a scope of provided
, this will not impact transitive classpaths.
This approach also should be used for any dependencies within your plugins that should also invalidate your cache.
Example
The following example is pulled from the Habushu project. Habushu builds the habushu-maven-plugin
and then has some example / test projects. It’s very convenient to have these modules automatically rebuild anytime a new SNAPSHOT version of the plugin is updated. To accomplish this, the following dependency has been added to the habushu-mixology example project. It effectively triggers our desired cache invalidation to force rebuilds on every new SNAPSHOT version of habushu-maven-plugin
:
...
<build>
<pluginManagement>
<plugins>
<!--
Normal declaration of an upstream Maven plugin:
-->
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<version>${project.version}</version>
<extensions>true</extensions>
...
<plugin>
<plugins>
<pluginManagement>
</build>
<dependencies>
...
<!--
Adding this dependency ensures that any SNAPSHOT updates
trigger a new build (that is, they invalidate the cache).
-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<version>${project.version}</version>
<!--
The provided scope tells Maven to NOT add this dependency
in the resulting classpath (it expects to already be
there - which we take advantage of to have it go missing
in this scneario.
-->
<scope>provided</scope>
</dependency>
</dependencies>
...