在父POM中定义Maven插件,但只在子项目中调用插件

63
我有一组项目,它们在构建过程中都需要运行相同的一系列Maven插件执行。我希望避免在每个项目中重新声明所有这些配置,因此我让它们都继承自一个父POM“模板”项目,该项目仅包含这些插件执行(8个不同的mojos)。但是我希望这些插件执行仅在子项目上运行,而不是在Maven构建期间在父项目上运行。
我尝试了四种不同的方法来实现这一点,但每种方法都有我不喜欢的副作用。
  1. Declare the plugin executions in the parent pom's build/plugins element and use properties-maven-plugin to turn on the skip properties on other plugins in the parent project. This didn't work because one of the plugin goals (maven-dependency-plugin:build-classpath) doesn't have a skip property.

  2. Declare the plugin executions in the parent pom's build/pluginManagement element. Unfortunately this requires me to redeclare each of the eight plugins in the build/plugins element of every child project's pom like:

    <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
    </plugin>
    ...
    

    This is too repetitive, and problematic if I ever need to change the plugins in the template pom.

  3. Declare the plugin executions in a profile in the parent pom which is activated by the lack of a nobuild.txt file (which does exist in the parent pom, so the plugins don't execute there):

    <profiles>
        <profile>
            <activation>
                <file>
                    <missing>nobuild.txt</missing>
                </file>
            </activation>
            <build>
                ....
            </build>
        </profile>
    </profiles>
    

    This works for the most part, except that the file path in the missing element seems to be based on the current working directory instead of the project basedir. This breaks some of the multimodule builds I'd like to be able to do. Edit: to clarify, the parent "template" project is actually itself a module in a multimodule project, and the build breaks when I try, for instance, to do a mvn install on the root. The project structure looks like:

    + job
    |- job-core
    |- job-template
    |- job1                   inherits from job-template
    |- job2                   inherits from job-template
    
  4. Set up a custom lifecycle and packaging. This seems to allow me to bind plugins to lifecycle phases, but not specify any configuration.

那么,是否有另一种方法来指定一堆Maven插件执行,可以在多个项目中重复使用(并且在这些项目的每个pom中最小化重复)?


1
我会选择第三种方案。但是你能否请教我它如何“破坏了一些多模块构建”?我对此并不是很明白。 - maba
2
为什么你不在第三个选项中指定 <missing>${basedir}/nobuild.txt</missing> 呢?这可能是一个执行其他 mojos 组合的 Maven 插件的好用例。但我不相信这样的插件已经存在。 - SpaceTrucker
@AndrewLogvinov 是的,我在父级中使用了pom打包。 - matts
@maba 抱歉,我应该提供更多信息。 我的“父”项目实际上是另一个父项目的模块。 从我的模板父项目继承的项目实际上是父项目的模块兄弟。 因此,当我构建根项目时,它使用其基本目录作为激活规则,找不到文件,并尝试在模板项目中执行插件。 我想我可以将模板项目重构为多模块项目之外,但我也希望避免这样做:P - matts
2
执行Maven插件目标在子模块上,但不要在父级上执行。 - Hilikus
显示剩余3条评论
5个回答

32
这是第五种方法。我认为它的缺点最少:没有配置文件、自定义生命周期、子POM中的声明,也没有插件的“跳过”要求。
https://dev59.com/92Uq5IYBdhLWcg3wQ-NF#14653088复制 -- 感谢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>

1
这是我作为一个业余Maven用户的最小侵入式解决方案。它采用了终端用户方法,使用“none”阶段来防止父级执行,但保持子级跟踪使用插件及其自己的参数等。我喜欢这个。 - searchengine27

11

我最终编写了自己的插件,利用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(每个阶段需要发生的mojo一个)。例如,job-package mojo绑定到包阶段,并使用mojo-executor库运行另外两个mojo(这些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)
在我看来,这个解决方案并不完全优化,因为现在我将插件配置嵌入了一系列的mojo中,而不是pom文件,但它达到了我集中配置和最小化子项目pom之间重复的目标。
(最后一个注意事项:我刚发现 maven-aggregate-plugin ,它似乎允许将多个插件执行分组在pom文件中。 这可能以稍微更理想的方式解决了问题,但我没有心情重新做过去几个小时的工作。 但是对其他人可能有好处。)

感谢提供maven-aggregate-plugin的链接。看起来非常不错。 - SpaceTrucker

5

个人而言我会选择方案2。重复最小化,如果可能应该避免使用配置文件,以避免需要记录哪些配置文件需要在哪些项目中激活。

一个项目只需要简单地执行 mvn (clean) install 即可正确构建。


1
那就是我定义配置文件的好处——它会自动为子项目激活,但不会为父项目激活。但在构建根项目时,它并没有起作用。我不必担心多个配置文件,因为模板父项目只有一个目的,而配置文件并没有真正被用作配置文件(它不是用于在命令行上开启或关闭的)。 - matts

3

我在寻找解决相同问题的方案时,发现了另一个选项(这里)。我将其发布在这里,因为这已经是一个很好的选项集合——希望它能帮助其他人不要花费数小时来解决这个问题:

鉴于该插件具有跳过执行的选项,该选项可以在父POM(<build>部分)中打开,将“inherit”标志设置为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>

当我第一次阅读这个解决方案时,我非常怀疑,但它对我起作用了-至少对于汇编插件是这样。


我认为这实际上是最好的方法,如果插件没有跳过属性,你可以更改阶段。 - Jakub Bochenski

1

另外一个选项:不使用“skip”属性,而是将执行阶段绑定到一个不存在的值,比如never

这个方法与@Timi建议的<inherited>false</inherited>方法非常搭配使用。


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