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

Posted

技术标签:

【中文标题】查找多模块Maven反应器项目的根目录【英文标题】:Finding the root directory of a multi module Maven reactor project 【发布时间】:2011-03-06 07:20:16 【问题描述】:

有没有一种简单的方法可以找到多模块 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,所以我最终得到了

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>

但我宁愿不在&lt;outputDirectory&gt; 元素中指定相对路径。 我更喜欢$reactor.root.directory/../to-deploy 之类的东西,但我找不到这样的东西。

另外,如果有某种方法可以继承此 maven-dependency-plugin 配置,我更喜欢这样我不必为每个模块指定它。

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

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

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

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

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

【问题讨论】:

【参考方案1】:

使用$session.executionRootDirectory

作为记录,$session.executionRootDirectory 在 Maven 3.0.3 的 pom 文件中为我工作。该属性将是您正在运行的目录,因此运行父项目,每个模块都可以获得该根目录的路径。

我将使用此属性的插件配置放在父 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>

【讨论】:

$session.executionRootDirectory 对我来说也是灵丹妙药。为什么这不在maven.apache.org/pom.html 中? 如果 $session.executionRootDirectory 等其他 maven 环境变量不起作用,请尝试使用 $project.basedir。 我在 Maven 3.3.9 中尝试了 $session.executionRootDirectory,但它不再起作用了。您需要改用 $user.dir。在我看来,这个选项不能令人满意,因为这意味着我只能独立运行父模块而不是子模块。为此,您需要这个***.com/questions/1012402/…【参考方案2】:

从 Maven 3.3.1 开始,您可以为此目的使用 $maven.multiModuleProjectDirectory。 (感谢https://***.com/a/48879554/302789)

编辑:这似乎只有在您的项目根目录中有 .mvn 文件夹时才能正常工作。

【讨论】:

@ChristofferHammarström 出于某种原因,仅在 3.3.9 的发行说明中作为错误修复出现:maven.apache.org/docs/3.3.9/release-notes.html 但是在 Maven 的 Jira 项目中进一步挖掘,这是它被引入的地方,并且是确实标记为 3.3.1 的固定版本:issues.apache.org/jira/browse/MNG-5767 遗憾的是,他们“修复”了它,现在它只指向当前子项目,而不是实际的多项目根。所以它并不比其他选项更好。 @kaqqao 是的,这很奇怪。我不能说我完全理解该错误报告的内容,但是当我尝试 mvn help:evaluate -Dexpression=maven.multiModuleProjectDirectory 它对我有用¯\_(ツ)_/¯ 编辑:我意识到它似乎只有在我有一个 .mvn 文件夹时才有效在我的项目的顶部! 为我工作没有根目录中的.mvn 文件夹。 用 maven 3.6.3 试过。结论:1) 必须有一个.mvn 文件夹,但它可以是空的 2) mvn 正确报告整个项目的根目录,即使从子目录(模块)运行命令。【参考方案3】:

我在项目中使用的东西是覆盖子模块 poms 中的属性。

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

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

【讨论】:

您要管理所有这些“/..”和“/../..”吗?我的意思是,这个解决方案确实有效,但它隐藏了您项目的实际结构。 我认为这应该是公认的答案。当您只构建一个单独的模块时,这甚至可以工作。 此解决方案在使用 eclipse 和 m2e 时有效,因为它不需要任何外来插件。 谢谢!这是简单项目最优雅的方式。没有额外的插件,只是显式覆盖。 +1【参考方案4】:

有一个 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

【讨论】:

我建议避免这个解决方案:如果你不执行阶段(在例子中是initialize)属性将不会被解决,所以,如果例如,您在clean/pre-clean 阶段的子模块上需要它,您会发现自己在子模块上复制/破解配置(已经在各种项目中看到过)。除非您有特殊要求(不,您的项目并不特殊),否则请使用 @karel 的解决方案。 这是一个很好的解决方案。对于较早的阶段,您只需指定&lt;phase&gt;pre-clean&lt;/phase&gt;【参考方案5】:

正如其他人所建议的那样,directory-maven-plugin 是要走的路。 但是,我发现它最适合 directory-of 目标,如下所述:https://***.com/a/37965143/6498617。

我更喜欢使用 highest-basedir 对我来说不适用于带有嵌套多模块 pom 的多模块项目。 directory-of 目标允许您将属性设置为整个项目中任何模块的路径,当然包括根。 它也比$session.executionRootDirectory 好得多,因为无论您构建根模块还是子模块,并且无论您从哪个当前工作目录进行 mvn,它都始终有效。

【讨论】:

【参考方案6】:

以下小配置文件对我有用。我需要为 CheckStyle 做这样的配置,我把它放到项目根目录下的config 目录中,这样我就可以从主模块和子模块运行它。

<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/../../?这仅适用于您将子项目放在父项目内的特定位置的情况。问题是,一般如何找到这条路径。 basedir 是`$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` 文件夹。稍加修改也可以获得根目录。对于嵌套模块,几个具有不同exist 的配置文件也可以工作。 有不同的解决方案,但我想找一个简单的,不需要复制粘贴到所有模块。【参考方案7】:

目前,Maven 不提供开箱即用的“根项目目录路径”属性。 «根项目» 表示«最顶层» 父项目。

请参考票并考虑投票:[MNG-7038] Introduce public property to point to a root directory of (multi-module) project - ASF JIRA。

【讨论】:

【参考方案8】:

我遇到了类似的问题,因为我需要在项目之间复制文件。 Maven 所做的事情是合乎逻辑的,因为它将使安装在存储库中的 pom.xml 远离硬编码值。

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

【讨论】:

【参考方案9】:

我不知道找到多模块项目根的“好”方法。但是你也许可以改进一点你目前的方法。

第一种选择是直接在根项目下创建一个附加模块,将所有 EAR 声明为其中的依赖项,并使用 dependency:copy-dependencies 将模块的依赖项复制到 to-deploy 目录(相对)。是的,路径仍然是相对的,但由于依赖插件配置是集中的,我不觉得这很烦人。

第二种选择是使用Maven Assembly Plugin 而不是Maven Dependency Plugin 来创建使用dir 格式的分发(这将在目录中创建分发)。这实际上是我会做的。

【讨论】:

谢谢!如果只能理解文档并将其链接到构建生命周期,则使用程序集插件将是最好的。我刚刚花了一个小时试图让它工作,然后放弃了......【参考方案10】:

另一种解决方案是使用 ant 任务将“rootdir=$basedir”写入根项目中的 target/root.properties,然后使用Properties Plugin 重新读取该文件。我没有我自己没试过,但我想它应该可以工作..?

【讨论】:

你如何从一个引用模块的 pom 文件中知道该文件的位置?实际上,如果您知道该文件的位置,就不必阅读其内容。 如果你使用 Git,并让 Ant 调用 git rev-parse --show-toplevel 这可能会起作用。【参考方案11】:

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

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

我可以写我的代码:

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

类似的方式 孙子pom:

<outputDirectory combine.children="override">$project.parent.parent.basedir/to-deploy</outputDirectory>

【讨论】:

【参考方案12】:

这样:在某个父项目的属性中,我有稍后需要关联的文件,这就是为什么我需要在任何地方都使用绝对路径。所以,我在 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>   

然后:

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

有效!

【讨论】:

【参考方案13】:

你可以用这样的东西。请注意,您必须定义两个配置文件 - 一个将为根目录激活,另一个为子目录激活。显然,这假设孩子将具有相同的结构,但这很容易适应。

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

【讨论】:

【参考方案14】:

对于 Maven 3.6.1,从子项目中,$parent.basedir 返回父项目的完全限定目录。

【讨论】:

那么当我需要所有项目的祖先,当一些子项目可能有自己的子项目时,它是如何工作的?【参考方案15】:

我有 $project.parent.basedir 的父 pom 的根目录。

【讨论】:

以上是关于查找多模块Maven反应器项目的根目录的主要内容,如果未能解决你的问题,请参考以下文章

定义将在 Maven 多项目构建中构建的模块列表

Maven:多模块顺序

笔记:Maven 反应堆

多模块项目的 Maven 配置

Maven搭建多模块企业级项目

Maven 搭建spring boot多模块项目