寻找多模块Maven反应堆项目的根目录

90

有没有一种简单的方法来查找多模块 Maven 项目的根目录,类似于 Gradle 的 rootDir


背景:

我想使用 maven-dependency-plugin 将所有子模块的构件复制到相对于整个项目根目录的目录中。

也就是说,我的布局类似于这样,名称已更改:

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,这样最终就会得到:

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

我可以在每个模块中使用相对路径来完成它,就像这样:

        <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-dependency-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文件中配置根目录,但我不认为这是一个解决方案。 那么有没有一种简单的方法来找到多模块项目的根目录呢?
15个回答

81

使用 ${session.executionRootDirectory}

就我所知,在 Maven 3.0.3 的 pom 文件中,${session.executionRootDirectory} 对我是有效的。该属性将是您正在运行的目录,因此运行父项目,每个模块都可以获取到该根目录的路径。

我将使用此属性的插件配置放在父 pom 中,以便继承。我在一个仅在我知道我将在父项目上运行 Maven 时才选择的概要文件中使用它。这样,当我在子项目上运行 Maven 时,我不太可能以不希望的方式使用此变量(因为那时该变量不是指向父级的路径)。

例如:

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

3
${session.executionRootDirectory} 对我也很有帮助。为什么它没有在 http://maven.apache.org/pom.html 中呢? - Bruce Edge
如果其他Maven环境变量(如${session.executionRootDirectory})无法正常工作,请尝试使用${project.basedir}。 - Pradeeban Kathiravelu
8
我尝试使用 Maven 3.3.9 中的 ${session.executionRootDirectory},但它不再起作用。现在需要使用 ${user.dir}。我认为这个选项并不理想,因为这意味着我只能运行父模块而不能独立运行子模块。要使其正常工作,您需要查看此链接 https://dev59.com/bnNA5IYBdhLWcg3wUMDn - Dr4gon

73
自Maven 3.3.1起,您可以使用${maven.multiModuleProjectDirectory}来实现此目的。 (感谢https://dev59.com/sm855IYBdhLWcg3wyniC#48879554)
编辑:当您在项目根目录下有一个.mvn文件夹时,似乎只有这样才能正常工作。

8
@kaqqao 是的,这很奇怪。我不能确切地理解那个 bug 报告的内容,但是当我尝试 mvn help:evaluate -Dexpression=maven.multiModuleProjectDirectory 时,它能正常工作 ¯\(ツ)/¯ 编辑:我意识到,似乎只有在我的项目顶部有一个 .mvn 文件夹时它才有效! - Grégory Joseph
3
在我的情况下,没有根目录中的.mvn文件夹也可以正常工作。 - Benny Bottema
5
尝试使用 Maven 3.6.3。结论:1)必须存在一个名为“ .mvn ”的文件夹,但可以为空;2)即使在从子目录(模块)运行命令时,mvn 也能正确地报告整个项目的根目录。 - wlnirvana
2
请不要随意使用此属性。这是一个内部属性(实现细节),它可能会在发布时消失。 - Michael-O
2
@GrégoryJoseph https://issues.apache.org/jira/browse/MNG-7038 - Michael-O
显示剩余11条评论

32

我在我的项目中使用的一种方法是重写子模块POM文件中的属性。

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

这样一来,你仍然有相对路径,但你可以在根模块中定义一个插件,并用正确的 myproject.root 替换,使你的子模块继承它。


5
你打算管理所有这些"/.."和"/../.."吗?我的意思是,虽然这个解决方案可行,但它隐藏了你的项目实际结构。 - Henry Aloni
1
我认为这应该是被接受的答案。即使你只是构建一个单独的模块,它也可以正常工作。 - filip
1
这个解决方案适用于使用Eclipse和m2e的情况,因为它不需要任何奇特的插件。 - 3urdoch
3
谢谢!这是简单项目中最优雅的方法。没有额外的插件,只需要明确的覆盖。鼓励! - Nikolai Shevchenko
简单易用,像魔法一样好用! - kartik

25

有一个Maven插件可以解决这个特定问题:directory-maven-plugin

它将会把你的项目根路径分配给你选择的属性。请参阅文档中 highest-basedir 目标。

例如:

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

然后在你的父/子pom.xml中任何地方使用${main.basedir}


6
我建议避免使用这个解决方案:如果您不执行该阶段(在示例中为initialize),属性将无法解决,因此,如果您需要它例如在clean/pre-clean阶段的子模块上,您将发现自己需要在子模块上复制/修改配置(已在各种项目中看到)。除非您有特殊要求(否则,您的项目并不特殊),请使用@karel的解决方案。 - Sergio
1
这是一个很好的解决方案。对于早期阶段,您只需指定 <phase>pre-clean</phase> - armandino
1
即使明确运行目标,属性仍无法解决同一反应堆中另一个插件的配置。我想它并不像看起来那么傻瓜式。实际上,运行“initialize”生命周期阶段而不是“highest-basedir”目标使其正常工作。 - Rick Moritz

7
正如其他人建议的那样,使用directory-maven-plugin是最好的选择。 然而,我发现在多模块项目中,使用highest-basedir不能很好地工作,并且对于嵌套的多模块poms更是如此。 directory-of目标允许您设置属性以指定整个项目中任何模块的路径,当然包括根模块。 与${session.executionRootDirectory}相比,它也更好,因为它始终有效,无论是构建根模块还是子模块,而且不受您从哪里执行mvn的当前工作目录的影响。参考:https://dev59.com/bnNA5IYBdhLWcg3wUMDn#37965143

6

3
以下的配置对我很有帮助,需要将其放入项目根目录下的config文件夹中,这样就可以在主模块和子模块中运行CheckStyle。
<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>

针对嵌套模块它可能不适用,但我相信可以通过使用不同的exists来修改多个配置文件来实现。 (我不知道为什么验证标签中应该有“../..”,而覆盖属性本身只需要“..”,但只有这种方式才能正常工作。)


那么你的意思是basedir是${project.basedir}/../../吗?这只适用于将子项目放置在父项目内的特定位置的情况。问题是,如何在一般情况下找到此路径。 - djjeck
basedir 是 ${project.basedir}/../。只需要在那里放置一些独特的文件(在我的情况下是 checkstyle.xml)。是的,这个方法可以用于一个模块嵌套层级。通常模块被放置在子目录中,所以我不考虑其他情况(但我相信类似的方法也可以适用)。 问题是如何找到 ${reactor.root.directory} 这样的东西。这是我的解决方案。但在我的情况下,我需要 /config 文件夹。稍作修改即可获得根目录。 对于嵌套模块,使用具有不同 exist 的多个配置文件也可以起作用。 - Vic
有不同的解决方案,但是我想找到一个简单的解决方案,它不需要将复制粘贴到所有模块中。 - Vic

2

除了使用绝对根路径外,在某些情况下,我有另一种解决问题的方法。

使用maven插件combine.children功能,参考:maven插件

我可以编写我的代码:

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

如果父pom未使用此插件,则可以在插件管理中编写。
子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>

类似的方式 grandchild pom:
<outputDirectory combine.children="override">${project.parent.parent.basedir}/to-deploy</outputDirectory>

2

我遇到了类似的问题,需要在项目之间复制文件。Maven所做的是很合理的,因为它会将存储库中安装的pom.xml与硬编码值隔离开来。

我的解决方案是将复制的目录放入Maven构件中,然后使用Ant进行提取/复制。


1

另一个解决方案是使用Ant任务将"rootdir=${basedir}"写入根项目中的target/root.properties文件中,然后使用Properties Plugin读取该文件。我自己没有尝试过,但我猜应该可以工作..?


3
你如何从已引用模块的pom文件中知道那个文件的位置?实际上,如果你知道那个文件的位置,就不需要读取它的内容了。 - djjeck
如果您正在使用Git,并让Ant调用git rev-parse --show-toplevel,那么这可能会起作用。 - Ed Randall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接