How to share POM fragments between different POMs - maven

I am currently struggling with Maven: I have a complex project made of several nested modules and for some of those modules, I have similar configurations in the POMs.
I would like to make it clean. Actually, I would like to define a "runnable-jar" common configuration and activate it in some modules.
Here is the POM fragment I would like to share between several projects:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<!-- Use a custom descriptor, with suffix "bin" -->
<descriptors>
<descriptor>src/main/assembly/runnable-jar-assembly.xml</descriptor>
</descriptors>
<!-- Add main class to manifest -->
<archive>
<manifest>
<mainClass>${mainClass}</mainClass>
</manifest>
</archive>
</configuration>
<!-- Add build of this package to lifecycle -->
<executions>
<execution>
<id>make-runnable-jar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
In some of the POMS, I would like to be able to do something like:
<!-- Set the main class -->
<properties>
<mainClass>my.main.Class</mainClass>
</properties>
<!-- Activate runnable jar build -->
<import>src/main/pom/runnable-jar-pom.xml</import>
I have searched for a mean to import some XML fragments into a POM, or to define a whole XML nodeset macro.
For what I have found, the closest solution would be to define a profile in the parent POM and activate it in some sub modules by testing the presence of a file. See this related question. But I am facing the problem of the {basedir} property not being set correctly inherited / set.
I find it very surprising to need a hack to do something so basic (=usual). How do you usually handle this in Maven ?

I have just discovered something that might solve my problem :
A module does not require to be a sub-module of its parent module.
Parent and sub-module relationships are separate concepts.
You can specify a parent POM module that is not the actual parent folder in your folder structure, by using the relativePath attribute (as explained in the doc)
In my case, I use the following layout:
main-project
utils (parent:main-project)
cli-programs (parent:main-project)
generic-cli (parent:cli-programs; Dummy & empty POM module)
cli-1 (parent:generic-cli)
cli-2 (parent:generic-cli)
Then, in generic-cli/pom.xml I can declare a configuration that is common to all my cli programs (like custom test suites, runnable-jar packaging, etc).

One way to do this would be to declare your <plugin> code inside <pluginManagement> of the parent pom of your multi-module project. The individual modules can then have a <plugin> section which can use this without redeclaring the contents.
Parent pom:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
... all the details...
</plugin>
...
</plugins>
</pluginManagement>
Child poms:
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugin>

Maven-tiles solves this. It's also on the roadmap for maven 3.x, tracked here.

not a total answer but a solution to the basedir problem is to use a common layout of the modules, e.g. root/modules/moduleA root/modules/moduleB.
You can't build the modules formm their own directory anymore, only through thr parent project. But you can work with the profiles.

Related

Parent pom with definitions that shouldn't be inherited

I am trying to build a parent pom . I'm not using dependencyManagement or pluginManagement. I want all the children to inherit everything.
However, the parent project itself has a specific build requirement that I'm using the maven-jar-plugin for. I don't want the child to inherit this piece. Is there a way to indicate sections that shouldn't be inherited by the child project?
However, the child projects do also use the maven-jar-plugin for another section. Essentially, I have
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
</execution>
</executions>
<configuration>
<archive>
</archive>
</configuration>
</plugin>
I want the configuration section to be inherited by all the children, but the execution section is only for the parent
Put the configuration into <pluginManagement>.
Put the execution into <plugins>.
Use <inherited>false</inherited>.

pluginManagement interferes with shade plugin

Just started with Maven for real; got a big surprise right away.
I understand (or I think I do) the concept of fat jar/uberjars. Package your code with all the dependencies, etc. maven-shade-plugin, found docs, some example, checked that it works. Now adding it to my POC project, which came from the maven-archetype-quickstart - what could possibly go wrong, eh?
To put it short, quickstart arrange the the following way:
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
...
So I just threw in a plugin for shading, ready to call it a day:
<!-- Maven Shade Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
Not so fast. First, I noticed that mvn clean package didn't mention maven-shade-plugin being executed (like the working example did). Hours of searching followed, and I was staring the "root cause" in the face - <pluginManagement>, offered by quickstart archetype, was it - somehow. Removing that tag magically allowed maven.shade.plugin to do its job. Otherwise, nope.
(Replica: https://github.com/alexakarpov/shade-me
unshade is the bad branch, master is good).
Talk about violation of the Principle of Least Surprise, eh =)
Can someone explain what's going on? The comment in generated pom mentioned something about parent pom, but I'm not doing anything with multi-pom setup yet..
<pluginManagement> role is described in Maven documentation :
Plugin Management contains plugin elements in much the same way [than plugins], except that rather than configuring plugin information for this particular project build, it is intended to configure project builds that inherit from this one.
Its goal here in the project generated by the archetype is to set specified versions of default plugins (maven-clean-plugin, maven-jar-plugin, ...). Note that these default plugins do not appear in your POM <plugins> section, but they are declared implicitly (you can check it by running mvn help:effective-pom).
But adding a plugin to <pluginManagement> section does not make your project invoke that plugin. Here, you can just set configuration and the version of the plugin you want to use. To invoke the plugin, you should absolutely declare it in <plugins> section.
In some projects (most of time multi-module projects), you could see the plugin and its configuration declared in <pluginManagement> of parent POM, and then referenced on <plugins> section of modules needing invocation of that plugin : thus, you do not have to repeat the same configuration on each module.
<pluginManagement> is mostly used if you want to use POM inheritance. Otherwise, on simple projects, you can just declare them in <plugins> section. I've also seen some projects defining all configuration in <pluginManagement>, just to make <plugins> section shorter and more readable, like the following example. It's just a matter of taste.
<build>
<!-- pluginManagement section : set versions and configurations -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<!-- plugins section : plugins that are invoked when building the project -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
</plugins>
</build>
You can also read more on StackOverflow : Maven : What is pluginManagement?

Parent POM : share common configuration files across child projects

I'm currently creating a parent POM, with all necessary configuration, as a base for all my personal projects. The ultimate goal is to have small child projects (= with little configuration) and to bootstrap projects more easily, by just inheriting the parent POM.
But I'm facing issues with plugins needing external files for configuration (as the maven-assembly-plugin, shown below in my example). I would like to have only one external file (the descriptor file for maven-assembly-plugin) shared across all child projects, and not redefine the same file in all my child projects.
Here's a complete example.
Example with maven-assembly-plugin
My parent POM (my-group-id:parent:1.0.0:pom), defining a configuration for maven-assembly-plugin, is the following:
<project>
<groupId>my-group-id</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<build>
<pluginManagement>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>create-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/bin.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</pluginManagement>
</build>
</project>
If I want all child projects to have the maven-assembly-plugin configuration defined in the parent, I must write the following (example for child project child1) :
<project>
<parent>
<groupId>my-group-id</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>child1</artifactId>
<version>0.0.1</version>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
But, as I said in introduction : with that configuration, for each child, I have to create a descriptor file (src/assembly/bin.xml) in its own project.
Moreover, it turns out that all my child projects have the same bin.xml file. I'll save you the contents, but for information, packaging is always a .tar.gz archive, with external libraries separated in a lib folder.
I just want to inherit the parent POM without bothering about the way assembly is done in my child projects. A shared descriptor file (bin.xml) seems to be the solution, but I can't see how to share it across all child projects. Anyway, I don't want to have a bin.xml per child project.
The question could be extended and does not only address the configuration of maven-assembly-plugin, because there is other plugins configurable with external files (docker-maven-plugin for example, where a Dockerfile can be provided). But at least, finding a solution for the maven-assembly-plugin specific case will be really helpful!

Is it possible to specify a dependency on a particular maven phase?

I am using the maven-docbkx-plugin to generate HTML and PDF output from docbook sources. I have several books, and these link to each other using olinks.
The olink database is generated in one maven phase (generate-resources), and the actual HTML and PDF generation, which looks up this database is executed in a subsequent maven phase (compile).
I have divided the maven project into a multi-module project, as each book has tens of included sub-documents. The docbkx-maven-plugin configuration is all done in the parent, then it is just the top-level docbook source that needs to be specified in the child POM.
But ... this does not work dependency-wise, as each module requires that the generate-resources of every other module has been run before it runs its compile phase, so that it can access the olink database of each of the other books.
Is there a way to do this in maven? Or will I need to re-structure into two maven projects (which will break the modularity of this project considerably, as all of the configuration will need to be declared in each project)?
The structure of the parent POM is:
...
<build>
<plugins>
<pluginManagenent>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<version>2.0.14</version>
<executions>
<execution>
<id>xrefdb</id>
<phase>generate-resources</phase>
<configuration>
...
</configuration>
<goals>
<goal>generate-html</goal>
</goals>
</execution>
<execution>
<id>html</id>
<phase>compile</phase>
<configuration>
...
</configuration>
<goals>
<goal>generate-html</goal>
</goals>
</execution>
</executions>
</plugin>
</pluginManagement>
</plugins>
</build>
</project>
And the modules:
<project>
...
<build>
<plugins>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<version>2.0.14</version>
<configuration>
...
</configuration>
</plugin>
</plugins>
</build>
</project>
I've done a bit more research on this, and from what I have read, what I am asking is not possible (but I would be happy to be advised otherwise). I have split my project into two, and given them a common parent from which they can draw their common configuration.
Another way I've solved this problem is to use maven profiles. I perform the first pass of all the modules in the first profile, then perform the second pass in a second profile.
It means the project has to be run twice to build all of its artifacts, but it is much more maintainable than spreading the sources over multiple projects.

Maven - Parent Pom - Child Inheritance

I am attempting to make a maven parent pom setup where I don't have to declare any plugin information in my child pom, everything is taken from the parent pom.
I essentially have it working where I've put all my plugins with there configurations in to the parent pom. Then in the child poms I have to declare the plugin still, but without the version and configuration information.
I don't want to have to declare the plugin in the child at all. This way I can add new features (such as pmd, freebugs, etc) to my parent pom and all my projects now have them working. How can I accomplish this?
Parent Pom
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.0</version>
<inherited>true</inherited>
<configuration>
<providerImplementations>
<cvs>cvs_native</cvs>
</providerImplementations>
<systemProperties>
<property>
<name>maven.scm.perforce.clientspec.name</name>
<value>${perforceClientSpec}</value>
</property>
</systemProperties>
</configuration>
</plugin>
Child Pom still needs this but I don't want to have to do this if I can avoid it:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
</plugin>
<pluginManagement> section is intended to configure project builds that inherit from this one. However, this only configures plugins that are actually referenced within the plugins element in the children (so you have to explicitly specify them, as you indicated). See more here.
If you want to avoid this, you can put this information into <build> section like this:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.0</version>
<configuration>
<...>
</configuration>
<executions>
<...>
</executions>
</plugin>
</plugins>
</build>
Instead of using pluginManagement, try using just <plugins> tag. It should be auto inherited. You may optionally override configuration in child pom. Check that by mvn help:effective-pom
You can't avoid naming the plugin in the child pom, cause how should maven know which plugin are you using. The pluginManagement section is intended for defining the versions of plugin furthermore it's used to define a default configuration.

Resources