在父 pom 中定义 Maven 插件,但只在子项目中调用插件
Posted
技术标签:
【中文标题】在父 pom 中定义 Maven 插件,但只在子项目中调用插件【英文标题】:Define Maven plugins in parent pom, but only invoke plugins in child projects 【发布时间】:2012-10-07 03:20:41 【问题描述】:我有一组项目,它们都需要在构建期间运行相同系列的 Maven 插件执行。我想避免在每个项目中重新声明所有这些配置,所以我让它们都继承自父 pom“模板”项目,该项目只包含那些插件执行(8 个不同的 mojos)。但我希望这些插件执行只在子项目上运行,而不是在 Maven 构建期间在父项目上运行。
我尝试了四种不同的方式来完成这件事,每种方式都有我不喜欢的副作用。
在父 pom 的 build/plugins
元素中声明插件执行,并在父项目中使用 properties-maven-plugin 到 turn on the skip
properties on other plugins。这不起作用,因为插件目标之一 (maven-dependency-plugin:build-classpath) 没有 skip
属性。
在父 pom 的 build/pluginManagement
元素中声明插件执行。不幸的是,这需要我在每个子项目的 pom 的 build/plugins
元素中重新声明八个插件中的每一个,例如:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
...
如果我需要更改模板 pom 中的插件,这太重复了。
在父 pom 中的配置文件中声明插件执行,该配置由 nobuild.txt
文件的 lack 激活(在父 pom 中确实存在,因此插件不会在那里执行):
<profiles>
<profile>
<activation>
<file>
<missing>nobuild.txt</missing>
</file>
</activation>
<build>
....
</build>
</profile>
</profiles>
这在大多数情况下都有效,除了missing
元素中的文件路径似乎基于当前工作目录,而不是基于项目的目录。这打破了我希望能够做的一些多模块构建。 编辑:澄清一下,父“模板”项目实际上本身就是多模块项目中的一个模块,并且当我尝试例如在根目录上执行mvn install
时,构建会中断。项目结构如下:
+ job
|- job-core
|- job-template
|- job1 inherits from job-template
|- job2 inherits from job-template
设置自定义生命周期和打包。这似乎允许我将插件绑定到生命周期阶段,but not specify any configuration。
那么,是否有另一种方法来指定一组可以在多个项目中重复使用的 Maven 插件执行(在每个项目的 pom 中重复最少)?
【问题讨论】:
你父 pom 的<packaging>
是什么?由于生命周期依赖于打包,唯一可能的解决方案(除了你提到的那些)可能是<packaging>pom</packaging>
。
我会选择第三种选择。但是请您告诉我它是如何破坏一些多模块构建的。我并没有真正关注你。
为什么不在第三种选择中指定<missing>$basedir/nobuild.txt</missing>
?对于执行其他 mojo 组合的 maven 插件,这可能是一个很好的用例。但我不相信这样的插件已经存在。
@AndrewLogvinov 是的,我在父级中使用 pom 包装
重复***.com/questions/1625492/…
【参考方案1】:
这是第五种方式。我认为它有最小的缺点:没有配置文件、没有自定义生命周期、没有子 POM 中的声明、插件没有“跳过”要求。
从https://***.com/a/14653088/2053580 复制它——非常感谢end-user!
<build>
<pluginManagement>
<plugins>
<plugin>
<!-- Main declaration and configuration of the plugin -->
<!-- Will be inherited by children -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<!--This must be named-->
<id>checkstyle</id>
<phase>compile</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<!-- You may also use per-execution configuration block -->
<configuration...>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<!-- This declaration makes sure children get plugin in their lifecycle -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<!-- Configuration won't be propagated to children -->
<inherited>false</inherited>
<executions>
<execution>
<!--This matches and thus overrides execution defined above -->
<id>checkstyle</id>
<!-- Unbind from lifecycle for this POM -->
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
【讨论】:
这是侵入性最小的,至少在我的业余专家看来。使用带有none
阶段的最终用户方法,该阶段阻止父级执行,但保持子级使用带有它自己的参数和诸如此类的插件的轨道。我喜欢这个。【参考方案2】:
我最终编写了自己的插件,该插件利用 mojo-executor 来调用其他 mojo。这使我能够 1) 集中构建配置和 2) 最大限度地减少在每个子项目中重复的配置量。
(如果您对所有这些的原因感到好奇:每个子项目都是一个将从命令行执行的作业。构建设置了一个调用者 shell 脚本并将其附加到构建中,因此它被检查到我们的工件存储库中。部署脚本稍后将它们拉到它们将运行的机器上。)
模板项目的pom的相关部分:
<project ...>
<parent> ... </parent>
<artifactId>job-template</artifactId>
<packaging>pom</packaging>
<name>job project template</name>
<build>
<pluginManagement>
<plugin>
<groupId>...</groupId>
<artifactId>job-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-sources-step</id>
<goals><goal>job-generate-sources</goal></goals>
</execution>
<execution>
<id>package-step</id>
<goals><goal>job-package</goal></goals>
</execution>
... (a couple more executions) ...
</executions>
</plugin>
</pluginManagement>
</build>
</project>
必须创建一个新的 maven-plugin 项目(job-maven-plugin)。 Pom 看起来像:
<project ...>
<parent> ... </parent>
<artifactId>job-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>job maven executor plugin</name>
<dependencies>
<dependency>
<groupId>org.twdata.maven</groupId>
<artifactId>mojo-executor</artifactId>
<!-- version 1.5 supports Maven 2, while version 2.0 only supports Maven 3 -->
<version>1.5</version>
</dependency>
</dependencies>
</project>
从模板项目中可以看出,我的插件中有多个 mojo(每个阶段一个需要发生的事情)。例如,job-package mojo 绑定到 package 阶段并使用 mojo-executor 库来运行另外两个 mojo(它们只是附加一些构建工件):
/**
* @goal job-package
* @phase package
*/
public class PackageMojo extends AbstractMojo
/**
* @parameter expression="$project"
* @required
* @readonly
*/
protected MavenProject project;
/**
* @parameter expression="$session"
* @required
* @readonly
*/
protected MavenSession session;
/**
* @component
* @required
*/
protected PluginManager pluginManager;
@Override
public void execute() throws MojoExecutionException, MojoFailureException
ExecutionEnvironment environment = executionEnvironment(project, session, pluginManager);
// Attach script as a build artifact
executeMojo(
plugin(
groupId("org.codehaus.mojo"),
artifactId("build-helper-maven-plugin"),
version("1.7")
),
goal("attach-artifact"),
configuration(
element("artifacts",
element("artifact",
element("file", "$project.build.directory/script.shl"),
element("type", "shl")
)
)
),
environment
);
// Zip up the jar and script as another build artifact
executeMojo(
plugin(
groupId("org.apache.maven.plugins"),
artifactId("maven-assembly-plugin"),
version("2.3")
),
goal("single"),
configuration(
element("descriptors",
element("descriptor", "$project.build.directory/job/descriptor.xml")
)
),
environment
);
然后,在子项目中,我只需要引用一次插件。在我看来,这比在每个子项目中重复每个幕后插件要好得多(这不可接受地增加了 pom 之间的耦合)。如果我以后想在构建过程中添加一个 mojo 执行,我只需要修改一个地方并增加一个版本号。子项目的 pom 看起来像:
<project ...>
<parent>
<groupId> ... </groupId>
<artifactId>job-template</artifactId>
<version> ... </version>
<relativePath>../job-template</relativePath>
</parent>
<artifactId>job-testjob</artifactId>
<name>test job</name>
<build>
<plugins>
<plugin>
<groupId> ... </groupId>
<artifactId>job-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
此外,整个多模块目录结构现在如下所示:
+- job
+- job-core
+- job-maven-plugin
+- job-template
+- job-testjob1 (inherits from job-template)
+- job-testjob2 (inherits from job-template)
在我看来,这个解决方案并不完全是最优的,因为我现在将插件配置嵌入到一系列 mojos 而不是 pom 中,但它满足了我的目标,即集中配置并最大限度地减少子项目 pom 之间的重复。
(最后一点:我刚刚发现 maven-aggregate-plugin 似乎允许在 pom 中对多个插件执行进行分组。这可能以稍微更理想的方式解决了问题,但我没有心情重做最后几个小时的工作。但可能对其他人有益。)
【讨论】:
感谢 maven-aggregate-plugin 链接。这看起来很不错。【参考方案3】:就我个人而言,我会选择解决方案 2。重复次数最少,如果可能,您应该尽量避免配置文件,以避免开始记录需要为哪些项目激活哪些配置文件。
只需执行mvn (clean) install
即可正确构建项目。
【讨论】:
嗯,这是我定义配置文件方式的好处——它会自动为孩子激活,但不会为父母激活。它只是在构建根项目时不起作用。我不必担心多个配置文件,因为模板父项目只有一个用途,并且配置文件并没有真正用作配置文件(它并不意味着在命令行上打开或关闭)。 【参考方案4】:这是我在寻找相同问题的解决方案时发现的另一个选项here。我在这里发布它是因为这已经是一个很好的选项集合 - 希望它可以帮助其他人不要花费数小时来解决这个问题:
鉴于插件具有跳过执行的选项,该选项可以在父 pom(部分)中打开,并将“继承”标志设置为 false,以便它不会传播给子代:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<inherited>true</inherited>
<configuration>
...
</configuration>
</execution>
</executions>
<!-- Skip plugin execution for parent pom but enable it for all children -->
<configuration>
<skipAssembly>true</skipAssembly>
</configuration>
<inherited>false</inherited>
</plugin>
虽然我在第一次阅读时对这个解决方案持怀疑态度,但它对我有用 - 至少对汇编插件来说是这样。
【讨论】:
我认为这实际上是最好的方法,如果插件没有跳过属性你可以改变阶段【参考方案5】:另一种选择:您可以更改执行绑定到不存在的值(例如never
)的阶段,而不是使用“跳过”属性。
这与@Timi 建议的<inherited>false</inherited>
方法一起使用非常好
【讨论】:
以上是关于在父 pom 中定义 Maven 插件,但只在子项目中调用插件的主要内容,如果未能解决你的问题,请参考以下文章
Maven中plugins和pluginManagement的区别