Maven中的继承配置文件

11

我在我的父POM中有以下配置文件

<profile>
    <id>P1</id>
    <activation>
        <activeByDefault>true</activeByDefault>
    </activation>
</profile>

<profile>
    <id>P2</id>
    <activation>
        <file>
            <exists>${project.basedir}/src/main/whatever</exists>
        </file>
    </activation>
</profile>

为什么子POM中P1处于活动状态,而P2没有呢?

目录${project.basedir}/src/main/whatever在父项目中不存在,但在子项目中存在。


P2 的激活条件中引用的目录是否存在?提问时请提供完整信息。 - Björn Pollex
我编辑了我的问题以回答你的评论。 - Adrian Ber
@AdrianBer 这件事有任何更新吗? - linski
5个回答

28

档案P2未激活,因为其exists标签下的路径无法解析为现有路径,尽管目录${project.basedir}/src/main/whatever存在。如果将属性${project.basedir}改写为${basedir},它应该激活P2档案。

这意味着${project.basedir}并没有像它应该的那样解析为项目基本目录。然而,help:effective-pom显示它确实如此。我已经报告了这个问题(MNG-5516)。

此外,我认为如果P2被激活,P1将不会被激活。

没错。引用activeByDefault文档的说法:

这个配置文件(在这个例子中是P1)将自动对所有构建生效,除非通过之前描述的方法之一激活了同一个POM中的另一个配置文件。所有默认激活的配置文件在通过命令行或激活配置时,会自动取消激活。
“继承”这个词让我感到困惑,因为“配置文件继承”在项目聚合中起作用,但在项目继承中却不起作用。
为了澄清这个问题,我模拟了这种情况。 “空的pom”表示除了标准模型、组、构件和版本标签之外,它是空的。
###简单场景
目录结构:
simple
 \-pom.xml

pom内容:

<profiles>
    <profile>
        <id>P1</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>P2</id>
        <activation>
            <file>
                <exists>${basedir}/dir/</exists>
            </file>
        </activation>
    </profile>
</profiles>

如果没有dir目录,mvn help:all-profiles将输出:
Profile Id: P1 (Active: true , Source: pom)
Profile Id: P2 (Active: false , Source: pom)

如果存在dir目录,mvn help:all-profiles将输出:
Profile Id: P2 (Active: true , Source: pom)
Profile Id: P1 (Active: false , Source: pom)

###项目继承
目录结构:
inheritance
 |--child
 |  \-pom.xml         // child pom
 \-pom.xml           // parent pom

子POM为空,而父POM中的配置文件与简单场景中的相同。无论是否存在inheritance/child/dir目录,从child目录运行mvn help:all-profiles命令都会输出以下内容:
Profile Id: P1 (Active: false , Source: pom)
Profile Id: P2 (Active: false , Source: pom)

当在子目录中运行`mvn help:effective-pom`时,它显示配置文件确实没有被继承。它的行为如下所述:
合并的POM中包含以下元素:
- 依赖项 - 开发人员和贡献者 - 插件列表(包括报告) - 具有匹配ID的插件执行 - 插件配置 - 资源
这里没有提到任何配置文件。
### 项目聚合
目录结构:
aggregation
 |--module
 |  \-pom.xml         // module pom
 \-pom.xml           // aggregator pom

模块pom为空,而聚合pom中的配置与简单场景中的配置相同。如果没有aggregation/module/dir目录,则从module目录运行mvn help:all-profiles命令会输出:
Profile Id: P1 (Active: true , Source: pom)
Profile Id: P2 (Active: false , Source: pom)

如果在aggregation/module/dir目录中运行mvn help:all-profiles命令,从module目录输出的结果为:
Profile Id: P2 (Active: true , Source: pom)
Profile Id: P1 (Active: false , Source: pom)

当在module目录下运行mvn help:effective-pom时,它显示配置文件是继承的。这并没有明确记录项目继承
如果你有多个Maven项目,并且它们都有相似的配置,你可以通过提取出这些相似的配置并创建一个父项目来重构你的项目。这样,你只需要让你的Maven项目继承那个父项目,这些配置就会应用到所有项目中。
注意:
- 这不适用于配置文件,如已经显示的那样。 - 从inheritance目录运行Maven构建将只运行父项目构建。 项目聚合
如果你有一组需要一起构建或处理的项目,你可以创建一个父项目,并将这些项目声明为其模块。这样一来,你只需要构建父项目,其他项目也会跟随一起构建。
注意:
  • 聚合目录运行maven构建将运行每个模块和聚合器的构建(实际顺序由maven根据不同的标准确定)。

###结论

配置文件可以在全局、用户或项目级别上进行定义。由于聚合项目是一起构建的(在同一个构建中),必须运行某种配置文件解析来计算活动配置文件。因此,这是令人困惑的部分:

  • 当项目继承配置文件时,不会从父pom继承到子pom。
  • 当项目聚合时,配置文件从聚合器pom继承到模块pom。

这是使用Maven 3.1.0和3.0.5进行测试的结果。


3
这篇帖子有误导性。实际上,配置文件从父POM继承到子POM。请查看我的回答。 - Noremac
1
这篇文章没有误导性。我用 Maven 3.3.9 测试了配置继承。如果父 POM 声明了子模块(聚合),则在子模块中可以看到该配置文件。如果父 POM 没有声明子模块(继承),则该配置文件不可见。 - Paulo Merson
@PauloMerson,如果同时存在项目继承和项目聚合怎么办? - mre

11
只是为了澄清,Maven配置文件是可以继承的。有关其他SO问题的参考,请参见:Inheriting Maven profiles。我已经成功地在我的项目中继承了配置文件,并且不需要额外的工作。
至于最初的问题,您在exists元素中定义了一个变量。根据documentation
自Maven 2.0.9以来,标签和可以进行插值。支持的变量是系统属性,例如${user.home}和环境变量,例如${env.HOME}。请注意,在POM本身中定义的属性和值在此处不可用于插值,例如上面的示例激活器无法使用${project.build.directory},但需要硬编码路径目标。
因此,我从中得出的结论是${project.basedir}不能使用,也无法工作。但是,如果您将其定义为环境变量,则可以使用它。

我发现一个需要注意的问题是,在父POM中,应该使用 <plugin-management> 来配置插件。但是在profiles中,为了让特定于profile的配置生效,不应该使用 <plugin-management>


关于配置文件的继承,你是正确的,它可以按预期直接使用。如果你在profiles/profile/build/plugins下定义一个插件,并在profiles/profile/build/pluginManagement/plugins下定义其配置,当该配置文件处于活动状态时,插件也会按预期应用 - 因此我认为有关插件管理部分在配置文件中不起作用的最后一句话是不正确的。 - Richard
实际上,我发现了另一个问题,我已经解决了,但仍然无法完全解决:有时在插件配置中指定的内容不能正确地传递到该插件中定义的每个执行中,这意味着我必须将例如用户名、密码和jdbc字符串(例如sql-maven-plugin)复制到每个执行的配置中。这看起来像是一些小的XML合并问题,但不是太大的问题——这就是为什么我在这方面没有提出任何问题的原因。 - Igor

7
问题不在于继承,而在于插值(即支持${...}的值):基于文件的配置激活仅支持有限的插值。详情请参见http://maven.apache.org/pom.html#Activation
因此,${project.basedir}不受支持,但只有${basedir}(和系统属性)受支持。
要了解更多详细信息,请查看模型构建算法:http://maven.apache.org/ref/3.2.1/maven-model-builder/
完整的模型插值发生在配置激活之后:因此,即使您的有效POM显示了${project.basedir}的插值值,该值也不会在配置激活时计算。
在Maven 3.2.2中,有多个增强功能:文档在http://jira.codehaus.org/browse/MNG-5590,运行时警告在http://jira.codehaus.org/browse/MNG-5608,以及更好的有效POM结果http://jira.codehaus.org/browse/MNG-5612

1
一般情况下,Maven配置文件不会被继承(参见http://jira.codehaus.org/browse/MNG-5127进行讨论并提供有用的博客链接)。我曾经成功地做过这样的事情:
<!-- Parent -->
<profile>
    <id>P2</id>
    <activation>
        <file>
            <exists>${project.basedir}/src/main/whatever</exists>
        </file>
    </activation>
    <!-- all the things you want to define for the child POMs -->
</profile>

<!-- Child -->
<!-- Include only the activation block, which must match parent's exactly -->
<!-- Whatever is in the parent will be inherited -->
<profile>
    <id>P2</id>
    <activation>
        <file>
            <exists>${project.basedir}/src/main/whatever</exists>
        </file>
    </activation>
</profile>

如果P2处于活动状态,我认为P1将不会处于活动状态。这是因为<activeByDefault>对于P1是真的。在我看来,元素名称有点误导人。 "默认情况下活动"意味着"始终活动",但实际上它意味着"仅在此POM中没有其他概要文件处于活动状态时才处于活动状态"。
以上是使用Maven 3.0.x发现的。

1
引用的 JIRA 问题不是说:“基于文件的配置激活实际上是继承的”吗?根据我的经验,如果父级中有一个基于文件(或文件夹)的激活配置文件,则在相对于子项目可用的情况下(即${basedir}相对于子项目),该配置文件将被激活。 - Sander Verhagen
1
好的观点,如果该属性从<exists>中删除,则应根据该帖子继承它。我已经使用上述技术使一些属性激活工作,所以现在我只需在想要继承配置文件时应用它。 - user944849

0

从具有基于文件激活的第二个配置文件中删除P2。

<profiles>
    <profile>
        <id>P1</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>P2</id>
        <activation>
            <file>
                <exists>${basedir}/dir/</exists>
            </file>
        </activation>
    </profile>
</profiles>

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