Parent POM : share common configuration files across child projects - maven

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!

Related

How does Maven know where input files come from and where output files go during the generate-sources phase?

I am new to Maven. I have a project which includes a grammar definition file that is read by SableCC, which is a parser generator. SableCC reads a grammar file and generates Java source code that, once compiled, creates a parser. The parser is used by the rest of my project.
I don't think my question is a SableCC question, as I've seen similar POM.XML files that use Antlr (another parser generator that is comparable to SableCC) and the <plugin> section of that POM.XML looks nearly the same as my <plugin> section in my POM.XML file.
Here is my project's file structure, identifying those parts that are used by SableCC:
Here is my complete POM.XML file, which I've cobbled together based on the plugin examples I've seen so far (and in particular, the one for SableCC):
<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>com.mbm</groupId>
<artifactId>properties</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Properties</name>
<description>Define and process program arguments</description>
<dependencies>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sablecc-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
My question is how does the SableCC plugin know where the input file is and where the 4 categories of generated output Java source files (analysis, lexer, node and parser) should go? I suspect I need to add more Maven "stuff" to my POM file, but I don't know what.
You can see the plugin configurations here:
https://github.com/tychobrailleur/sablecc-maven-plugin
More specifically:
sourceDirectory: Directory containing grammar files. By default, ${basedir}/src/main/sablecc
outputDirectory: Directory containing the generated sources. By default, ${project.build.directory}/generated-sources/sablecc
You can also extract the JAR and see these configurations in the plugin.xml file:
<configuration>
<outputDirectory implementation="java.lang.String">${project.build.directory}/generated-sources/sablecc</outputDirectory>
<timestampDirectory implementation="java.lang.String">${basedir}/target</timestampDirectory>
<sourceDirectory implementation="java.lang.String">${basedir}/src/main/sablecc</sourceDirectory>
<project implementation="org.apache.maven.project.MavenProject">${project}</project>
<staleMillis implementation="int" default-value="0">${lastModGranularityMs}</staleMillis>
</configuration>

Can I have maven artifact run maven plugin when it is installed?

I have created a Maven plugin (called unpackTemplates) that unpacks a dependency jar file and copies resource files (in this case, templates) from it into a specific location in a project.
Right now, I put the following into the pom file of every project that has a dependency with templates. It looks like:
<project>
<groupId>DuriansAreDope</groupId>
<artifactId>DuriansAreDope</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugin>
<groupId>mycorp</groupId>
<artifactId>unpackTemplates</artifactId>
<version>1.0</version>
<executions>
<execution>
<configuration>
<groupId>com.mycorp.lib</groupId>
<version>1.0</version>
<artifactId>Lib-With-Templates</artifactId>
</configuration>
<goals>
<goal>unpackTemplates</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
<pluginManagement>....</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>com.mycorp.lib</groupId>
<artifactId>Lib-With-Templates</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
The above project pom works for us. It calls the plugin and the plugin does it's job. However, we'd like to avoid adding the plugin section to the pom of every project.
It would make more sense to put that plugin section in the dependencies pom. This way the project pom does not need to be modified beyond adding the <dependency> tags as usual. And the dependency has it's plugin run wherever it is installed.
I've seen that the pom file for Gson contains a <build><plugins>...</plugins></build> section in it. When I give my dependencies the following pom files, however, the plugin is not run (although the dependency is found, downloaded, installed, etc correctly).
<project>
<groupId>com.mycorp.lib</groupId>
<artifactId>Lib-With-Templates</artifactId>
<version>1.0</version>
<build>
<plugin>
<groupId>mycorp</groupId>
<artifactId>unpackTemplates</artifactId>
<version>1.0</version>
<executions>
<execution>
<configuration>
<groupId>com.mycorp.lib</groupId>
<version>1.0</version>
<artifactId>Lib-With-Templates</artifactId>
</configuration>
<goals>
<goal>unpackTemplates</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
<pluginManagement>....</pluginManagement>
</build>
</project>
Any ideas what I'm doing wrong, or if the Gson pom is simply doing something else entirely?
(NB: The groupId/version/artifactIds in <configuration> are necessary because they are (string) parameters to the plugin; presumably if I got the run-straight-from-dependency approach working I could refactor them away, but again, it's not even running the kludgy version with parameters.)
two points:
First I agree with khmarbaise in that you don't need a plugin of your own for those tasks. To unpack to a specific location you can use dependency:unpack-dependencies and outputDirectory parameter.
If you need more configuration you can use the assembly plugin to structure your artifact (which you want to unpack).
For the second point it seems to me that you want to use the contents of your lib-with-templates in many projects. Why don't you add the plugin and dependency to a parent pom which you include in every pom where you need it? Then you don't need to declare it in "every pom". If you don't really need it in every pom you can put it in a profile and choose a proper activation for it.
HTH!

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.

Maven multi-module project - copying all "package" JARS from submodules into parent/target/

I have a maven project with quite a few submodules. What I am looking for is a way to get all the .jar files produced by the sub-modules included in the aggregating POM's /target/ directory, so they can be conveniently used afterwards.
They don't need to be merged. Preferably not, but if they must be then that is ok.
Don't care about dependancies
This is primarily for convenience, at this point
A basic version of what I am looking at doing:
Prj1/pom.xml => prj1/target/proj1.jar (and classes/generated-sources/etc)
Prj2/pom.xml => prj2/target/proj2.jar
Main/pom.xml =>
main/target/proj1.jar
main/target/proj2.jar
... classes/generated-sources not needed at all,
... but they could be combined here. I assume they will be
I've been reading, and using some suggestions from SO as well. So far I haven't found a way to do this, but I'm sure it is there.
edit:
I've given up on getting this to work in a simple way, for all included subprojets. The best answer I have so far is using the dependancy plugin to manually specify (again), each and every sub-module to be included. The idea was to be able to configure the POMs easily for the dozens of clients, simply including the modules necessary and then having it magically stick all the sub-modules's jars in one location. Maven is pretty nice, when you don't do much with it, but the angle bracket tax is incredible when you try.
I still find it odd that such standard-seeming tasks (judging from the questions asked on SO, and what any normal project would do) are so difficult. Is maven3 better?
You could try the maven-dependency-plugin:copy plugin:goal.
You will have to add this to the pom of all submodules that you want to copy.
EDIT: Or just in the parent pom (see comments).
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-artifact</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>${project.packaging}</type>
</artifactItem>
</artifactItems>
<outputDirectory>../Main/target/dependencies</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
If you do put this in the parent pom, keep in mind that it will work nicely only if your whole project is one module deep. Meaning that if the the modules under the parent module have their own submodules, they will not end up in the desired folder. Your folder structure will look like this:
- Parent
- Module 1
- Sub module 1
**Main**
- Module 2
**Main**
To prevent this, create a dedicated module with above configuration, and specify manually each module that you want to copy. This way all modules, no matter how deep they are will end up in one folder.
I have spent the entire day trying to solve this... and finally it works, so even though I am on a tough schedule, I will share it here, if I can only save someone's frustration in the future...
Step 1. Create an additional Maven project that will be a parent of all the projects that you want to copy together. Let's call this project parent.
This project is needed to tell Maven, which projects to build together. Also, you will declare in your other projects that their parent is parent, so they will see the MyDir property that you define here.
<groupId>com.your.domain</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>
<properties>
<MyDir>/your/path/to/copy/to</MyDir>
</properties>
<modules>
<module>../project1</module>
<module>../project2</module>
<module>../project2</module>
</modules>
Step 2. For every project that you want to be copied to the same location, specify that it's parent is the parent project (make sure you specify correct groupId and artifactId and version of your parent project):
<parent>
<groupId>com.your.domain</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
<relativePath>../parent</relativePath>
</parent>
And also, for each of these projects, also specify the jar and dependency plugins settings, like so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<outputDirectory>${MyDir}</outputDirectory>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.your.domain.Program</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${MyDir}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Then just run mvn install on the parent project. Bam!
P.S. The above assumes all projects are located in the same directory (parent project next to children), but you can change relative paths as you wish.
One way to achieve this would be to use the moduleSet option of maven assembly plugin.
You could create an assembly descriptor like this (a variation of the example in the link) and use it in assembly plugin declaration in the pom.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>bin</id>
<formats>
<format>dir</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<moduleSets>
<moduleSet>
<useAllReactorProjects>true</useAllReactorProjects>
<binaries>
<outputDirectory>/</outputDirectory>
<unpack>false</unpack>
</binaries>
</moduleSet>
</moduleSets>
</assembly>

How to share POM fragments between different POMs

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.

Resources