Finding the root directory of a multi module Maven reactor project
Asked Answered
V

16

89

Is there an easy way to find the root of a multi-module Maven project, like Gradle's rootDir?


Background:

I want to use the maven-dependency-plugin to copy artifacts from all sub-modules of my multi-module project to a directory that is relative to the root directory of the entire project.

That is, my layout looks similar to this, names changed:

to-deploy/
my-project/
    module-a/
    module-b/
    more-modules-1/
        module-c/
        module-d/
    more-modules-2/
        module-e/
        module-f/
    ...

And i want all the artifacts to be copied from the target-directories of their respective modules to my-project/../to-deploy so i end up with

to-deploy/
    module-a.jar
    module-b.jar
    module-c.jar
    module-d.jar
    module-e.jar
    module-f.jar
my-project/
    ...

I could do it with a relative path in each module, like so:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy</id>
                    <phase>install</phase>
                    <goals>
                        <goal>copy</goal>
                    </goals>
                    <configuration>
                        <artifactItems>
                            <artifactItem>
                                <groupId>${project.groupId}</groupId>
                                <artifactId>${project.artifactId}</artifactId>
                                <version>${project.version}</version>
                                <type>jar</type>
                                <outputDirectory>../../to-deploy</outputDirectory>
                            </artifactItem>
                        </artifactItems>
                    </configuration>
                </execution>
            </executions>
        </plugin>

But i'd rather not specify a relative path in the <outputDirectory> element. I'd prefer something like ${reactor.root.directory}/../to-deploy, but i can't find anything like this.

Also, i'd prefer if there was some way to inherit this maven-dependency-plugin configuration so i don't have to specify it for each module.

I also tried inheriting a custom property from the root pom:

<properties>
    <myproject.root>${basedir}</myproject.root>
</properties>

But when i tried to use ${myproject.root} in the module POM's, the ${basedir} would resolve to the basedir of the module.

Also, i found http://labs.consol.de/lang/de/blog/maven/project-root-path-in-a-maven-multi-module-project/ where it's suggested that each developer and presumably the continuous integration server should configure the root directory in a profiles.xml file, but i don't consider it a solution.

So is there an easy way to find the root of a multi-module project?

Violetteviolin answered 21/6, 2010 at 12:34 Comment(0)
S
81

use ${session.executionRootDirectory}

For the record, ${session.executionRootDirectory} works for me in pom files in Maven 3.0.3. That property will be the directory you're running in, so run the parent project and each module can get the path to that root directory.

I put the plugin configuration that uses this property in the parent pom so that it's inherited. I use it in a profile that I only select when I know that I'm going to run Maven on the parent project. This way, it's less likely that I'll use this variable in an undesired way when I run Maven on a child project (because then the variable would not be the path to the parent).

For example,

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-artifact</id>
            <phase>package</phase>
            <goals>
                <goal>copy</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>${project.artifactId}</artifactId>
                        <version>${project.version}</version>
                        <type>${project.packaging}</type>
                    </artifactItem>
                </artifactItems>
                <outputDirectory>${session.executionRootDirectory}/target/</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
Sundew answered 6/10, 2011 at 15:30 Comment(3)
${session.executionRootDirectory} was the magic bullet for me too. Why is this not in maven.apache.org/pom.html?Setup
Try with ${project.basedir} if other maven environment variables such as ${session.executionRootDirectory} did not work.Ardin
I tried ${session.executionRootDirectory} with Maven 3.3.9 and it doesn't work anymore. You need to use ${user.dir} instead. In my opinion this option is unsatisfactory because it means I can only run the parent and not submodules independently. For that to work you need this #1012902Policyholder
M
78

Since Maven 3.3.1, you can use ${maven.multiModuleProjectDirectory} for this purpose. (thanks to https://mcmap.net/q/246292/-maven-variable-for-reactor-root)

edit: this seems to only work properly when you have a .mvn folder at the root of your project.

Montford answered 28/3, 2018 at 7:11 Comment(16)
Could you link to the release notes or docs for this? I can't find them.Ragged
@ChristofferHammarström for some reason that only shows up in the release notes of 3.3.9 as a bug fix: maven.apache.org/docs/3.3.9/release-notes.html But digging further in Maven's Jira project, this is where it gets introduced, and is indeed marked as fixed-for 3.3.1: issues.apache.org/jira/browse/MNG-5767Unwitnessed
Sadly, they "fixed" it and now it just points to the current subproject, not the actual multiproject root. So it's no better than the other options.Verrucose
In which version did this change @kaqqao?Unwitnessed
I maybe said it wrong, but I was referring to this. It used to point to the actual root (in some cases at least) prior to 3.3.9, and it now always points to the dir where Maven was executed. So it's not the root if executed from a subproject... so no better than ${session.executionRootDirectory} :(Verrucose
@Verrucose yeah it's odd. I can't say I understand exactly what that bug reports, but when I try mvn help:evaluate -Dexpression=maven.multiModuleProjectDirectory it works for me ¯\_(ツ)_/¯ edit: I realised it seems to only work when I have an .mvn folder at the top of my project!Unwitnessed
@GrégoryJoseph Ah, that explains it! Thanks for the update. Anything special needed in the .mvn directory or just the presence of the dir is enough?Verrucose
@Verrucose i haven't had a chance to test that out, let me know what you find!Unwitnessed
Worked for me without .mvn folder in the root.Fallon
Tried with maven 3.6.3. Conclusion: 1) there has to be a .mvn folder, but it can be empty 2) mvn reports the root directory for entire project properly even running the command from a child directory (module).Pershing
Please do NOT use this property at any time. This is an internal property (implementation detail). It may disappear with a release.Bitner
@GrégoryJoseph Ḯ'm sorry, i'm unmarking this answer as the definitive solution for now, as it seems unclear that it's entirely correct.Ragged
@ChristofferHammarström that's fair enough, especially given Michael-O 's answer just above -- I haven't been using Maven in anger recently enough to need/verify this.Unwitnessed
@Bitner any chance you have an official answer, then? And/or a trail folks who actually need this can follow (e.g open bug/feature request) ?Unwitnessed
@GrégoryJoseph issues.apache.org/jira/browse/MNG-7038Bitner
What a happy accident, that I stumble across this, just as it was resolved yesterday and pre-released with mvn 4.0.0-alpha 6 :)Linkman
V
34

Something which I have used in my projects is to override the property in the sub-module poms.

    root:           <myproject.root>${basedir}</myproject.root>
    moduleA:        <myproject.root>${basedir}/..</myproject.root>
    other/moduleX:  <myproject.root>${basedir}/../..</myproject.root>

This way you still have the relative paths, but you can define a plugin once in the root module, and your modules will inherit it with the right substitution for myproject.root.

Valaree answered 13/1, 2012 at 9:7 Comment(5)
Are you going to manage all these "/.." and "/../.."? I mean, sure this solution works, but it hides the actual structure of your project.Gamecock
I think this should be the accepted answer. This even works when you just build an individual module.Togoland
This solution works when using eclipse and m2e as it doesn't require any exotic plugins.Officialism
Thanks! This is the most elegant way for simple projects. No additional plugins, just explicit override. +1Resurrectionist
Simple and easy, works like a charm!Sonics
F
25

There is a maven plugin that solves this particular problem: directory-maven-plugin

It will assign the root path of your project to a property of your choosing. See highest-basedir goal in the docs.

For example:

<!-- Directory plugin to find parent root directory absolute path -->
<plugin>
  <groupId>org.commonjava.maven.plugins</groupId>
  <artifactId>directory-maven-plugin</artifactId>
  <version>0.1</version>
  <executions>
    <execution>
      <id>directories</id>
      <goals>
        <goal>highest-basedir</goal>
      </goals>
      <phase>initialize</phase>
      <configuration>
        <property>main.basedir</property>
      </configuration>
    </execution>
  </executions>
</plugin>

Then use ${main.basedir} anywhere in your parent / child pom.xml.

Fetishist answered 28/11, 2013 at 11:16 Comment(3)
I suggest to avoid this solution: if you don't execute the phase (in the example is initialize) the property won't be solved, so, if you need it for example on a submodule at clean/pre-clean phase you will find yourself duplicating/hacking the config on submodules (already saw it on various projects). Use @karel 's solution unless you have a special requirement (and no, your project is not special).Spearing
This is a good solution. For an earlier phase, you can just specify <phase>pre-clean</phase>.Inca
Even after running the goal explicitely, the property does not resolve for a config of another plugin in the same reactor. I guess it's not as fool proof as it looks. Actually running the initialize lifecycle phase instead of the highest-basedir goal made it work.Linkman
E
7

As others have suggested, directory-maven-plugin is the way to go. However, I found it works best with the directory-of goal, as described here: https://mcmap.net/q/172861/-maven2-property-that-indicates-the-parent-directory.

I prefer that as using highest-basedir didn't work for me with a multi-module project, with nested multi-module poms. The directory-of goal lets you set a property to the path of any module in the whole project, including the root of course. It is also way better than ${session.executionRootDirectory}, because it always works, regardless of whether you build the root or a sub-module, and irrespective of the current working directory where you mvn from.

Edge answered 22/6, 2016 at 10:35 Comment(0)
A
6

Currently, Maven does not provide such the «Root project directory path» property out of the box.
«Root project» means the «topmost» parent project.

Please, refer to the ticket and consider voting for it: [MNG-7038] Introduce public property to point to a root directory of (multi-module) project - ASF JIRA.

Alcove answered 23/8, 2021 at 8:22 Comment(0)
B
3

The following small profile worked for me. I needed such a configuration for CheckStyle, which I put into the config directory in the root of the project, so I can run it from the main module and from submodules.

<profile>
    <id>root-dir</id>
    <activation>
        <file>
            <exists>${project.basedir}/../../config/checkstyle.xml</exists>
        </file>
    </activation>
    <properties>
        <project.config.path>${project.basedir}/../config</project.config.path>
    </properties>
</profile>

It won't work for nested modules, but I'm sure it can be modified for that using several profiles with different exists's. (I have no idea why there should be "../.." in the verification tag and just ".." in the overriden property itself, but it works only in that way.)

Ben answered 2/7, 2013 at 10:44 Comment(3)
So are you saying that the basedir is ${project.basedir}/../../? This only works in the case you're putting the child projects at a specific location inside the parent project. The question is, how to find this path in general.Misgive
The basedir is ` ${project.basedir}/../. Just some unique file is needed there (in my case it's checkstyle.xml). And yes, this works (as I mentioned) for one-level of module nesting. Usually modules are put into subdirectories, so I don't consider here any other cases (but I'm sure something similar will work). The question was how to find something like ${reactor.root.directory}. Here is my solution. But in my case I needed the /config` folder. With a slight modification one can get the root directory as well. And for nested modules several profiles with different exist's also work.Ben
There are different solutions, but I wanted to find a simple one which does not require copy-paste to all modules.Ben
O
2

I encountered similar problem as i needed to copy files between projects. What Maven does is logical because it will keep the pom.xml installed in repository away from hard coded value.

My solution was to put copied dir in a Maven artifact, then employ Ant to extract/copy

Olibanum answered 6/6, 2012 at 10:34 Comment(0)
U
2

Except use absolutely root path, in some case, I have another way to solve the problem.

Use maven plugin combine.children feature, refs: maven plugin

I can write my code:

parent pom:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
          <execution>
              <id>copy-dependencies</id>
              <phase>package</phase>
              <goals>
                  <goal>copy-dependencies</goal>
              </goals>
              <configuration>
                  <includeScope>runtime</includeScope>
                  <outputDirectory combine.children="override">${project.basedir}/to-deploy</outputDirectory>
              </configuration>
          </execution>
      </executions>
    </plugin>
  </plugins>
</build>

or write in plugin management, if parent pom doesn't use this plugin.

child pom:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
          <execution>
              <!--same as parent plugin id-->
              <id>copy-dependencies</id>
              <configuration>
                  <outputDirectory combine.children="override">${project.parent.basedir}/to-deploy</outputDirectory>
              </configuration>
          </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Similar way grandchild pom:

<outputDirectory combine.children="override">${project.parent.parent.basedir}/to-deploy</outputDirectory>
Upgrowth answered 7/2, 2022 at 13:14 Comment(0)
I
1

I'm not aware of a "nice" way to find the root of a multi-module project. But you can maybe improve a bit your current approach.

A first alternative would be to create an additional module directly under the root project, to declare all EARs as dependencies in it and to use dependency:copy-dependencies to copy the dependencies of the module to the to-deploy directory (relatively). Yes the path would still be relative but since the dependency plugin configuration would be centralized, I don't find it that annoying.

A second alternative would be to use the Maven Assembly Plugin instead of the Maven Dependency Plugin to create a distribution using the dir format (this will create a distribution in a directory). This is actually what I would do.

Introject answered 21/6, 2010 at 14:4 Comment(1)
Thank you! Using the assembly plugin would be the best if it was only possible to understand the docs and link it into the build lifecycle. I've just spent an hour trying to make it work, and given up...Ragged
W
1

Another solution would be to use an ant task to write "rootdir=${basedir}" to a target/root.properties in the root project, and then use the Properties Plugin to read that file back in. I haven't tried it myself, but I guess it should work..?

Werewolf answered 14/12, 2010 at 11:18 Comment(2)
And how would you know the location of that file, from the pom file of a referenced module? Actually, if you knew the location of that file, you wouldn't have to read its content.Misgive
If you are using Git, and get Ant to call git rev-parse --show-toplevel this could just work.Segmental
F
1

You can go with something like this. Note, that you have to define two profiles - one which will be activated for the root directory and one for children. Obviously this assumes children will have the same structure but this can be easily adapted.

    <profiles>
        <profile>
            <id>root-dir</id>
            <activation>
                <file>
                    <exists>${basedir}/lib</exists>
                </file>
            </activation>
            <properties>
                <project.libdir>${basedir}/lib</project.libdir>
            </properties>
        </profile>
        <profile>
            <id>child-dir</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <project.libdir>${basedir}/../../lib</project.libdir>
            </properties>
        </profile>
    </profiles>
Fives answered 15/9, 2019 at 16:19 Comment(0)
F
0

so that: somewhere in properties of some parent Project I've the file which i need to relate later, what's why i need absolute path on it everywhere. So, i get it with help of groovy:

<properties>    
<source> import java.io.File; 
        String p =project.properties['env-properties-file']; 
        File f = new File(p); 
        if (!f.exists()) 
        { 
           f = new File("../" + p); 
          if (!f.exists()) 
          { 
             f = new File("../../" + p); 
          } 
        } 
        // setting path together with file name in variable xyz_format 
        project.properties['xyz_format'] =f.getAbsolutePath() 
                            + File.separator 
                            + "abc_format.xml"; 
</source>
</properties>   

and then:

  <properties>
     <snapshots>http://localhost:8081/snapshots<snapshots>
     <releases>http://localhost:8081/releases</releases>
     <sonar>jdbc:oracle:thin:sonar/sonar@localhost/XE</sonar>
     <sonar.jdbc.username>sonar</sonar.jdbc.username>
     <format.conf> ${xyz_format}</format.conf>  <---- here is it!
    </properties>

it works!

Fury answered 26/3, 2016 at 14:25 Comment(0)
S
0

With Maven 3.6.1, from a child project, ${parent.basedir} returns the fully qualified directory of the parent project.

Sepulveda answered 25/10, 2019 at 3:49 Comment(2)
So how does it work when i need the ancestor of all projects, when some child projects might have child projects of their own?Ragged
I believe then it would be ${parent.parent.basedir} etc- you'd need to add to each pom, as appropriateCohberg
P
0

With Maven 4 (implemented since 4.0.0-beta7, see MNG-7038) you can use the project.rootDirectory variable, where the root directory is definied either by using a .mvn directory or using the new pom attribute root=true which is introduced with the new pom model version 4.1.0 together with Maven 4.

<project xmlns="http://maven.apache.org/POM/4.1.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0
         http://maven.apache.org/xsd/maven-4.1.0.xsd" root="true">

and then use the variable, e.g.

<excludeFromFailureFile>${project.rootDirectory}/exclude-pmd.properties</excludeFromFailureFile>

Puling answered 5/7, 2024 at 7:15 Comment(0)
M
-1

I've got root directory for parent pom with ${project.parent.basedir}.

Merkel answered 16/9, 2020 at 16:52 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.