Skip to content

Use Gradle or Maven

B. K. Oxley (binkley) edited this page Aug 14, 2024 · 11 revisions
Maven Gradle

Use Gradle or Maven

The choice between Gradle and Maven depends on your team, your broader ecosystem, and your project needs. In summary:

  • Gradle — your build script is written in Groovy or Kotlin; dynamic, imperative, and mutable; requires debugging your build on occasion, but less verbose than Maven's XML. Use of "parent" (umbrella) projects is possible but challenging. You can locally extend your build script either inline with build code, with project plugins, or with plugins from a separate project (perhaps shared across project for your team). If interested in custom plugins, read more here
  • Maven — your build scripts is written in XML; declarative and immutable; verbose but specific; it either works or not. Use of "parent" (umbrella) projects is simple with built-in support. You can locally extend your build with plugins from a separate project (perhaps shared across project for your team). If interested in custom plugins, read more here

For Modern Java/JVM projects, use Gradle or Maven unless your language provides you a native build tool (Clojure, Scala). The article doesn't cover alternative build tools: industry data shows Gradle or Maven are the build tools for most folks. Unless you find yourself in a complex monorepo culture (Google, etc.), or there are mandates from above, you need to select one of Gradle or Maven. However, for projects not using Gradle or Maven, you will still find improvements for your build herein (though details will differ).

For new projects, you may find Spring Initializr, mn from Micronaut, or JHipster, among many other project excellent starters, more to your liking: they provide you with starter Gradle or Maven scripts specific for those frameworks. That's great! This article should still help you improve your build beyond "getting started". You should pick and choose build features as makes sense to you and your circumstances.

This article offers no preference between Gradle or Maven. You need to decide with your team and circumstances. After picking your build tool, you might rename run-with-gradle.sh or run-with-maven.sh to just run.sh or similar.

Projects using Ant should migrate. It is true that Ant is well-maintained (the latest version dates from 2021). However, you will spend much effort in providing modern build tooling, and effort in migrating from Ant is repaid in smaller work for integrating modern tools. Data point: consider the number of Stackoverflow posts providing Gradle or Maven answers to those for Ant. Consider Ant builds to be no longer well-supported, and a form of Tech Debt.

Throughout, when covering both Gradle and Maven, Gradle will be discussed first, then Maven. This is no expressing a preference! It is neutral alphabetical ordering.

NB — Gradle Enterprise provides additional features for Maven as well such as build caching and build scans.

Cleaning up

Once you pick between Gradle or Maven, it is a good time to clean up. If you have cloned the project, some renames/deletions to consider:

You are ready to make great software.

Smart configuration

Both Gradle and Maven support a tiered approach to project configuration:

Take advantage of this! That means to lean on configuration settings that follow this sequence of preference:

  1. Command-line settings — use of -Dfoo=bar (both) or -Pfoo=bar (Gradle) so developers can make immediate changes locally.
  2. Environment settings — use of settings exported to the local shell/IDE (per developer) or to CI (for project builds for the team).
  3. Project file settings — careful not to share secrets in files committed to source!
  4. System settings — use of system configuration that goes across projects. A common example might be an environment variable for a shared corporate repository to pull dependencies from.

This project has a simple example providing only command-line and environment settings using the NVD API key for OWASP (pulling down CVE data for validating dependencies). In the examples Gradle and Maven check for settings in this order:

  1. Overrides from the command-line using owasp.nvdApiKey.
  2. Fallback to an environment variable using OWASP_NVD_API_KEY.
  3. Default to null or empty string which the build plugins for Gradle and Maven take to mean "no key provided".

Gradle

apiKey = findProperty("owasp.nvdApiKey") ?: System.getenv("OWASP_NVD_API_KEY")

Gradle is a bit subtle here, and you may need to experiment to find the right calls for achieving your prefered ordering of values and overrides (for example, providers.environmentVariable("OWASP_NVD_API_KEY") compiled, but did not behave as I hoped even though it is documented in the Gradle guide).

Maven

<owasp.nvdApiKey>${env.OWASP_NVD_API_KEY}</owasp.nvdApiKey>
<!-- and further down -->
<nvdApiKey>${owasp.nvdApiKey}</nvdApiKey>

This was easy and worked out of the box: the declarative syntax for Maven shines in this example.

Keeping up to date

Gradle

To update Gradle:

$ $EDITOR gradle.properties  # Update gradleWrapperVersion property
$ ./gradlew wrapper  # Update scripts and supporting files
$ ./gradlew wrapper  # Confirm, and download files if needed

Maven

To update Maven:

$ $EDITOR pom.xml  # Update maven.version property
$ ./mvnw wrapper:wrapper  # Update scripts and supporting files
$ ./mvnw wrapper:wra  # Confirm, and download files if needed

Note that Maven wrapper is developing, and will be bundled with an upcoming Maven release. For now it is a separate plugin in your pom.xml.

Tips

  • Take advantage of your shell's tab completion:
  • The sample Gradle and Maven build scripts often specify specific versions of the tooling, separate from the plugin versions. This is intentional. You should be able to update the latest tool version even when the plugin has not yet caught up.
  • Gradle itself does not provide support for "profiles", a key Maven feature. This is different from profiling build performance! Maven profiles can be used in many ways. The most common are to enabling/disabling build features on the command line, tailoring the build to a particular deployment environment, or using different credentials for other systems. If this feature is important for your team, you can code if/else blocks directly in build.gradle, or use a plugin such as Kordamp Profiles Gradle plugin. (Kordamp has a suite of interesting Gradle plugins beyond this one; read more on that page)
  • Gradle uses advanced terminal control, so you cannot always see what is happening. To view Gradle steps plainly when debugging your build, use:
    $ ./gradlew <your tasks> | cat
    or save the output to a file:
    $ ./gradlew <your tasks> | tee -o some-file
  • If your source code is in Kotlin, so should be your build. Gradle provides a Kotlin DSL for build scripts as a first-class counterpart to the traditional Groovy DSL.
  • Maven colorizes output, but does not use terminal control to overwrite output.
  • See Setup your CI for another approach to getting plain text console output
  • The Maven Notifier may be to your liking.
  • If you like Maven, but XML isn't your thing, you might explore the Polyglot for Maven extension which provides the POM in multiple languages/formats (eg, Ruby, YAML, many others).
  • If you have a multi-module Maven build, you might consider Takari Smart Builder to speed it up.
  • Gradle and Maven best practice is to specify the version for each plugin, even default plugins that come with your tool, and even the version of the tool itself. This enforces reproducible builds, and helps you verify the Software Supply Chain. See Gradle Enforcer Plugin and Maven Enforcer Plugin to specify exact versions of Gradle or Maven for your build.

Going further

TODO: Placeholder section

Clone this wiki locally