How does build-helper:attach-artifact work, and when would you use it instead of producing a separate Maven module?
answer
- classifier + type distinguish secondary
- same GAV, one main artifact
- file must already exist
- bind to package phase
- variant vs independent module
basics
~10 sattach-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.
solid answer
~40 sEvery Maven project has one *main* artifact, but you can attach additional ('secondary' / 'attached') artifacts that share the GAV (groupId:artifactId:version) and are distinguished by a **classifier** plus **type/extension**. build-helper:attach-artifact does exactly that: you point it at an already-produced file and give it a classifier (e.g. `client`, `linux-x86_64`) and type (e.g. `jar`, `zip`). On install/deploy those files are published next to the main JAR. You'd use it instead of a separate module when the extra file is a *variant* of the same logical component — a stripped client jar, a native binary for one OS, a documentation bundle — where spinning up a whole module is overkill. If the variant has its own dependencies, its own consumers, or independent versioning, a real module (or the assembly/shade plugin emitting a classified jar) is cleaner.
code
xml · 14 lines<execution>
<id>attach-client</id>
<phase>package</phase>
<goals><goal>attach-artifact</goal></goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.directory}/app-client.jar</file>
<type>jar</type>
<classifier>client</classifier>
</artifact>
</artifacts>
</configuration>
</execution>go deeper
Know secondary artifacts exist and need a classifier.
Configure attach-artifact with file/type/classifier bound to package; consume it with a classified dependency.
Decide attach-artifact vs separate module vs shade-produced classified jar based on dependencies and lifecycle.
Define repo-wide conventions for variant artifacts (native, client, docs) and guard against coordinate collisions across teams.
## The single-artifact rule and how to escape it Maven coordinates are **GAV**: groupId, artifactId, version. By default a project deploys exactly one *main* artifact at that GAV (e.g. a JAR). Consumers can also ask for **secondary artifacts** that share the same GAV but add a **classifier** — a free-text suffix in the coordinate — and a **type** (which maps to a file extension). For example `com.acme:app:1.0` is the main jar; `com.acme:app:1.0:client` (classifier `client`) and `com.acme:app:1.0:sources` are secondary. ## What attach-artifact does `build-helper:attach-artifact` takes a file that some *earlier* build step produced and registers it on the project so the `install` and `deploy` phases write it to the local/remote repository with the right coordinates. It does **not** create the file — you must already have built it (via assembly, shade, antrun, exec, etc.). Key configuration: - `<artifacts>` — a list, each with `<file>` (the path), `<type>` (extension, default `jar`), and `<classifier>`. - Bind to the `package` phase (after the file exists, before install). ```xml <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>attach-client-zip</id> <phase>package</phase> <goals><goal>attach-artifact</goal></goals> <configuration> <artifacts> <artifact> <file>${project.build.directory}/${project.artifactId}-client.zip</file> <type>zip</type> <classifier>client</classifier> </artifact> </artifacts> </configuration> </execution> </executions> </plugin> ``` A consumer then depends on it with the classifier: ```xml <dependency> <groupId>com.acme</groupId><artifactId>app</artifactId> <version>1.0</version><classifier>client</classifier><type>zip</type> </dependency> ``` ## Attach-artifact vs a separate module Use attach-artifact when the file is a **variant of the same component**, shares its lifecycle and version, and has no independent dependency graph: OS-specific native binaries, a slimmed client jar, a distribution zip, generated docs. Use a **separate module** when the artifact has its own source, its own dependencies, its own consumers, or needs to version independently — sharing a GAV would lie about its identity and break dependency resolution. ## Gotchas - Two attached artifacts must not collide on (classifier, type) — duplicate coordinates overwrite. - The main artifact still must exist; you cannot attach an artifact to a `pom`-packaging project as its *main* artifact this way. - The shade and assembly plugins can directly produce classified artifacts; prefer those if they already build the file.
- What distinguishes two artifacts that share the same groupId, artifactId, and version?The classifier and the type/extension. A consumer selects the secondary one by specifying that classifier (and type) in the dependency.
- Does attach-artifact build the file?No. It only registers an already-existing file for install/deploy; an earlier step (assembly, shade, antrun, exec) must create it, and the execution must run after that step in the lifecycle.
saying these in an interview costs you the question
- Claiming attach-artifact creates/packages the file itself
- Thinking secondary artifacts can have a different version than the main one
- Using it when the artifact really needs its own dependencies/lifecycle (should be a module)