查找多模块 Maven 反应器项目的根目录

有没有一种简单的方法可以找到一个多模块 Maven 项目的根,比如 Gradle 的 rootDir


背景:

我希望使用 maven- 依赖-插件将工件从我的多模块项目的所有子模块复制到相对于整个项目的根目录的目录中。

也就是说,我的布局看起来类似于这个,名称改变了:

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

我希望所有的工件都能从他们各自模块的目标目录中复制到 my-project/../to-deploy,这样我就能

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

我可以在每个模块中使用一个相对路径,如下所示:

        <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>

但是我不想在 <outputDirectory>元素中指定相对路径。 我喜欢像 ${reactor.root.directory}/../to-deploy这样的东西,但是我找不到这样的东西。

另外,我希望有一些方法来继承这个 maven 依赖-plugin 配置,这样我就不必为每个模块指定它。

我还尝试从根 pom 继承一个自定义属性:

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

但是,当我尝试在模块 POM 中使用 ${myproject.root}时,${basedir}将解析为模块的基底。

另外,我发现 http://labs.consol.de/lang/de/blog/maven/project-root-path-in-a-maven-multi-module-project/建议每个开发人员和持续集成服务器应该在 profiles.xml 文件中配置根目录,但我不认为这是一个解决方案。

那么有没有一种简单的方法可以找到多模块项目的根呢?

112806 次浏览

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.

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..?

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>

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.

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

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.)

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.

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!

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://stackoverflow.com/a/37965143/6498617.

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.

Since Maven 3.3.1, you can use ${maven.multiModuleProjectDirectory} for this purpose. (thanks to https://stackoverflow.com/a/48879554/302789)

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

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>

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

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

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.

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>