Saturday, November 04, 2006

Solution to the Maven2 version number problem

In the past few days, I've ranted profusely about the difficulty of changing version numbers of modules in Maven2. As things stand currently, when a module references its parent, it must explicitly state the parent's version in hard-coded form. Once that import is done, the natural thing to do is to define the current module's version by the version of the parent. The down side is that, when the parent version changes, this must be reflected on all child modules. If your project makes simultaneous releases of all its modules in one sweep, as many projects seem to do, then you must manually change the version number of each module by changing the version number of the parent reference in each module. This is a time consuming and error prone process at best.

I recently experimented with a solution to the above problem. It's now part of the SLF4J project (which has 10 or so modules).

The idea is to declare the version number for the whole project as a property, namely "aversion" (pun intended), in the parent pom. The parent pom's own version number can be anything as long as it ends with "SNAPSHOT".

Here is an excerpt from SLF4J parent pom:
<project>
...
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>SLF4J</name>
<properties>
<aversion>1.1.0-RC0</aversion>
</properties>
....
</project>

Child modules' version is specified via the ${aversion} property. Children's reference to their parent's version is hard coded. However, since the parent pom's version is a SNAPSHOT, child modules will see the changes in the parent pom. In particular, if the parent pom changes the value of ${aversion}, the children will see the change.

Here is the pom.xml file for the slf4j-api module.
<project>
<parent>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${aversion}</version>
<packaging>jar</packaging>
<name>SLF4J API Module</name>
...
</project>

Unless I've missed something, this hack seems to work just fine. I would be interested to know whether there is a downside it.

17 comments:

Anonymous said...

Hello,

I'm currently in the process of selling my soul to Maven2 and have been experimenting with a small project that uses SLF4J. As my project is a component I opted for the slf4j-api as a dependency and not an specific implementation. The problem is that Maven can't compile my project because it can't find "org.slf4j:slf4j-parent:pom:1.0-SNAPSHOT".

An inspection of the Maven2 repository confirms that no JAR exists for slf4j-parent (see http://www.ibiblio.org/maven2/org/slf4j/slf4j-parent/1.0-SNAPSHOT/), which is apparently a dependency of slf4j-api.

I will probably download the SLF4J source and do a "mvn install" to get around the problem by installing into my local repository. Below is a copy of my pom.xml for what it's worth.

Have you cleared out your local repository and tried to compile a project with slf4j as a dependency?

<?xml version="1.0"?>
<project>

<modelVersion>4.0.0</modelVersion>
<groupId>com.ostendorf</groupId>
<artifactId>repository-jdbc-driver</artifactId>
<name>Repository JDBC Driver</name>
<version>0.0.1</version>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>com.ostendorf</groupId>
<artifactId>repository-core</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.1.0-RC0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Ceki said...

Ooops. Given that SNAPSHOTs are forbidden on ibiblio, the ${aversion} trick breaks projects such as SLF4j which are publicly accessible on ibiblio.

Here is a perl script named version.pl capable of changing the appropriate version tags in pom.xml files.

For example, the following command correctly bumped version tags to 1.1.0-RC1 in the SLF4J project:

find . -name "pom.xml"|xargs perl version.pl 1.1.0-RC1

By the way, 1.1.0-RC1 was recetly released in response to the comments voiced above. Is is available on ibiblio.

Omar Cruz said...

Hi Sir Ceki Gülcü!

Actually, I'm here to say that I'm using log4j as a logger for the programs I'm doing. Would you be nice if I could throw some question in the future about that logger that you develop?

Many thanks,

Omar from the Philippines

Anonymous said...

Hi Ceki,

> If your project makes simultaneous releases of all its modules in one sweep, as many projects seem to do, then you must manually change the version number of each module by changing the version number of the parent reference in each module.

not really, if you use maven "the right way" also for releases you will never have to modify versions manually!
This is the task for the release plugin: for a multi-module build you should run "mvn release:prepare" in order to have all the versions (modules and parent) automatically changed for you. You should never manually change a -SNAPSHOT to a release version actually.

Ceki said...

Thanks for the info. However, the 'mvn release:prepare' command fails with "The project org.slf4j:slf4j-parent isn't a snapshot (1.1.0)".

It looks like as soon as you go astray of the path expected by the release plugin-in, you are back to square one.

Anonymous said...

Hi,

just a small comment. The trick with aversion doesn't solve the problem when you need to branch your project, i.e. when you need to have 2 active parent poms.

Marcel

Anonymous said...

Why don't you use the maven-release-plugin? It does exactly the job you are describing.

Anonymous said...

find . -name pom.xml | xargs perl -i -pe 's/0.17-SNAPSHOT/0.18-SNAPSHOT/'

Anonymous said...

find . -name pom.xml -exec sed -i "s/1.0-SNAPSHOT/1.1-SNAPSHOT/" {} \;

Unknown said...

mvn release:prepare is the best solution for this problem. However, ensure that the parent pom (and the module poms) are SNAPSHOT versions.

Anonymous said...

The release plugin might help (if it actually worked, rather than crashing as it usually does).

But that doesn't solve the real problem: merging. Having endless copies of the version number all over the place means lots of conflicts when bringing branches back together. Is there any way to leave the version out of the parent? Surely it can get it using the relativePath?

Anonymous said...

I know this is an older post, but with Maven 2.0.9 I have simply been omitting the version element from the individual modules as they will inherit the version from their parent. This works with the groupId as well if your modules share the same groupId as their parent.

Alski said...

You can't ommit version from the parent reference in the child. This forces you to specify the matching version. I think you misread the original post. It flat out doesn't work.

Half-Cooked said...

I have tried another way of managing the version. Which is to provide the version as command line propertey.
Use this property to version the parent POM .
Do not specify the version of child modules (they inherit from parent). while declaring the parent use the version specified in command line.

This works with any version you give in the command line. Only problem here is that the version is not configurable item. But that can be solved if we read the version from a config-managed properties file.

Hasan Ceylan said...

Hi Ceki,

>>>
It looks like as soon as you go astray of the path expected by the release plugin-in, you are back to square one.

I think what you should do is to keep the child pom's parent versions as snapshot version instead of release versions.

Then you should be able to release the super pom with all the child poms.

I do this all the time. If you need help just ping me at hceylan at batoo dot org

Greetings from Istanbul


if you need

Werner Keil said...

We had this problem in a large Maven 2 project in India. And it still seems unsolved or worse in Maven 3, which I use for Eclipse UOMo.

Greetings from Bangalore,
Werner

Anonymous said...

Hi, this problem is solved with mvn versions-plugin: Do mvn versions:set -DnewVersio=1.1.8 and you are done.