Search code examples
mavenpom.xmlversioningparent-pom

Avoid wrong version interpolation if child's pom version is different from those of the parent's aggregator pom and its sub modules


Problem description

We have a Maven aggregator pom with some child poms (modules) all having the same version:

pom.xml (parent zoo, version 2.0.0)
|-- pom.xml (child module cat, version 2.0.0)
|-- pom.xml (child module dog, version 2.0.0)
|-- ...

Within the dependency management section all children are declared with the project version to ease declaration of dependencies. The parent pom looks like

<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
<packaging>pom</packaging>

<modules>
  <module>cat</module>
  <module>dog</module>
</modules>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.acme</groupId>
      <artifactId>cat</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- other child modules go here -->
  </dependencies>
</dependencyManagement>

The child poms are defined as

<parent>
  <groupId>com.acme</groupId>
  <artifactId>zoo</artifactId>
  <version>2.0.0</version>
</parent>

<groupId>com.acme</groupId>
<artifactId>cat</artifactId>

<dependencies>
  <dependency>
    <groupId>com.acme</groupId>
    <artifactId>dog</artifactId>
  </dependency>
</dependencies>

There is another pom which declares the parent pom as its parent too (inheritance) but is not listed as sub module in this parent (no aggregation). This pom has a different version.

<parent>
  <groupId>com.acme</groupId>
  <artifactId>zoo</artifactId>
  <version>2.0.0</version>
</parent>

<groupId>com.acme</groupId>
<artifactId>boo</artifactId>
<version>1.0.0</version>

<dependencies>
  <dependency>
    <groupId>com.acme</groupId>
    <artifactId>dog</artifactId>
  </dependency>
</dependencies>

Actually we have expected that the version of the dependency com.acme.dog is pulled from the dependency management section of the parent pom com.acme.zoo and is equal to 2.0.0. However the Maven documentation on project interpolation and variables says

One factor to note is that these variables are processed after inheritance as outlined above. This means that if a parent project uses a variable, then its definition in the child, not the parent, will be the one eventually used.

That is: in the reactor build the variable ${project.version} used in the dependency management section of the parent pom com.acme.zoo is evaluated with respect to com.acme.bar and equal to 1.0.0 what is not as intended.

Note

There is a workaround with using a variable in the parent pom which has to be kept in sync with the parent pom versions. However, this solution is incompatible with the Maven Release Plugin.

Question

How can we achieve the desired behaviour

  • aggregator pom with children having the same version
  • declaration of children in the dependency management section to ensure that all dependencies have the same version
  • use of inheritance together with different versions
  • compatibility with maven-release-plugin

without the pitfalls of project interpolation of variables?


Solution

  • The maven release plugin is able to change the versions of the dependencies managed in the parent pom.

    So if you define your maven parent like this:

    <groupId>com.acme</groupId>
    <artifactId>zoo</artifactId>
    <version>2.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <modules>
      <module>cat</module>
      <module>dog</module>
    </modules>
    
    <dependencyManagement>
     <dependencies>
       <dependency>
         <groupId>com.acme</groupId>
         <artifactId>cat</artifactId>
         <version>2.0.0-SNAPSHOT</version>
       </dependency>
       <!-- other child modules go here -->
     </dependencies>
    </dependencyManagement>
    

    As you see the versions of the parent and the managed dependency are the same. I set them to a SNAPSHOT version because the release plugin will create the final versions on release:perform

    Your child poms can stay as you had them.

    Because in your setup, your parent project is also the reactor you can then call

    mvn release:perform -DautoVersionSubmodules=true
    

    which will update the version of the parent in all submodules when you run this command. That option is essentially the same as if you run

    mvn versions:update-child-modules
    

    meaning it will change the child poms.

    After you run the mvn release:perform command your parent pom will look like this:

    <groupId>com.acme</groupId>
    <artifactId>zoo</artifactId>
    <version>2.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <modules>
      <module>cat</module>
      <module>dog</module>
    </modules>
    
    <dependencyManagement>
     <dependencies>
       <dependency>
         <groupId>com.acme</groupId>
         <artifactId>cat</artifactId>
         <version>2.0.1-SNAPSHOT</version>
      </dependency>
      <!-- other child modules go here -->
     </dependencies>
    </dependencyManagement>
    

    and your child poms like this

    <parent>
      <groupId>com.acme</groupId>
      <artifactId>zoo</artifactId>
      <version>2.0.1-SNAPSHOT</version>
    </parent>
    
    <groupId>com.acme</groupId>
    <artifactId>cat</artifactId>
    
    <dependencies>
      <dependency>
        <groupId>com.acme</groupId>
        <artifactId>dog</artifactId>
      </dependency>
    </dependencies>
    

    The final versions will only exist in the tag created by the release:prepare command.

    PS: You may define other versions for the final and the next development version when they are prompted after running the release:prepare command.