具有显式 finalName 的 Maven 将无法正常工作
Maven with an explicit finalName won't work properly
1.背景
我的 Maven 项目有很多带有 jars
和 wars
的模块和子模块,一切正常。我也可以毫无问题地将它部署到服务器上。
我决定遵循 this maven naming conversion,我正在对 project.name
和 project.build.finalName
进行一些测试以获得合适的名称。
我定义的为根工件创建 project.name
的模式是 company-${project.artifactId}
,对于模块和子模块是 ${project.parent.name}-${project.artifactId}
:
- company-any-artifact-any-module1
- company-any-artifact-any-module2-any-submodule1
- company-any-artifact-any-module2-any-submodule2
project.build.finalName
的模式是 ${project.name}-${project.version}
:
- company-any-artifact-any-module1-1.0.jar
- company-any-artifact-any-module2-any-submodule1-2.0.jar
- company-any-artifact-any-module2-any-submodule2-3.0.war
但是 maven 没有生成这些文件,而是给了我一个 WhosebugError
.
2。重现错误的示例
您可以从 github 克隆此示例:https://github.com/pauloleitemoreira/company-any-artifact
在 github 中,有 master
branch, that will reproduce this error. And there is only-modules
分支,这是一个使用 ${project.parent.name}
生成我想要的 jar finalName
的工作示例。
让我们考虑一个具有一个根 pom 工件、一个 pom 模块和一个子模块的 maven 项目。
-any-artifact
|
|-any-module
|
|-any-submodule
2.1 任意工件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>any-artifact</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>company-${project.artifactId}</name>
<modules>
<module>any-module</module>
</modules>
<!-- if remove finalName, maven will not throw Whosebug error -->
<build>
<finalName>${project.name}-${project.version}</finalName>
</build>
</project>
2.2 任意模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-artifact</artifactId>
<groupId>com.company</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact</groupId>
<artifactId>any-module</artifactId>
<packaging>pom</packaging>
<name>${project.parent.name}-${project.artifactId}</name>
<modules>
<module>any-submodule</module>
</modules>
</project>
2.3 任意子模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-module</artifactId>
<groupId>com.company.any-artifact</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact.any-module</groupId>
<artifactId>any-submodule</artifactId>
<name>${project.parent.name}-${project.artifactId}</name>
</project>
3。问题
当尝试 mvn clean install
时,maven 给了我一个 WhosebugError
:
Exception in thread "main" java.lang.WhosebugError
at org.codehaus.plexus.util.StringUtils.isEmpty(StringUtils.java:177)
at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:194)
at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:163)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:266)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
重要的是要知道错误仅在我们使用子模块时发生。如果我们使用根 POM 工件和 jar 模块创建项目,则不会发生错误。
4.问题
为什么只有在使用子模块时才会出现这个错误?
有什么建议可以解决我的问题吗?我应该忘记它并按照我想要的模式为每个项目手动设置 project.name
和 project.build.fileName
吗?
重要更新:
有的回答只说用&{parent.name}
,但是不行。拜托,这是一个有赏金的问题,在回答这个问题之前考虑用 Maven version 3.3.9
测试你的解决方案。
Maven 版本 3.3.9
Edit - 在错误发生的阶段向问题添加详细信息,一切正常,直到 prepare-package
阶段,但 Whosebug 发生在项目 Maven 生命周期的 package
阶段。
这是属性继承的问题。
尝试使用 ${parent.name}
而不是 ${project.parent.name}
。
看:Project name declared in parent POM isn't expanded in a module filtered web.xml.
---更新---
Benjamin Bentmann(行家提交者)说:"In general though, expressions of the form ${project.parent.*}
are a bad practice as they rely on a certain build state and do not generally work throughout the POM, giving rise to surprises"。
https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22
也许你应该考虑使用 ${project.parent.*}
是个好方法。
将 company-any-artifact 中的 pom.xml 更改为以下,它会起作用。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>any-artifact</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>${project.groupId}</name>
<modules>
<module>any-module</module>
</modules>
<!-- if remove finalName, maven will not throw Whosebug error -->
<build>
<finalName>${project.groupId}-${project.version}</finalName>
</build>
</project>
将子模块中的 pom.xml 更改为以下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-artifact</artifactId>
<groupId>com.company</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact</groupId>
<artifactId>any-module</artifactId>
<packaging>pom</packaging>
<!-- <name>${project.parent.name}-${project.artifactId}</name> -->
<modules>
<module>any-submodule</module>
</modules>
<build>
<finalName>${project.parent.name}-${project.artifactId}</finalName>
</build>
</project>
将子模块 pom.xml 更改为以下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-module</artifactId>
<groupId>com.company.any-artifact</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact.any-module</groupId>
<artifactId>any-submodule</artifactId>
<!-- <name>${project.parent.name}-${project.artifactId}-${project.version}</name> -->
<build>
<finalName>company-${project.parent.name}-${project.artifactId}-${project.version}</finalName>
</build>
</project>
然后输出是:company-any-module-any-submodule-1.0-SNAPSHOT
有趣!我开始克隆 repo 并重现错误。如果能从下面提到的帮助我调试问题的任何步骤中获得任何线索,我将不胜感激 -
Maven Life Cycle Phases
问题发生的阶段是生命周期的 package
阶段。意思是 mvn package
重现了您的项目的问题。
遍历错误中的堆栈跟踪行。了解它失败的表达式评估 -
@Override
public Object evaluate( String expr ) throws ExpressionEvaluationException {
return evaluate( expr, null ); // Line 143
}
它也不是导致它的 finalName
属性。由于指定了相同的默认值
<finalName>${artifactId}-${version}</finalName>
使用相同的项目配置工作正常。
然后尝试将 any-submodule
的包装更改为
<packaging>pom</packaging>
错误消失了。意思是打包为 jar
, war
等表达式评估不同并导致溢出。
修改 any-module
或 any-submodule
pom.xml
内容 我可以自信地说是 project.parent.name
导致递归评估表达式并导致堆栈溢出(如何? - 是 I am still looking for..)。此外,更改
<name>${project.parent.name}-${project.artifactId}</name>
到
<name>${parent.name}-${project.artifactId}</name>
在我没有收到错误但生成的 jar 类型为 -
的意义上对我有用
${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar
和
${parent.name}-any-submodule-1.0-SNAPSHOT
分别用变化。
根据要求寻找解决方案,我正在寻找你正在使用的递归的尾巴。
注意 - 仍在努力寻找解决此问题的合适方法。
正如我在
的回答中所说
我们先来看一下基础知识:
如POM Reference所述:
finalName: This is the name of the bundled project when it is finally built (sans the file extension, for example: my-project-1.0.jar). It defaults to ${artifactId}-${version}.
name: Projects tend to have conversational names, beyond the artifactId.
所以这两个有不同的用途。
name
纯粹是信息性的,主要用于生成的文档和构建日志。它不会被继承,也不会在其他任何地方使用。它是一个人类可读的字符串,因此可以包含任何字符,即文件名中不允许的空格或字符。所以,这将是有效的:<name>My Turbo Project on Speed!</name>
。这显然至少是一个有问题的工件文件名。
如上所述,finalName
是生成的神器的名称。它是继承的,所以它通常应该依赖属性。仅有的两个真正有用的选项是默认 ${artifactId}-${version}
和无版本 ${artifactId}
。其他一切都会导致混淆(例如名为 foo
的项目创建了一个工件 bar.jar
)。实际上,我的涡轮增压项目!将是有效的,因为这是一个有效的文件名,但实际上,这样的文件名往往是相当不可用的(例如,尝试从 bash 中寻址包含 ! 的文件名)
所以,关于 Whosebug 发生的原因:
name
不继承
project.parent.name
也不会在插值期间进行计算,因为名称是 children 完全不可见的少数属性之一
parent.name
实际上曾经在旧的 Maven 版本中工作,但更多是由于错误(而且 已弃用 访问没有前导 [=20= 的属性]).
- 缺失的 属性 不会被插值,即保留在模型中
- 因此,在
any-submodule
的有效 pom 中,finalName
的值仍然是(用 mvn help:effective-pom
试试):${project.parent.name}-any-submodule
到目前为止还很糟糕。现在是 Whosebug
的原因
Maven 有一个称为后期插值的附加功能,可以在实际使用插件参数时评估它们的值。这允许 pluing 使用不属于模型的属性,但在生命周期的早期由插件生成(这允许,例如,插件对最终名称做出 git 修订)。
那么会发生什么:
编辑:使错误的实际原因更清楚(见评论):
- 评估 jar 插件的最终名称:
@Parameter( defaultValue = "${project.build.finalName}", readonly = true )
PluginParameterExpressionEvaluator
启动并尝试评估最终名称(${project.parent.name}-any-submodule
,其中包含 属性 表达式 ${project.parent.name}.
- 评估者询问模型,模型又return是parent项目的名称,即:
${project.parent.name}-any-module
.
- 所以评估者试图解决这个问题,return
${project.parent.name}-any-module
(再次),因为 属性 总是根据当前项目解决,循环又开始了。
- 抛出 WhosebugError。
如何解决这个问题
遗憾的是,你不能。
您需要为每个项目明确指定 name
(以及 artifactId
)。没有解决方法。
那么,你可以让finalName
依赖它。然而,我会反对它(见我对 的回答)
以这种方式更改最终名称的问题是本地构建工件的名称和存储库中的工件名称会不同,因此在本地您的工件被命名为 any-artifact-any-module-any-submodule.jar
,但您的工件名称存储库仍将是 any-submodule.jar
建议
- 如果你真的需要区分那个罚款,改为更改artifactId:
<artifactId>artifact-anymodule-anysubmodule</artifactId>
。
- 不要使用破折号作为短名称来区分结构的级别。
- 提示:模块的路径仍然可以
anymodule
,不需要是模块实际的artifactId!
- 当我们这样做的时候:使用
name
的目的是为了人类可读,所以你可能会考虑一些更具视觉吸引力的东西(因为这是出现在构建日志中的名称) : <name>Artifact :: AnyModule :: AnySubModule</name>
.
- 使用非常短的 groovy 脚本自动创建名称条目实际上非常容易。
- 您还可以编写强制规则来强制命名 artifactIds
对您问题的严格回答是,${project.parent.name}
不会 作为模型插值过程的一部分进行解析。反过来,您在代码的一个完全不同的地方有一个 WhosebugError
,即当...构建项目的最终 JAR 时。
第 1 部分:构建的模型错误
事情是这样的。当您在项目上启动 Maven 命令时,它采取的第一个操作是创建项目的有效模型。这意味着读取您的 POM 文件、使用激活的配置文件进行推理、应用继承、对属性执行插值……所有这些都是为了为您的项目构建最终的 Maven 模型。这项工作由 Maven Model Builder 组件完成。
建立模型的过程相当复杂,步骤很多,可能分为2个阶段,但我们感兴趣的部分在model interpolation部分。这是 Maven 将模型中所有由 ${...}
表示的标记替换为计算值的时候。它发生在注入配置文件并执行继承之后。在那个时间点,由 MavenProject
对象表示的 Maven 项目还不存在,只有它的 Model
正在构建。只有在您拥有完整模型后,您才能开始从中构建 Maven 项目。
因此,当插值完成时,它仅根据 POM 文件中存在的信息进行推理,并且唯一有效的值是 mentioned in the model reference. (This replacement is performed by the StringSearchModelInterpolator
class, if you want to look at the source code.) Quite notably, you will notice that the <parent>
element in the model does not contain the name of the parent model. The class Model
in Maven is actually generated with Modello from a source .mdo
file, and that source only defines groupId
, artifactId
, version
and relativePath
(along with a custom id
) for the <parent>
element. This is also visible in the documentation.
所有这些的结果是,在执行模型插值后,令牌 ${project.parent.name}
将不会被替换。而且,进一步地,由它构造的 MavenProject
将有一个包含 ${project.parent.name}
的名称未被替换。您可以在日志中看到这一点,在您的示例项目中,我们有
[INFO] Reactor Build Order:
[INFO]
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule
意味着 Maven 认为项目 any-module
的实际名称是 ${project.parent.name}-any-module
.
第 2 部分:奇怪的事情开始了
我们现在正处于 reactor 中的所有项目都已正确创建甚至编译的时候。实际上,理论上一切都应该工作得很好,但项目本身的名称完全是无聊的。但是你有一个奇怪的情况,它在创建带有 maven-jar-plugin
的 JAR 时失败了。在您的示例中构建失败并显示以下日志:
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ any-submodule ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] company-any-artifact ............................... SUCCESS [ 0.171 s]
[INFO] ${project.parent.name}-any-module .................. SUCCESS [ 0.002 s]
[INFO] ${project.parent.name}-any-submodule ............... FAILURE [ 0.987 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
这意味着在构建模型后出现了问题。原因是插件 injects the name of the project as a parameter:
/**
* Name of the generated JAR.
*
* @parameter alias="jarName" expression="${jar.finalName}" <b>default-value="${project.build.finalName}"</b>
* @required
*/
private String finalName;
注意 project.build.finalName
作为子模块生成的 JAR 名称的默认值。此注入和变量的插值由另一个名为 PluginParameterExpressionEvaluator
.
的 class 完成
那么这里发生了什么:
any-submodule
上的JAR插件注入项目的最终名称,命名为${project.parent.name}-any-submodule
。
- 感谢父项目的继承,在你最顶层的POM项目中声明了
<finalName>
,它继承了<finalName>${project.name}-${project.version}</finalName>
.
- Maven 现在尝试为
any-submodule
插入 ${project.name}
。
- 由于第 1 部分,这解析为
${project.parent.name}-any-submodule
。
- Maven 现在尝试为
any-submodule
插入 ${project.parent.name}
。这可以正常工作:构建 MavenProject
并在项目实例上调用 getParent()
,返回具体的 Maven 父项目。因此,${project.parent.name}
将尝试解析 any-module
的名称,实际上是 ${project.parent.name}-any-module
.
- Maven 现在尝试插入
${project.parent.name}-any-module
,但仍在 any-submodule
项目实例 上。对于 PluginParameterExpressionEvaluator
,评估标记的根 "project"
没有改变。
- Maven 现在尝试在
any-submodule
上插入 ${project.parent.name}
,这同样可以正常工作并且 returns ${project.parent.name}-any-module
.
- Maven 现在尝试在
any-submodule
上插入 ${project.parent.name}
... 这有效 returns ${project.parent.name}-any-module
所以它会尝试评估 ${project.parent.name}
...
你可以看到这里发生了无穷无尽的递归,这导致了你拥有的 WhosebugError
。这是 PluginParameterExpressionEvaluator
中的错误吗?这还不清楚:它的原因是首先没有正确替换的模型值。从理论上讲,它可以处理评估 ${project.parent}
的特殊情况并创建一个新的 PluginParameterExpressionEvaluator
在这个父项目上工作,而不是总是在当前项目上工作。如果您对此有强烈的感觉,请随时创建 a JIRA issue.
第 3 部分:为什么没有子模块也能工作
通过上面所说的,您现在可以推断出为什么它在这种情况下有效。让我们推理一下 Maven 需要做什么来评估最终名称,因为必须在 Maven Jar 插件中注入:
any-module
上的JAR插件注入项目的最终名称,命名为${project.parent.name}-any-module
。
- 感谢父项目的继承,在你最顶层的POM项目中声明了
<finalName>
,它继承了<finalName>${project.name}-${project.version}</finalName>
.
- Maven 现在尝试为
any-module
插入 ${project.name}
。
- 这解析为
${project.parent.name}-any-module
,与之前相同。
- Maven 现在尝试为
any-module
插入 ${project.parent.name}
。就像以前一样,这可以正常工作:MavenProject
已构建,getParent()
将在项目实例上调用,返回具体的 Maven 父项目。因此,${project.parent.name}
将尝试解析 any-artifact
的名称,实际上是 company-any-artifact
.
- 插值成功停止
而且你没有任何错误。
1.背景
我的 Maven 项目有很多带有 jars
和 wars
的模块和子模块,一切正常。我也可以毫无问题地将它部署到服务器上。
我决定遵循 this maven naming conversion,我正在对 project.name
和 project.build.finalName
进行一些测试以获得合适的名称。
我定义的为根工件创建 project.name
的模式是 company-${project.artifactId}
,对于模块和子模块是 ${project.parent.name}-${project.artifactId}
:
- company-any-artifact-any-module1
- company-any-artifact-any-module2-any-submodule1
- company-any-artifact-any-module2-any-submodule2
project.build.finalName
的模式是 ${project.name}-${project.version}
:
- company-any-artifact-any-module1-1.0.jar
- company-any-artifact-any-module2-any-submodule1-2.0.jar
- company-any-artifact-any-module2-any-submodule2-3.0.war
但是 maven 没有生成这些文件,而是给了我一个 WhosebugError
.
2。重现错误的示例
您可以从 github 克隆此示例:https://github.com/pauloleitemoreira/company-any-artifact
在 github 中,有 master
branch, that will reproduce this error. And there is only-modules
分支,这是一个使用 ${project.parent.name}
生成我想要的 jar finalName
的工作示例。
让我们考虑一个具有一个根 pom 工件、一个 pom 模块和一个子模块的 maven 项目。
-any-artifact
|
|-any-module
|
|-any-submodule
2.1 任意工件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>any-artifact</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>company-${project.artifactId}</name>
<modules>
<module>any-module</module>
</modules>
<!-- if remove finalName, maven will not throw Whosebug error -->
<build>
<finalName>${project.name}-${project.version}</finalName>
</build>
</project>
2.2 任意模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-artifact</artifactId>
<groupId>com.company</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact</groupId>
<artifactId>any-module</artifactId>
<packaging>pom</packaging>
<name>${project.parent.name}-${project.artifactId}</name>
<modules>
<module>any-submodule</module>
</modules>
</project>
2.3 任意子模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-module</artifactId>
<groupId>com.company.any-artifact</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact.any-module</groupId>
<artifactId>any-submodule</artifactId>
<name>${project.parent.name}-${project.artifactId}</name>
</project>
3。问题
当尝试 mvn clean install
时,maven 给了我一个 WhosebugError
:
Exception in thread "main" java.lang.WhosebugError
at org.codehaus.plexus.util.StringUtils.isEmpty(StringUtils.java:177)
at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:194)
at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:163)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:266)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
重要的是要知道错误仅在我们使用子模块时发生。如果我们使用根 POM 工件和 jar 模块创建项目,则不会发生错误。
4.问题
为什么只有在使用子模块时才会出现这个错误?
有什么建议可以解决我的问题吗?我应该忘记它并按照我想要的模式为每个项目手动设置 project.name
和 project.build.fileName
吗?
重要更新:
有的回答只说用&{parent.name}
,但是不行。拜托,这是一个有赏金的问题,在回答这个问题之前考虑用 Maven version 3.3.9
测试你的解决方案。
Maven 版本 3.3.9
Edit - 在错误发生的阶段向问题添加详细信息,一切正常,直到 prepare-package
阶段,但 Whosebug 发生在项目 Maven 生命周期的 package
阶段。
这是属性继承的问题。
尝试使用 ${parent.name}
而不是 ${project.parent.name}
。
看:Project name declared in parent POM isn't expanded in a module filtered web.xml.
---更新---
Benjamin Bentmann(行家提交者)说:"In general though, expressions of the form ${project.parent.*}
are a bad practice as they rely on a certain build state and do not generally work throughout the POM, giving rise to surprises"。
https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22
也许你应该考虑使用 ${project.parent.*}
是个好方法。
将 company-any-artifact 中的 pom.xml 更改为以下,它会起作用。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>any-artifact</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>${project.groupId}</name>
<modules>
<module>any-module</module>
</modules>
<!-- if remove finalName, maven will not throw Whosebug error -->
<build>
<finalName>${project.groupId}-${project.version}</finalName>
</build>
</project>
将子模块中的 pom.xml 更改为以下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-artifact</artifactId>
<groupId>com.company</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact</groupId>
<artifactId>any-module</artifactId>
<packaging>pom</packaging>
<!-- <name>${project.parent.name}-${project.artifactId}</name> -->
<modules>
<module>any-submodule</module>
</modules>
<build>
<finalName>${project.parent.name}-${project.artifactId}</finalName>
</build>
</project>
将子模块 pom.xml 更改为以下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>any-module</artifactId>
<groupId>com.company.any-artifact</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.company.any-artifact.any-module</groupId>
<artifactId>any-submodule</artifactId>
<!-- <name>${project.parent.name}-${project.artifactId}-${project.version}</name> -->
<build>
<finalName>company-${project.parent.name}-${project.artifactId}-${project.version}</finalName>
</build>
</project>
然后输出是:company-any-module-any-submodule-1.0-SNAPSHOT
有趣!我开始克隆 repo 并重现错误。如果能从下面提到的帮助我调试问题的任何步骤中获得任何线索,我将不胜感激 -
Maven Life Cycle Phases 问题发生的阶段是生命周期的
package
阶段。意思是mvn package
重现了您的项目的问题。遍历错误中的堆栈跟踪行。了解它失败的表达式评估 -
@Override public Object evaluate( String expr ) throws ExpressionEvaluationException { return evaluate( expr, null ); // Line 143 }
它也不是导致它的
finalName
属性。由于指定了相同的默认值<finalName>${artifactId}-${version}</finalName>
使用相同的项目配置工作正常。然后尝试将
any-submodule
的包装更改为<packaging>pom</packaging>
错误消失了。意思是打包为
jar
,war
等表达式评估不同并导致溢出。修改
any-module
或any-submodule
pom.xml
内容 我可以自信地说是project.parent.name
导致递归评估表达式并导致堆栈溢出(如何? - 是 I am still looking for..)。此外,更改<name>${project.parent.name}-${project.artifactId}</name>
到
<name>${parent.name}-${project.artifactId}</name>
在我没有收到错误但生成的 jar 类型为 -
的意义上对我有用${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar
和${parent.name}-any-submodule-1.0-SNAPSHOT
分别用变化。根据要求寻找解决方案,我正在寻找你正在使用的递归的尾巴。
注意 - 仍在努力寻找解决此问题的合适方法。
正如我在
我们先来看一下基础知识:
如POM Reference所述:
finalName: This is the name of the bundled project when it is finally built (sans the file extension, for example: my-project-1.0.jar). It defaults to ${artifactId}-${version}.
name: Projects tend to have conversational names, beyond the artifactId.
所以这两个有不同的用途。
name
纯粹是信息性的,主要用于生成的文档和构建日志。它不会被继承,也不会在其他任何地方使用。它是一个人类可读的字符串,因此可以包含任何字符,即文件名中不允许的空格或字符。所以,这将是有效的:<name>My Turbo Project on Speed!</name>
。这显然至少是一个有问题的工件文件名。如上所述,
finalName
是生成的神器的名称。它是继承的,所以它通常应该依赖属性。仅有的两个真正有用的选项是默认${artifactId}-${version}
和无版本${artifactId}
。其他一切都会导致混淆(例如名为foo
的项目创建了一个工件bar.jar
)。实际上,我的涡轮增压项目!将是有效的,因为这是一个有效的文件名,但实际上,这样的文件名往往是相当不可用的(例如,尝试从 bash 中寻址包含 ! 的文件名)
所以,关于 Whosebug 发生的原因:
name
不继承project.parent.name
也不会在插值期间进行计算,因为名称是 children 完全不可见的少数属性之一
parent.name
实际上曾经在旧的 Maven 版本中工作,但更多是由于错误(而且 已弃用 访问没有前导 [=20= 的属性]).- 缺失的 属性 不会被插值,即保留在模型中
- 因此,在
any-submodule
的有效 pom 中,finalName
的值仍然是(用mvn help:effective-pom
试试):${project.parent.name}-any-submodule
到目前为止还很糟糕。现在是 Whosebug
的原因Maven 有一个称为后期插值的附加功能,可以在实际使用插件参数时评估它们的值。这允许 pluing 使用不属于模型的属性,但在生命周期的早期由插件生成(这允许,例如,插件对最终名称做出 git 修订)。
那么会发生什么:
编辑:使错误的实际原因更清楚(见评论):
- 评估 jar 插件的最终名称:
@Parameter( defaultValue = "${project.build.finalName}", readonly = true )
PluginParameterExpressionEvaluator
启动并尝试评估最终名称(${project.parent.name}-any-submodule
,其中包含 属性 表达式 ${project.parent.name}.- 评估者询问模型,模型又return是parent项目的名称,即:
${project.parent.name}-any-module
. - 所以评估者试图解决这个问题,return
${project.parent.name}-any-module
(再次),因为 属性 总是根据当前项目解决,循环又开始了。 - 抛出 WhosebugError。
如何解决这个问题
遗憾的是,你不能。
您需要为每个项目明确指定 name
(以及 artifactId
)。没有解决方法。
那么,你可以让finalName
依赖它。然而,我会反对它(见我对
以这种方式更改最终名称的问题是本地构建工件的名称和存储库中的工件名称会不同,因此在本地您的工件被命名为 any-artifact-any-module-any-submodule.jar
,但您的工件名称存储库仍将是 any-submodule.jar
建议
- 如果你真的需要区分那个罚款,改为更改artifactId:
<artifactId>artifact-anymodule-anysubmodule</artifactId>
。 - 不要使用破折号作为短名称来区分结构的级别。
- 提示:模块的路径仍然可以
anymodule
,不需要是模块实际的artifactId! - 当我们这样做的时候:使用
name
的目的是为了人类可读,所以你可能会考虑一些更具视觉吸引力的东西(因为这是出现在构建日志中的名称) :<name>Artifact :: AnyModule :: AnySubModule</name>
. - 使用非常短的 groovy 脚本自动创建名称条目实际上非常容易。
- 您还可以编写强制规则来强制命名 artifactIds
对您问题的严格回答是,${project.parent.name}
不会 作为模型插值过程的一部分进行解析。反过来,您在代码的一个完全不同的地方有一个 WhosebugError
,即当...构建项目的最终 JAR 时。
第 1 部分:构建的模型错误
事情是这样的。当您在项目上启动 Maven 命令时,它采取的第一个操作是创建项目的有效模型。这意味着读取您的 POM 文件、使用激活的配置文件进行推理、应用继承、对属性执行插值……所有这些都是为了为您的项目构建最终的 Maven 模型。这项工作由 Maven Model Builder 组件完成。
建立模型的过程相当复杂,步骤很多,可能分为2个阶段,但我们感兴趣的部分在model interpolation部分。这是 Maven 将模型中所有由 ${...}
表示的标记替换为计算值的时候。它发生在注入配置文件并执行继承之后。在那个时间点,由 MavenProject
对象表示的 Maven 项目还不存在,只有它的 Model
正在构建。只有在您拥有完整模型后,您才能开始从中构建 Maven 项目。
因此,当插值完成时,它仅根据 POM 文件中存在的信息进行推理,并且唯一有效的值是 mentioned in the model reference. (This replacement is performed by the StringSearchModelInterpolator
class, if you want to look at the source code.) Quite notably, you will notice that the <parent>
element in the model does not contain the name of the parent model. The class Model
in Maven is actually generated with Modello from a source .mdo
file, and that source only defines groupId
, artifactId
, version
and relativePath
(along with a custom id
) for the <parent>
element. This is also visible in the documentation.
所有这些的结果是,在执行模型插值后,令牌 ${project.parent.name}
将不会被替换。而且,进一步地,由它构造的 MavenProject
将有一个包含 ${project.parent.name}
的名称未被替换。您可以在日志中看到这一点,在您的示例项目中,我们有
[INFO] Reactor Build Order:
[INFO]
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule
意味着 Maven 认为项目 any-module
的实际名称是 ${project.parent.name}-any-module
.
第 2 部分:奇怪的事情开始了
我们现在正处于 reactor 中的所有项目都已正确创建甚至编译的时候。实际上,理论上一切都应该工作得很好,但项目本身的名称完全是无聊的。但是你有一个奇怪的情况,它在创建带有 maven-jar-plugin
的 JAR 时失败了。在您的示例中构建失败并显示以下日志:
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ any-submodule ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] company-any-artifact ............................... SUCCESS [ 0.171 s]
[INFO] ${project.parent.name}-any-module .................. SUCCESS [ 0.002 s]
[INFO] ${project.parent.name}-any-submodule ............... FAILURE [ 0.987 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
这意味着在构建模型后出现了问题。原因是插件 injects the name of the project as a parameter:
/** * Name of the generated JAR. * * @parameter alias="jarName" expression="${jar.finalName}" <b>default-value="${project.build.finalName}"</b> * @required */ private String finalName;
注意 project.build.finalName
作为子模块生成的 JAR 名称的默认值。此注入和变量的插值由另一个名为 PluginParameterExpressionEvaluator
.
那么这里发生了什么:
any-submodule
上的JAR插件注入项目的最终名称,命名为${project.parent.name}-any-submodule
。- 感谢父项目的继承,在你最顶层的POM项目中声明了
<finalName>
,它继承了<finalName>${project.name}-${project.version}</finalName>
. - Maven 现在尝试为
any-submodule
插入${project.name}
。 - 由于第 1 部分,这解析为
${project.parent.name}-any-submodule
。 - Maven 现在尝试为
any-submodule
插入${project.parent.name}
。这可以正常工作:构建MavenProject
并在项目实例上调用getParent()
,返回具体的 Maven 父项目。因此,${project.parent.name}
将尝试解析any-module
的名称,实际上是${project.parent.name}-any-module
. - Maven 现在尝试插入
${project.parent.name}-any-module
,但仍在any-submodule
项目实例 上。对于PluginParameterExpressionEvaluator
,评估标记的根"project"
没有改变。 - Maven 现在尝试在
any-submodule
上插入${project.parent.name}
,这同样可以正常工作并且 returns${project.parent.name}-any-module
. - Maven 现在尝试在
any-submodule
上插入${project.parent.name}
... 这有效 returns${project.parent.name}-any-module
所以它会尝试评估${project.parent.name}
...
你可以看到这里发生了无穷无尽的递归,这导致了你拥有的 WhosebugError
。这是 PluginParameterExpressionEvaluator
中的错误吗?这还不清楚:它的原因是首先没有正确替换的模型值。从理论上讲,它可以处理评估 ${project.parent}
的特殊情况并创建一个新的 PluginParameterExpressionEvaluator
在这个父项目上工作,而不是总是在当前项目上工作。如果您对此有强烈的感觉,请随时创建 a JIRA issue.
第 3 部分:为什么没有子模块也能工作
通过上面所说的,您现在可以推断出为什么它在这种情况下有效。让我们推理一下 Maven 需要做什么来评估最终名称,因为必须在 Maven Jar 插件中注入:
any-module
上的JAR插件注入项目的最终名称,命名为${project.parent.name}-any-module
。- 感谢父项目的继承,在你最顶层的POM项目中声明了
<finalName>
,它继承了<finalName>${project.name}-${project.version}</finalName>
. - Maven 现在尝试为
any-module
插入${project.name}
。 - 这解析为
${project.parent.name}-any-module
,与之前相同。 - Maven 现在尝试为
any-module
插入${project.parent.name}
。就像以前一样,这可以正常工作:MavenProject
已构建,getParent()
将在项目实例上调用,返回具体的 Maven 父项目。因此,${project.parent.name}
将尝试解析any-artifact
的名称,实际上是company-any-artifact
. - 插值成功停止
而且你没有任何错误。