Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to package multi-module maven project with nested modules

I have a multi-module project in Maven that compiles correctly, but does not package correctly.

In the main maven pom.xml, I include the modules like this:

<modules>
    <module>module1</module>
    <module>module1/test</module>
    <module>module2</module>
    <module>module2/test</module>
    ...
    <module>moduleN</module>
    <module>moduleN/test</module>
</modules>

Unlike maven conventions, this project has to have its test source trees compiled as a separate modules, because the compile dependencies for the test code differ from the dependencies of the main source code. So the solution is to setup all the test modules to depend on whatever main source modules to ensure they are being compiled in the correct order.

The directory structure for this project is very non-standard. I have placed the pom.xml's in locations where I think they belong. I cannot change this directory structure though - it is out of my control. It is like this:

/
+- pom.xml
+- module1/
   +- pom.xml
   +- src/
      +- com/
          +- company/
             +- Module1.java
   +- test/
      +- pom.xml
      +- src/
          +- com/
             +- company/
                +- Module1Test.java
...

Here is what the pom.xml for a regular source module looks like. This works perfectly:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.company</groupId>
        <artifactId>project</artifactId>
        <version>5.3.1-SNAPSHOT</version>
    </parent>

    <artifactId>module1</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>modulex</artifactId>
            <version>${product.version}-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <sourceDirectory>src</sourceDirectory>

        <directory>target</directory>
        <outputDirectory>target/classes</outputDirectory>
        <testOutputDirectory>target/test-classes</testOutputDirectory>

        ...
    </build>

</project>

Here is the pom.xml for a test module:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.company</groupId>
        <artifactId>project</artifactId>
        <version>5.3.1-SNAPSHOT</version>

        <!-- I was forced to do this because this module was 2 levels deep -->
        <relativePath>../../pom.xml</relativePath>
    </parent>

    <artifactId>module1.test</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>module1</artifactId>
            <version>${product.version}-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <sourceDirectory>src</sourceDirectory>
        <testSourceDirectory>src</testSourceDirectory>

        <directory>target</directory>
        <outputDirectory>target/classes</outputDirectory>
        <testOutputDirectory>target/test-classes</testOutputDirectory>

        ...
    </build>

</project>

However, since the test module is a sub-directory of the main module, I am getting the following errors once it tries to package module.test:

[ERROR] Failed to execute goal on project module1.test: Could not resolve dependencies for project com.company:module1.test:jar:5.3.1-
SNAPSHOT: Could not find artifact com.company:module1:jar:5.3.1-SNAPSHOT in repo1.maven.org (http://repo1.maven.org/maven2/) -> [Help 1]

For some reason, Maven thinks it needs to fetch this jar from a repository, even though it just built it previously....

Do I need to move the pom.xml in module1/test to a parent level like the others? How can I fix this without moving the pom.xml's?

Thanks

like image 964
egervari Avatar asked Jun 28 '12 12:06

egervari


1 Answers

The first thing is that in Maven you have different folders for production code and test code.

src/main/java

src/test/java

Furthermore the code in src/main/java will be compiled by the maven-compiler-plugin and it's goal: compile whereas the test code src/test/java will be compiled also by the maven-compiler-plugin but with its goal: testCompile.

If you have different dependencies for test and production therefor you can define a scope along with the dependency like this:

   <dependency>
     <groupId>org.testng</groupId>
     <artifactId>testng</artifactId>
     <version>6.2.1</version>
     <scope>test</scope>
   </dependency>

which means this will be only useable within Test code area and will not be packaged. If you don't define the scope:

   <dependency>
     <groupId>org.testng</groupId>
     <artifactId>testng</artifactId>
     <version>6.2.1</version>
   </dependency>

This is a dependency which is used for production and will be packaged if you have a packging type like "war" etc.

I have seen a thing like this which i assume is a typo:

<dependencies>
    <dependency>
        <groupId>com.company</groupId>
        <artifactId>modulex</artifactId>
        <version>${product.version}-SNAPSHOT</version>
    </dependency>
</dependencies>

which is simply wrong, cause you have to use:

  <dependencies>
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>modulex</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

The ${product.version} is not defined and you should use ${project.version} instead!!

I recommend not to jump two levels within a single step... You should change the structure of your project like this:

/
+- pom.xml
+- module1/
   +- pom.xml
   +-- module1-src
        +- pom.xml
        +- src/main/java
            +- com/
               +- company/
                 +- Module1.java
   +-- module1-test
        +- pom.xml
        +- src/
          +- com/
             +- company/
                +- Module1Test.java
...

Based on the above structure you will get things like:

<modules>
    <module>module1</module>
    <module>module2</module>
    ...
    <module>moduleN</module>
</modules>

and in module1:

<modules>
    <module>module1-src</module>
    <module>module1-test</module>
</modules>

Otherwise you start fighting against Maven where you will loose the combat.

like image 167
khmarbaise Avatar answered Sep 28 '22 08:09

khmarbaise