How do you force a maven MOJO to be executed only

2020-06-03 04:20发布

问题:

I have a MOJO I would like executed once, and once only after the test phase of the last project in the reactor to run.

Using:

if (!getProject().isExecutionRoot()) {
        return ;
}

at the start of the execute() method means my mojo gets executed once, however at the very beginning of the build - before all other child modules.

回答1:

The best solution I have found for this is:

/**
 * The projects in the reactor.
 *
 * @parameter expression="${reactorProjects}"
 * @readonly
 */
private List reactorProjects;

public void execute() throws MojoExecutionException {

    // only execute this mojo once, on the very last project in the reactor
    final int size = reactorProjects.size();
    MavenProject lastProject = (MavenProject) reactorProjects.get(size - 1);
    if (lastProject != getProject()) {
        return;
    }
   // do work
   ...
}

This appears to work on the small build hierarchies I've tested with.



回答2:

The best solution is relying on a lifecycle extension by extending your class from org.apache.maven.AbstractMavenLifecycleParticipant (see also https://maven.apache.org/examples/maven-3-lifecycle-extensions.html) which got a method afterSessionEnd added with https://issues.apache.org/jira/browse/MNG-5640 (fixed in Maven 3.2.2).



回答3:

There is a Sonatype blog entry that describes how to do this. The last project to be run will be the root project as it will contain module references to the rest. Thereforec you need a test in your mojo to check if the current project's directory is the same as the directory from where Maven was launched:

boolean result = mavenSession.getExecutionRootDirectory().equalsIgnoreCase(basedir.toString());

In the referenced entry there is a pretty comprehensive example of how to use this in your mojo.



回答4:

I think you might get what you need if you use the @aggregator tag and bind your mojo to one of the following lifecycle phases:

  • prepare-package
  • package
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install
  • deploy


回答5:

The solution with using session.getEventDispatcher() no longer works since Maven 3.x. The whole eventing has been removed in this commit: https://github.com/apache/maven/commit/505423e666b9a8814e1c1aa5d50f4e73b8d710f4



回答6:

Check out maven-monitor API

You can add an EventMonitor to the dispatcher, and then trap the END of the 'reactor-execute' event: this is dispatched after everything is completed, i.e. even after you see the BUILD SUCCESSFUL/FAILED output.

Here's how I used it recently to print a summary right at the end:

/**
 * The Maven Project Object
 *
 * @parameter expression="${project}"
 * @required
 * @readonly
 */
protected MavenProject project;


/**
 * The Maven Session.
 *
 * @parameter expression="${session}"
 * @required
 * @readonly
 */
protected MavenSession session;

...


@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
    //Register the event handler right at the start only
    if (project.isExecutionRoot())
        registerEventMonitor();
    ...
}


/**
 * Register an {@link EventMonitor} with Maven so that we can respond to certain lifecycle events
 */
protected void registerEventMonitor()
{
    session.getEventDispatcher().addEventMonitor(
            new EventMonitor() {

                @Override
                public void endEvent(String eventName, String target, long arg2) {
                    if (eventName.equals("reactor-execute"))
                        printSummary();
                }

                @Override
                public void startEvent(String eventName, String target, long arg2) {}

                @Override
                public void errorEvent(String eventName, String target, long arg2, Throwable arg3) {}


            }
    );
}


/**
 * Print summary at end
 */
protected void printSummary()
{
    ...
}


回答7:

Normally, this is a matter of configuration. You might have to setup a project just for the mojo and make it dependent on all of the other projects. Or you could force one of the child projects to be last by making it dependent on all of the other children.