skip to content

Build Helper & Glue

The build-helper, exec, and antrun plugins for adding source roots, attaching extra artifacts, deriving properties, and running arbitrary steps in a phase. Interviewers ask about these escape hatches, and when reaching for them is a design smell.

on this pageshow

explore

questions

5

What is the build-helper-maven-plugin, and what kinds of problems does it solve in a Maven build?

level: juniorimportance: should knowfreq 45%

answer

  1. org.codehaus.mojo
  2. add-source / attach-artifact
  3. parse-version / regex-property
  4. reserve-network-port
  5. bag of unrelated goals

basics

~10 s

It's a utility plugin that adds small 'glue' goals Maven core lacks: registering extra source/test folders, attaching extra build outputs as artifacts, parsing versions, extracting a property by regex, and reserving free network ports.

solid answer

~40 s

The build-helper-maven-plugin (groupId org.codehaus.mojo) is a collection of small, unrelated utility goals that fill gaps in Maven's fixed convention. The most common are: add-source / add-test-source to register additional source roots (e.g. generated code) so the compiler picks them up; attach-artifact to publish secondary outputs alongside the main JAR; parse-version to split a version into major/minor/incremental properties; regex-property to derive a property by applying a regex; and reserve-network-port to grab free ephemeral ports for integration tests. You bind each goal to a phase via an execution with its own id. It exists because Maven's lifecycle and project model are deliberately rigid — one main artifact, conventional src layout — and these goals let you bend that without writing a custom plugin.

code

xml · 17 lines
xml
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <version>3.6.0</version>
  <executions>
    <execution>
      <id>add-generated</id>
      <phase>generate-sources</phase>
      <goals><goal>add-source</goal></goals>
      <configuration>
        <sources>
          <source>${project.build.directory}/generated-sources/jaxb</source>
        </sources>
      </configuration>
    </execution>
  </executions>
</plugin>

go deeper

for a junior

Know it adds extra source folders and exists under the mojohaus groupId; recall add-source by name.

for a middle

Wire add-source to generate-sources and attach-artifact with a classifier; understand each goal is independent.

for a senior

Choose between build-helper named goals vs exec/antrun; keep generated code out of src; reserve ports for parallel ITs.

for a principal

Standardize glue patterns across a multi-module repo; pin plugin versions in pluginManagement; weigh a real custom plugin vs accreting glue.

## What it is Maven enforces a strong convention: each project produces one main artifact, sources live under `src/main/java`, the lifecycle runs a fixed set of phases. The **build-helper-maven-plugin** (Maven coordinates `org.codehaus.mojo:build-helper-maven-plugin`) is a 'Swiss-army-knife' plugin: a bag of small, independent **goals** (a goal = one runnable plugin task) that each patch one limitation of that convention. They share nothing except living in the same plugin. ## The main goals - **add-source** — registers an extra directory as a compile source root, so the `maven-compiler-plugin` also compiles it. Typical use: a folder of code emitted by a code generator (JAXB, protobuf, OpenAPI). Bind it to the `generate-sources` phase. - **add-test-source** — same idea for test sources (bind to `generate-test-sources`). - **add-resource / add-test-resource** — register extra resource directories. - **attach-artifact** — takes an additional file you built and **attaches** it to the project so `install`/`deploy` publish it next to the main artifact, using a `classifier` (e.g. `client`, `tests`) and `type` to keep coordinates unique. - **parse-version** — reads the project version (e.g. `1.4.7-SNAPSHOT`) and sets properties `parsedVersion.majorVersion`, `minorVersion`, `incrementalVersion`, `qualifier`, etc., for downstream filtering or naming. - **regex-property** — sets a new property by applying a regular expression `regex`/`replacement` to a `value`. Useful to sanitize a branch name or strip a suffix. - **reserve-network-port** — finds free TCP ports and assigns them to named properties (e.g. `it.server.port`) so parallel integration-test runs don't collide. Bind it before the servers start (e.g. `process-test-resources` or `pre-integration-test`). ## How you wire it Each goal goes in its own `<execution>` with a unique `<id>`, `<phase>`, `<goals>`, and `<configuration>`. ```xml <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>add-generated-sources</id> <phase>generate-sources</phase> <goals><goal>add-source</goal></goals> <configuration> <sources> <source>${project.build.directory}/generated-sources/openapi</source> </sources> </configuration> </execution> </executions> </plugin> ``` ## Relationship to exec/antrun It is part of the broader 'glue' family with **exec-maven-plugin** (run an external program or a Java main) and **maven-antrun-plugin** (run inline Ant tasks). Reach for build-helper for its specific named goals; reach for exec/antrun for arbitrary one-off steps.

  • Why not just put generated code under src/main/java?
    Generated code should never live under version-controlled source roots — it gets stale, causes merge noise, and is overwritten. You generate it into target/ and use add-source so the compiler still sees it, keeping it out of git.
  • What groupId is the plugin under?
    org.codehaus.mojo (the Mojohaus project), not org.apache.maven.plugins, so it does not get the short prefix-resolution by default unless configured in settings.xml pluginGroups.

It's the junk drawer of Maven plugins — a handful of unrelated little tools (tape, scissors, batteries) that each fix one annoyance the main toolbox doesn't cover.

saying these in an interview costs you the question

  • Saying it is part of Maven core or under org.apache.maven.plugins
  • Thinking add-source copies files rather than registering a compile root
  • Believing one execution can run multiple unrelated goals cleanly without separate ids

context

open as a page

How does build-helper:attach-artifact work, and when would you use it instead of producing a separate Maven module?

level: middleimportance: should knowfreq 35%

basics

~10 s

attach-artifact registers an extra file (built by some earlier step) as a secondary artifact with a classifier and type, so mvn install and deploy publish it alongside the main JAR under the same groupId/artifactId/version.

open as a page

Walk through using build-helper:reserve-network-port to make integration tests safe to run in parallel. Why is it needed?

level: middleimportance: should knowfreq 25%

basics

~20 s

reserve-network-port finds free TCP ports and stores them in Maven properties. You bind it before your test server starts, then point the server and tests at those properties instead of a hardcoded port, so concurrent builds don't clash.

open as a page

Compare exec-maven-plugin and maven-antrun-plugin for binding a custom step to a phase. When would you choose each, and what are the trade-offs?

level: seniorimportance: should knowfreq 40%

basics

~20 s

exec-maven-plugin runs an external program (exec:exec) or a Java main on the project classpath (exec:java). maven-antrun-plugin runs inline Ant tasks (copy, mkdir, echo, etc.). Use exec for one external command/Java, antrun for small file/scripting glue.

open as a page

How do build-helper:parse-version and build-helper:regex-property work, and what are realistic uses for the properties they produce?

level: middleimportance: nice to knowfreq 18%

basics

~10 s

parse-version splits the project version (e.g. 2.4.1-SNAPSHOT) into properties like parsedVersion.majorVersion/minorVersion/incrementalVersion/qualifier. regex-property creates a property by applying a regex find/replace to a value. Both feed naming, filtering, and conditional config.

open as a page