在父 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 的&lt;packaging&gt; 是什么?由于生命周期依赖于打包,唯一可能的解决方案(除了你提到的那些)可能是&lt;packaging&gt;pom&lt;/packaging&gt; 我会选择第三种选择。但是请您告诉我它是如何破坏一些多模块构建的。我并没有真正关注你。 为什么不在第三种选择中指定&lt;missing&gt;$basedir/nobuild.txt&lt;/missing&gt;?对于执行其他 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 建议的&lt;inherited&gt;false&lt;/inherited&gt; 方法一起使用非常好

【讨论】:

以上是关于在父 pom 中定义 Maven 插件,但只在子项目中调用插件的主要内容,如果未能解决你的问题,请参考以下文章

在父模块上执行 Maven 插件目标,但不在子模块上

Maven中plugins和pluginManagement的区别

Maven之pom知识点

maven项目的继承

dependencies与dependencyManagement的区别

控制每次执行时继承的 Maven 插件