在父pom中定义Maven插件,但仅在子项目中调用插件 [英] Define Maven plugins in parent pom, but only invoke plugins in child projects

查看:135
本文介绍了在父pom中定义Maven插件,但仅在子项目中调用插件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组项目,这些项目在构建期间都需要运行相同系列的Maven插件执行.我想避免在每个项目中重新声明所有此配置,因此我使它们全部继承自父pom模板"项目,该项目仅包含那些插件执行(8个不同的mojo).但是我希望这些插件执行仅在Maven构建期间在子项目上运行,而不在父项目上运行.

我尝试完成这四种不同的方式,每种方式都有我不喜欢的副作用.

  1. 在父pom的build/plugins元素中声明插件执行,并使用properties-maven-plugin来mvn install时,构建就会中断.项目结构如下:

    + job
    |- job-core
    |- job-template
    |- job1                   inherits from job-template
    |- job2                   inherits from job-template
    

  2. 设置自定义生命周期和包装.这似乎使我可以将插件绑定到生命周期阶段,解决方案

我最终编写了自己的插件,该插件利用 mojo-执行程序来调用其他Mojos.这使我能够(1)集中化构建配置,以及(2)最小化在每个子项目中重复的配置量.

(如果您对所有这些原因感到好奇:每个子项目都是一个作业,将在命令行中执行.该构建会设置一个调用程序外壳脚本并将其附加到该构建,因此它将被检入到我们的工件存储库中.稍后,部署脚本会将它们放到将在其上运行的计算机上.)

模板项目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).例如,作业程序包mojo绑定到程序包阶段,并使用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中对多个插件执行进行分组,这可能以一种较为理想的方式解决了该问题,但我不想重做最后几个小时的工作,尽管这可能对其他人有好处.)

I have a set of projects which all need to run the same series of Maven plugin executions during their builds. I'd like to avoid re-declaring all of this configuration in every project, so I made them all inherit from a parent pom "template" project which only contains those plugin executions (8 different mojos). But I want those plugin executions to only run on the child projects and not on the parent project during Maven builds.

I've tried to accomplish this four different ways, each with a side-effect I don't like.

  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.

So, is there another way to specify a bunch of Maven plugin executions that can be reused across several projects (with minimal repetition in each of those projects' poms)?

解决方案

I ended up writing my own plugin which utilizes mojo-executor to invoke other mojos. This allows me to 1) centralize the build configuration and 2) minimize the amount of configuration that gets duplicated in each of the child projects.

(In case you are curious about the reason for all of this: each child project is a job which will be executed from the command line. The build sets up an invoker shell script and attaches it to the build so it gets checked into our artifact repository. A deploy script later pulls these down onto the machine they will run on.)

Relevant parts of the template project's 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>

Had to create a new maven-plugin project (job-maven-plugin). Pom looks like:

<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>

As you can see from the template project, there were multiple mojos in my plugin (one per phase that needed stuff to happen). As an example, the job-package mojo is bound to the package phase and uses the mojo-executor library to run two other mojos (which just attach some build artifacts):

/**
 * @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
        );
    }
}

Then, in the child projects, I just have to refer to the plugin once. In my opinion, this is greatly preferable over reiterating each of the behind-the-scenes plugins in every child project (which unacceptably increases the coupling between poms). If I want to add a mojo execution to the build procedure in the future, I only have to modify one place and bump a version number. Child project's pom looks like:

<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>

Also, the entire multimodule directory structure now looks like this:

+- job
  +- job-core
  +- job-maven-plugin
  +- job-template
  +- job-testjob1               (inherits from job-template)
  +- job-testjob2               (inherits from job-template)

In my opinion this solution is not entirely optimal, since I now have plugin configuration embedded in a series of mojos instead of a pom, but it meets my goals for centralizing the configuration and minimizing the duplication among child project poms.

(One last note: I just discovered maven-aggregate-plugin which seems to allow grouping multiple plugin executions in the pom. This might have solved the problem in a slightly more desirable way, but I'm not in the mood to redo the last few hours of work. Might be beneficial to someone else though.)

这篇关于在父pom中定义Maven插件,但仅在子项目中调用插件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆