Oct 13, 2009

Manage dependent projects in Maven

If you have been used Maven for a long time you probably know that it supports both project inheritance and aggregation. There is detailed description with samples on Maven site how both of them work. But sometimes you have really separated projects one of them depends on another, but both of them are continuously in development phase. So you need to use latest build artifacts of the base project without wasting time to run all its tests. There are some ways to resolve this issue. Lets say that we have project B witch depends on project A. Both of them contain many modules and base pom.xml files to build their own hierarchy.

So the first natural way to resolve the issue is to build projects in the right order manually (A and then B). But if project A was not changed from the last time then you will just waste your build time. Also in this case you should use some commands instead of one (it may be problematic for your CI server). Maven has release plugin that may help you. You just need to setup internal (company or project level) Maven repository and configure releasing policy for your project build. There are a lot of players on the market of Maven repositories: Archiva, Nexus, Proximity, Artifactory, etc. All of them supports local releasing of project artifacts. When setting releasing policy for the project you should decide if you will support only stable versions or snapshots as well. In case of snapshots Maven may generate build time version of artifacts after each release build. After all parts are configured you may use release plugin commands to publish project artifacts to the internal repository and make them available for all local repositories. So you need to build project A only when changes are made (for example on each commit to VCS). When you build project B Maven will automatically check for new versions of the project A artifacts and download them to the local repository if changes was found.

But sometimes your project contains modules that are not written in Java and are very platform dependent (for example install C++ library code on each build). In this case the previous solution can't be applied. You need to change main pom.xml of the project B to add information about dependency on project A, but you want to apply it only if project A sources are available on your machine. Use following profile for this purpose:

<profile>
<id>Build A project</id>

<modules>
<module>${a.relative.path}</module>
</modules>

<activation>
<property>
<name>a.relative.path</name>
</property>
</activation>
</profile>

This profile will be activated only if system property a.relative.path is passed. Note that value of this property should be related to root of project B (for example ../../a). Maven will build project A as part of each project B build. But how to avoid running tests in the project B? We use aggregation so no settings are inherited from the project B pom.xml except system properties. So, lets use them. Add following profile to the project A base pom.xml:

<profile>
<id>Disable tests for external build</id>
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
<activation>
<property>
<name>a.test.skip</name>
</property>
</activation>
</profile>

This profile will be activated when a.test.skip system variable is present and will disable all tests execution like -Dmaven.test.skip does. If you need additional settings for quick build of project A set them using system properties as well. Now you may build both projects with one Maven command: mvn -Da.relative.path=../../a -Da.tesk.skip clean install. If you don't have access to the project A sources then rely on the current version of the artifacts from local repository and build phase of the project A will be skipped.
Maven is very powerful build tool and even complex scenarios may be implemented using it. Develop with pleasure.

No comments: