How to use Maven for complex build processes
Tip #1: Use profile activation conditions to do different things based on Operating System
In the below example on a mvn clean the message I will only run on Linux is ran on Linux but not on Mac.
<profiles>
<profile>
<id>platform-unix</id>
<activation>
<os>
<family>unix</family>
<name>!mac os x</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>clean-message</id>
<phase>clean</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<echo message="I will only run on Linux"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</os>
</activation>
</profile>
</profiles>
Tip #2: Use maven-dependency-plugin if you need to download dependencies to custom locations in your project
In the below example, every dependency with a scope set of
runtime will be copied to lib directory of the project except for the artifacts with the ID of coreTest and libTest.<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.basedir}/lib</outputDirectory>
<excludeArtifactIds>coreTest,libTest</excludeArtifactIds>
</configuration>
</execution>
</executions>
</plugin>
Tip #3: You don’t need to have Maven dependencies specified to use maven-dependency-plugin to pull to custom locations
In the below example,
myProject.jar and agent-solaris.sh is copied to libdirectory of the project.<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-special-libraries</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.mycompany</groupId>
<artifactId>myProject</artifactId>
<version>1.0.0</version>
<type>jar</type>
<destFileName>myProject.jar</destFileName>
</artifactItem>
<artifactItem>
<groupId>com.mycompany</groupId>
<artifactId>agent</artifactId>
<version>1.0.0</version>
<type>bin</type>
<classifier>solaris-x86_64</classifier>
<destFileName>agent-solaris.sh</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${project.basedir}/lib</outputDirectory>
</configuration>
</execution>
<plugin>
Tip #4: You can download all artifacts matching a given groupId and artifactId list and even include their pom.xml
In this example, it downloads the
pom.xml and jar for my-magic-plugin to lib/pom but strips the version from the jar.<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-special-libraries</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<configuration>
<includeGroupIds>com.mycompany.myapp</includeGroupIds>
<includeArtifactIds>my-magic-plugin</includeArtifactIds>
<copyPom>true</copyPom>
<includeScope>runtime</includeScope>
<stripVersion>true</stripVersion>
<outputDirectory>${project.basedir}/pom</outputDirectory>
</configuration>
</execution>
</executions>
</plugins>
Tip #5: You can download specific files out of a jar and copy them to a location
In this example, we download the
META-INF/MANIFEST.MF out of the my-core-appartifact and put it in target/tmp directory<plugin> <artifactId>maven-dependency-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <execution> <id>unpack-version</id> <phase>compile</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>com.myexample</groupId> <artifactId>my-core-app</artifactId> <version>${project.version}</version> <includes>META-INF/MANIFEST.MF</includes> <outputDirectory> ${project.build.directory/tmp </outputDirectory> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugins>
Tip #6: If you prefer Apache Ant over Apache Maven you can still use it from within Maven
In this example, we call Apache Ant from within Maven to do various things:
- Delete
lib/thirdpartydirectory andlib/generated.jar filesas part of amvn clean - Copy
src/main/my-appandscript.shto the location defined by${my.staging}before a Maven compilation - Create a zip artifact which contains various files and folders at
target/<artifactId>-<version>.zip - Copy the staged
MANIFEST.MFfrom the previous example to target/version.txt
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>clean-project</id>
<phase>clean</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<delete dir="${project.basedir}/lib/thirdparty" />
<delete file="${project.basedir}/lib/generated.jar" />
</target>
</configuration>
</execution>
<execution>
<id>create-staging-area</id>
<phase>process-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<copy todir="${my.staging}">
<fileset dir="${basedir}/src/main/my-app" />
</copy>
<copy file="${basedir}/scipt.sh" todir="${my.staging}" />
</target>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<mkdir dir="${project.build.directory}" />
<zip destfile="${project.build.directory}/${project.build.finalName}.zip">
<fileset dir="${project.basedir}">
<include name="lib/**" />
<include name="my-app.cmd" />
<include name="my-app" />
</fileset>
<fileset dir="${project.build.directory}">
<include name="documentation/**" />
<include name="xsd/**" />
<include name="version.txt" />
</fileset>
</zip>
</target>
</configuration>
</execution>
<execution>
<id>copy-versionnumber</id>
<phase>prepare-package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<copy tofile="${project.build.directory}/version.txt">
<fileset file="${project.build.directory}/tmp/META-INF/MANIFEST.MF" />
</copy>
</target>
</configuration>
</execution>
</executions>
</plugin>
Tip #7: You can attach any file to be included as an artifact in the built project
For the zip we created in the previous example, attach it to the generated Artifact.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>
${project.build.directory}/${project.build.finalName}.zip
</file>
<type>zip</type>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
Tip #8: If you want to simplify the install of your product, consider building for a packaging tool like RPM, NPM, Docker etc
In the example, below we are building an RPM for Red Hat Linux / Fedora / CentOS to allow for our CLI application to be easily installed with
yum install <app name>.rpm or yum install <app name> if we publish to a Yum repository.<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1-alpha-4</version>
<executions>
<execution>
<id>attach-rpm</id>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<license>All rights reserved</license>
<group>My Company</group>
<needarch>x86_64</needarch>
<version>${project.version}</version>
<name>a-cli-app</name>
<vendor>My Company</vendor>
<summary>My CLI appplication turns water into wine.</summary>
<description>Through an effective propaganda campaign we will make you believe that water = wine all with a single metaphorical push button on your Linux terminal</description>
<defineStatements>
<defineStatement>__os_install_post %{nil}</defineStatement>
</defineStatements>
<mappings>
<mapping>
<directory>${app.home}/lib</directory>
<filemode>755</filemode>
<username>cliuser</username>
<groupname>cligroup</groupname>
<sources>
<source>
<location>${project.basedir}/lib</location>
</source>
</sources>
</mapping>
<mapping>
<directory>${app.home}</directory>
<filemode>755</filemode>
<username>cliuser</username>
<groupname>cligroup</groupname>
<sources>
<source>
<location>${project.basedir}/cli-app</location>
</source>
</sources>
</mapping>
<mapping>
<directory>${app.home}</directory>
<filemode>755</filemode>
<username>cliuser</username>
<groupname>cligroup</groupname>
</mapping>
</mappings>
</configuration>
</plugin>