CI and IDE Integration for Ant, Maven and Gradle

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page covers the two places where a build is run by something other than you typing a command: a continuous integration (CI) server and an IDE. It is written against Ant 1.10.18, Maven 3.10.0 (3.9.16 also checked) and Gradle 9.8.1, and was checked on 2026-10-16. Every command whose output is shown below was run on that day with JDK 26 on macOS, on a one-module slice of the tidepool project (the tidepool-model sources, a small test, and a build file for each tool) so the three tools can be compared side by side. Paths in the output are shortened to … for readability; nothing else is edited. The GitHub Actions workflow files were parsed as YAML but were not run here, because there is no GitHub runner in this environment; each command they contain was run locally, and the action inputs were checked line by line against the official action documentation. Features newer than Maven 3.9 or Gradle 8.14 name their release and say when they are incubating.

A CI build and an IDE build do the same job as the one you run in a terminal; what changes is the environment. There is no person to answer a prompt, no terminal to render colour, a fresh machine that has to download everything, and a consumer (a CI server or an IDE) that wants the result as a status code and as XML files instead of as text. The sections below take those four differences in turn. The tool-neutral background is on Build Automation Fundamentals; the wrapper and checksum steps that CI should always include are on Supply Chain Security.

Running builds headless

A headless build must not wait for input, must report success or failure through its exit code, and should keep its log short enough to read in a CI web page. The three tools have different switches for the same three needs:

Need Ant 1.10.18 Maven 3.10.0 Gradle 9.8.1

Never prompt

-noinput

-B (--batch-mode)

--non-interactive (9.6, incubating)

Plain, colourless log

-emacs, or a log file with -logfile

-B disables colour; --color never

--console=plain; NO_COLOR

Less log noise

-q (quiet), -S (silent)

--no-transfer-progress (-ntp), -q

-q (--quiet)

Put switches in the environment

ANT_ARGS

MAVEN_ARGS

GRADLE_OPTS, org.gradle.console in gradle.properties

Machine-readable log

-logger org.apache.tools.ant.XmlLogger

surefire XML in target/surefire-reports

test XML in build/test-results/test

Exit code

0 success, 1 failure

0 success, 1 failure

0 success, 1 failure

The -q, ANT_ARGS and GRADLE_OPTS entries come from the manuals and were not run here; the rest of this section shows the switches that were run for each tool. The same flags are described in depth on Running Ant, Running Maven from the Command Line and The Gradle Command-Line Interface.

Ant

-noinput makes any input task fail instead of waiting, which is what a CI job wants. Ant’s exit code is 0 for BUILD SUCCESSFUL and 1 for BUILD FAILED. This build file compiles the tidepool model, packages it and then checks that the data file exists, so that a missing file can show a failure:

<?xml version="1.0" encoding="UTF-8"?>
<project name="tidepool-model" default="jar" basedir=".">
  <property name="build.dir" value="build"/>

  <target name="compile">
    <mkdir dir="${build.dir}/classes"/>
    <javac srcdir="src/main/java" destdir="${build.dir}/classes" release="21"
           includeantruntime="false"/>
  </target>

  <target name="jar" depends="compile">
    <jar destfile="${build.dir}/tidepool-model.jar" basedir="${build.dir}/classes"/>
  </target>

  <target name="check-data">
    <fail message="data/stations.csv is missing">
      <condition>
        <not><available file="data/stations.csv"/></not>
      </condition>
    </fail>
  </target>

  <target name="ci" depends="jar,check-data"/>
</project>

Run on a clean checkout (Ant 1.10.18, JDK 26; the long scratch-directory prefix is shortened to …):

ant -noinput ci
echo "exit=$?"
Buildfile: …/ant/build.xml

compile:
    [mkdir] Created dir: …/ant/build/classes
    [javac] Compiling 4 source files to …/ant/build/classes

jar:
      [jar] Building jar: …/ant/build/tidepool-model.jar

check-data:

ci:

BUILD SUCCESSFUL
Total time: 0 seconds
exit=0

With data/stations.csv renamed, the same command fails. The build file and line number are in the message, and the exit code is 1:

check-data:

BUILD FAILED
…/ant/build.xml:16: data/stations.csv is missing

Total time: 0 seconds
exit=1

By default Ant stops at the first failing target. -keep-going (-k) continues with targets that do not depend on the failed one, which is useful to see every failing check in one CI run; here nothing else was left to run, and the exit code was still 1:

check-data:
Target 'check-data' failed with message 'data/stations.csv is missing'.
Cannot execute 'ci' - 'check-data' failed or was not executed.

BUILD FAILED
…/ant/build.xml:16: data/stations.csv is missing

For a log a CI server can parse, Ant’s built-in XmlLogger writes the whole build as one XML file. It is selected with -logger and the file with -logfile; on the failing build above, the root element carries the error message:

ant -noinput -logger org.apache.tools.ant.XmlLogger -logfile build-log.xml ci
<build error="…/ant/build.xml:16: data/stations.csv is missing" time="0 seconds">

Other loggers in the same package are AnsiColorLogger (colour for a terminal) and MailLogger (mail on completion). With -S (-silent) and -emacs, which print nothing but task output and failures and drop the adornments, the successful build above printed only the Buildfile: line. Test results come from the test task rather than the logger: junitlauncher writes JUnit XML that CI servers read, as shown on Testing with JUnit and junitlauncher.

Maven

-B (--batch-mode) is the non-interactive switch: it never prompts and, according to mvn --help, also disables output colour. Most of the CI noise in a first build is not the build but the transfer progress lines from downloading dependencies; --no-transfer-progress (-ntp) removes them. Both can be set once for the whole job with the MAVEN_ARGS environment variable, whose arguments Maven places before the command-line arguments (Maven 3.9.0 and later).

The POM used for these runs is a minimal one with a JUnit 6 test dependency and the surefire plugin pinned:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>io.tidepool</groupId>
  <artifactId>tidepool-model</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>6.1.3</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.6.0</version>
      </plugin>
    </plugins>
  </build>
</project>

To show the difference, the same build was run twice on a machine with an empty local repository (-Dmaven.repo.local pointing at a fresh directory each time), once with defaults and once with MAVEN_ARGS:

mvn -Dmaven.repo.local=../repo1 clean verify > default.log 2>&1
MAVEN_ARGS="-B --no-transfer-progress" mvn -Dmaven.repo.local=../repo2 clean verify > ci.log 2>&1

The default log had 422 lines (2,993 when split at carriage returns, because the progress counters redraw one line), with 187 Downloaded from entries; the MAVEN_ARGS log had 48 lines and no transfer progress. The first lines of the second log are the useful ones for a CI page:

[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< io.tidepool:tidepool-model >---------------------
[INFO] Building tidepool-model 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] Loaded 24109 auto-discovered prefixes for remote repository central (prefixes-central.txt)

(The last line is Maven Resolver loading Central’s published repository prefix list; it is not a CI setting.)

Colour is controlled by --color auto|always|never, available in 3.9.16, 3.10.0 and 4.0.0-rc-7 (Maven 4 release candidate). It overrides -B: with -B --color always, the first lines of the output were [^[[34;1mINFO^[[0m] Scanning for projects... when shown with cat -v, so ANSI escapes were emitted even in batch mode. The older -Dstyle.color=always user property is still honoured by 3.10.0 and gave the same escapes when tested. Colour is rarely wanted in a CI log, but some CI web pages render it, and then --color always is the supported way to turn it on.

A failing test returns exit code 1 and an [ERROR] block with the assertion, the module and the report directory. This is the real output from changing the expected value in HeightTest to 2.5 (lines shortened with …):

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.031 s <<< FAILURE! -- in io.tidepool.model.HeightTest
[ERROR] io.tidepool.model.HeightTest.keepsMetres -- Time elapsed: 0.019 s <<< FAILURE!
[ERROR] Failures:
[ERROR]   HeightTest.keepsMetres:10 expected: <2.5> but was: <1.5>
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[ERROR] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.6.0:test (default-test) on project tidepool-model: There are test failures.
[ERROR] See …/maven/target/surefire-reports for the individual test re
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.

A passing run leaves TEST-io.tidepool.model.HeightTest.xml in target/surefire-reports, which is the file Jenkins, GitLab and the GitHub test-report actions read. Surefire writes it by default; nothing has to be configured. Details are on Testing with Surefire.

Gradle

Gradle’s console mode is plain when it is not attached to a terminal, which is the default in CI, but an explicit --console=plain makes the intent visible and also works when a CI service allocates a pseudo-terminal. --non-interactive (Gradle 9.6, incubating) disables all interactive prompting; the same switch is the org.gradle.console.interactive property set to false. The NO_COLOR environment variable, also new in 9.6, suppresses colour regardless of the console mode. The build used here:

Kotlin DSL
plugins {
    java
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter:6.1.3")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

tasks.test {
    useJUnitPlatform()
}
Groovy DSL
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:6.1.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

Only the Kotlin DSL version was run (the Groovy form is the same build in the other DSL; see Groovy DSL and Migrating to Kotlin). A second invocation shows why Gradle is cheap to re-run on CI when nothing changed: every task is up to date.

./gradlew --non-interactive --console=plain build
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test UP-TO-DATE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE

BUILD SUCCESSFUL in 2s
4 actionable tasks: 4 up-to-date
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.8.1/userguide/configuration_cache_enabling.html

A failing test, again changed to expect 2.5, gives exit code 1 and the report location:

> Task :test FAILED

HeightTest > keepsMetres() FAILED
    org.opentest4j.AssertionFailedError at HeightTest.java:10

1 test completed, 1 failed

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file://…/gradle/build/reports/tests/test/index.html

* Try:
> Run with --scan to get full insights from a Build Scan (powered by Develocity).

BUILD FAILED in 924ms

The XML for CI servers is in build/test-results/test/TEST-io.tidepool.model.HeightTest.xml. Colour behaviour was checked with --console=colored: tasks marked UP-TO-DATE were wrapped in yellow escapes (^[[33m) normally, and with NO_COLOR=1 set only the bold escape (^[[1m) remained.

Gradle should be invoked through the wrapper on CI so that the build picks the Gradle version it was written for, and should not run clean by default; running clean removes the outputs that incremental builds and the build cache would otherwise reuse. See The Gradle Wrapper and Best Practices and Troubleshooting.

Caching on CI

A CI runner is usually a fresh machine, so the first cost of a build is downloading the same dependencies that the previous run downloaded. Four kinds of cache are involved, and they are different things:

  • Dependency caches: the Maven local repository (~/.m2/repository) and Gradle’s caches/modules-2. They save downloads and need no change to the build.

  • Tool and wrapper caches: the Gradle distribution unpacked by the wrapper, or the Maven distribution unpacked by the Maven wrapper.

  • Build-output caches: Gradle’s build cache and the Maven build cache extension, which reuse task or goal outputs.

  • Configuration caches: Gradle’s configuration cache, which reuses the result of the configuration phase.

actions/setup-java

actions/setup-java installs a JDK and, with cache: maven or cache: gradle, caches the dependency directory of that tool. Its README (v6) lists the inputs: cache (maven, gradle or sbt), cache-dependency-path for the files that are hashed into the key, cache-path to override the cached directory, and cache-read-only: true to restore without saving. Cache keys also include .mvn/extensions.xml and gradle.properties, so a change to a Maven extension or a Gradle dependency property invalidates the cache. Ant has no cache: value, because Ant does not itself resolve dependencies; with Ivy or the Resolver Ant Tasks the cache directory has to be cached with actions/cache explicitly (see Ivy Overview).

gradle/actions/setup-gradle

gradle/actions/setup-gradle (current major v6, latest release v6.4.0 on 2026-09-28) replaces the deprecated gradle/gradle-build-action. It restores and saves the Gradle User Home, validates every gradle-wrapper.jar in the repository against known checksums, and adds Build Scan links to the job summary. The inputs worth knowing:

Input Effect

cache-read-only

Read cache entries but do not write them back. The documented use is feature branches, so that only the default branch updates the cache.

cache-provider

enhanced (default since 6.1.0) or basic, a fully open-source wrapper over actions/cache.

cache-encryption-key

Base64 AES key used to encrypt configuration-cache entries, which may contain credentials. Required to keep the configuration cache between jobs.

build-scan-publish

Publishes a Build Scan for every Gradle invocation; needs build-scan-terms-of-use-url and build-scan-terms-of-use-agree.

add-job-summary-as-pr-comment

Copies the job summary to the pull request: never (default), on-failure or always.

The companion gradle/actions/dependency-submission action runs the build in a mode that captures the resolved dependency graph and submits it to GitHub’s Dependency Submission API, which feeds Dependabot alerts. It needs contents: write permission and must run on the default branch to be useful. The standalone wrapper-validation action does only the wrapper check, for projects that do not use setup-gradle.

Gradle’s configuration cache in read-only mode

--configuration-cache stores the configured task graph, and a later build reuses it when the inputs match. Gradle 9.1 added a read-only mode for CI: a hit is reused, a miss is not stored, and nothing is serialised. The documentation describes it as incubating and warns that execution-time configuration-cache violations are not detected on a miss. The intended pattern is that builds on the main branch write the cache and pull-request builds only read it. Run on the sample project:

./gradlew --non-interactive --console=plain --configuration-cache clean build
./gradlew --non-interactive --console=plain --configuration-cache -Dorg.gradle.configuration-cache.read-only=true clean build
BUILD SUCCESSFUL in 1s
5 actionable tasks: 5 executed
Configuration cache entry stored.
BUILD SUCCESSFUL in 1s
5 actionable tasks: 5 executed
Configuration cache entry reused.

Without clean the tasks would all have been up to date; clean is used here only so that the second run still has something to execute. The details of what makes a build compatible are on Configuration Cache and Isolated Projects.

The Maven build cache extension

Maven has no build-output cache in core. The Apache Maven Build Cache Extension (version 1.3.0 checked) is loaded from .mvn/extensions.xml:

<?xml version="1.0" encoding="UTF-8"?>
<extensions>
  <extension>
    <groupId>org.apache.maven.extensions</groupId>
    <artifactId>maven-build-cache-extension</artifactId>
    <version>1.3.0</version>
  </extension>
</extensions>

With no maven-build-cache-config.xml the extension runs with defaults. The first mvn -B -ntp clean verify printed that it was calculating a checksum of the project inputs (Found 5 input files) and Local build was not found by checksum d455c26241932114; it then ran every goal. The second run, with nothing changed, restored the build from the cache and skipped every plugin execution:

[INFO] Attempting to restore project io.tidepool:tidepool-model from build cache
[INFO] Local build found by checksum d455c26241932114
[INFO] Found cached build, restoring io.tidepool:tidepool-model from cache by checksum d455c26241932114
[INFO] Skipping plugin execution (cached): resources:resources
[INFO] Skipping plugin execution (cached): compiler:compile
[INFO] Skipping plugin execution (cached): resources:testResources
[INFO] Skipping plugin execution (cached): compiler:testCompile
[INFO] Skipping plugin execution (cached): surefire:test
[INFO] Skipping plugin execution (cached): jar:jar
[INFO] BUILD SUCCESS
[INFO] Total time:  0.473 s

On JDK 26 the first run also printed a WARNING: A terminally deprecated method in sun.misc.Unsafe has been called from the extension’s hashing library (net.openhft.hashing); it did not affect the result. The cache here is the local one under ~/.m2/build-cache; sharing it between CI jobs needs a remote cache server, which was not run here. The extension is covered on Build Performance: Cache, Parallel and mvnd.

A CI pipeline for tidepool

The same sequence applies to every tool; only the commands in the middle change.

flowchart LR A[Checkout] --> B[Set up JDK] B --> C[Restore caches] C --> D[Build and test headless via wrapper] D --> E{Exit code 0?} E -- yes --> F[Upload reports and artifacts] E -- no --> G[Upload reports and fail the job] F --> H[Save caches on main only] G --> H

The checkout, JDK and cache steps are the part a CI service provides; the build step is the one this page showed for each tool; and reports are uploaded even on failure, because the failing run is the one you want the XML for.

GitHub Actions workflows for tidepool

The workflows below build the same one-module slice of tidepool as the examples above, one file per tool. They use the current majors of the actions as of 2026-10-16: actions/checkout v7, actions/setup-java v6, actions/upload-artifact v7 and gradle/actions v6. Each runs a JDK 21 Temurin build, matching the release or toolchain level in the build files, and uses permissions: contents: read. They are not run here; the YAML parses, the commands are the ones run locally above, and the input names were checked against the READMEs and the Gradle manual page.

Ant. GitHub-hosted runners include a JDK and Ant in their tools cache, according to GitHub’s Ant guide, so no install step is needed and there is no dependency cache to restore:

name: tidepool-ant
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"
      - name: Build and check
        run: ant -noinput -buildfile build.xml ci
      - uses: actions/upload-artifact@v7
        with:
          name: tidepool-model-jar
          path: build/tidepool-model.jar

Maven. MAVEN_ARGS applies -B and --no-transfer-progress to every Maven call in the job, cache: maven caches ~/.m2/repository, and the project is built with the Maven wrapper (see Maven Wrapper). Reports are uploaded if: always():

name: tidepool-maven
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

env:
  MAVEN_ARGS: -B --no-transfer-progress

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"
          cache: maven
      - name: Verify
        run: ./mvnw verify
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: surefire-reports
          path: "**/target/surefire-reports/TEST-*.xml"

Gradle. setup-gradle handles caching and wrapper validation, and writes the cache only on main. The configuration cache is enabled on the command line. A second job submits the dependency graph on main, which is why it has the only contents: write permission in the file:

name: tidepool-gradle
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"
      - uses: gradle/actions/setup-gradle@v6
        with:
          cache-read-only: ${{ github.ref != 'refs/heads/main' }}
      - name: Build
        run: ./gradlew --non-interactive --console=plain --configuration-cache build
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: test-results
          path: "**/build/test-results/test/TEST-*.xml"

  dependency-submission:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "21"
      - uses: gradle/actions/dependency-submission@v6

The actions are pinned by major-version tag, as the official examples do. For the build-integrity side of CI see Supply Chain Security. For an Android-specific workflow see CI/CD with GitHub Actions, and for a Maven build that also builds a container image see Docker Images for Java and Spring Boot.

Jenkins and GitLab CI

The same commands work in any CI system; what differs is how caches, test results and tool installations are declared. These notes are taken from the official documentation and were not run here.

Jenkins. A Jenkinsfile calls the wrapper through sh (or bat on Windows). The Gradle manual’s example wraps the call in withGradle { … }, provided by the Gradle plugin, so that Build Scan links appear on the build page, and publishes test results with the junit step on **/build/test-results/test/TEST-*.xml. The equivalent for Maven is a junit step on the surefire XML files, and for Ant on the files written by junitlauncher. With the wrapper in the repository, a Jenkins agent needs only a JDK, which the pipeline tools { jdk 'jdk-21' } block selects from the Jenkins tool configuration.

GitLab CI. Both Gradle and Maven keep their caches outside the project by default, but GitLab can only cache paths inside $CI_PROJECT_DIR, so the documented Gradle recipe sets GRADLE_USER_HOME to $CI_PROJECT_DIR/.gradle; the equivalent for Maven is to point -Dmaven.repo.local at a directory in the project (the same idea, not run here). The Gradle manual recommends caching only .gradle/wrapper on the SaaS shared runners (keyed on gradle/wrapper/gradle-wrapper.properties) and the full .gradle/caches on self-hosted runners, and artifacts:reports:junit to show test results in the merge request. GitLab also provides a Gradle.gitlab-ci.yml template.

On a persistent agent, a shared Gradle User Home must not be used concurrently by unrelated jobs, and on an ephemeral agent the daemon gives no benefit after the job ends. Both points are in Gradle’s CI page.

From a green build to a deployment

A CI build is the first stage of a delivery process, not the whole of it. Three process ideas apply to all three tools and are independent of the CI product. Build the release artifact once and promote that same file through environments, rather than rebuilding it for each one, so that what was tested is what is deployed. Publish it to a repository manager under a release version, not a snapshot, with a tag in version control that identifies the commit; the mechanics are on Repositories and Publishing Across Tools. And keep credentials for the repository out of the build files, in the CI service’s secret store, referenced by placeholders such as ${env.CENTRAL_TOKEN} in Maven’s settings.xml or an environment variable read by Gradle. Publishing to Maven Central and to a real repository manager is not run anywhere in this section; the examples publish to a file:// repository or to localhost.

IDE support

An IDE does two things with a build tool: it reads the build to learn the project model (source roots, dependencies, compiler level), and it can launch the tool to run tasks, goals or targets. The two can disagree with the command line if the IDE uses a different JDK, a different Maven or Gradle version, or different settings, which is the cause of most "builds in the IDE but not on CI" reports. Check which JDK and which tool version the IDE is configured to use before debugging the build.

IDE Maven Gradle Ant

IntelliJ IDEA

Built-in integration; the documentation says it bundles Maven 3.9.11 (in IDEA 2026.2) and, since 2025.1, offers Maven 4; a custom Maven can be selected.

Built-in integration through the Gradle and "Gradle for Java" plugins, enabled by default; no separate Gradle install is needed.

The Ant plugin is not bundled; the JetBrains documentation says the latest compatible IDEA version is 2023.2. With newer IDEA, run Ant from a terminal or an external tool.

Eclipse

M2Eclipse (m2e): launches Maven builds, builds the Eclipse build path from pom.xml, resolves workspace projects without installing them.

Buildship, the Eclipse plugin for Gradle, imports and runs Gradle builds through the Tooling API.

Ant’s manual says all modern Java IDEs support Ant almost out of the box.

VS Code

"Maven for Java" extension, included in the Extension Pack for Java: explore projects, run goals, edit dependencies, view the effective POM.

"Gradle for Java" extension; tests can be delegated to Gradle from the Testing Explorer.

No dedicated extension is documented; run Ant from the integrated terminal.

Apache NetBeans

Maven projects are documented in the NetBeans tutorials.

Built-in support, according to the Gradle manual.

See Ant’s own IDE integration page.

The cells for Ant in VS Code and NetBeans say what the documentation does or does not state; they are not claims that nothing exists. External Ant integrations are listed on Ant’s "External Tools and Tasks" page.

The Gradle Tooling API

IntelliJ IDEA and Buildship import and run Gradle projects through Gradle’s Tooling API, a library for embedding Gradle in other software. The documentation lists what it provides: querying the project hierarchy, dependencies and tasks, running a build with progress events, running a single test class or method, cancelling a build, and downloading the Gradle version the wrapper asks for. It is version independent: the same API works with Gradle versions older and newer than the API library. It always uses the Gradle daemon, so repeated requests from an IDE reuse one long-lived process. Gradle TestKit, used to test plugins, is built on it. The API and its use from an init script are covered on Init Scripts and the Tooling API.

Ant can also be driven from Java code; see Embedding Ant.

Practical advice

  • Commit the wrapper (gradlew, mvnw) and open the project through the build file, not by hand-assembling an IDE project, so that the IDE and CI use the same tool version.

  • Set the IDE’s Maven or Gradle JVM to the same JDK that CI uses, or rely on a Gradle toolchain or Maven toolchain to pin the compiler.

  • Prefer delegating build and run actions to Gradle or Maven when the IDE’s own compiler gives different results, for example with annotation processors such as the picocli processor in tidepool-cli.

  • Do not commit IDE project files that duplicate the build model.