61

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

matts
  • 6,738
  • 1
  • 33
  • 50
  • What is the `` of your parent pom? Since lifecycle is depdendent on packaging the only possible solution (in addition to the ones that you've mentioned) could be `pom`. – Andrew Logvinov Oct 17 '12 at 11:34
  • 1
    I would go with the 3rd alternative. But could you please enlighten me how it *breaks some of the multimodule builds*. I don't really follow you on that. – maba Oct 17 '12 at 12:22
  • 2
    why don't you specify `${basedir}/nobuild.txt` in the 3rd alternative? This might be a good use case for a maven plugin that executes a composition of other mojos. But I don't believe that such a plugin already exists. – SpaceTrucker Oct 17 '12 at 13:27
  • @AndrewLogvinov yes, I am using pom packaging in the parent – matts Oct 17 '12 at 14:50
  • @maba Sorry, I should have given more information. My "parent" project is actually itself a module of another parent project. The projects which inherit from my template parent are actually module siblings of the parent project. So when I build the root project, it uses its base directory for the activation rule, doesn't find the file, and tries to execute the plugins in the template project. I suppose I could refactor the template project out of the multimodule project, but I was also hoping to avoid that :P – matts Oct 17 '12 at 14:53
  • @SpaceTrucker Adding ${basedir} doesn't seem to work in the `` tag. The missing activation always triggers when I try that. :( – matts Oct 17 '12 at 14:55
  • PS: thank you for all of your responses! – matts Oct 17 '12 at 14:56
  • 2
    Duplicate of http://stackoverflow.com/questions/1625492/execute-maven-plugin-goal-on-child-modules-but-not-on-parent – Hilikus Mar 27 '13 at 19:34

5 Answers5

32

Here's the fifth way. I think it has minimum drawbacks: no profiles, no custom lifecycle, no declarations in child POMs, no 'skip' requirement for plugins.

Copied it from https://stackoverflow.com/a/14653088/2053580 -- many thanks to 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>
Oleg Efimov
  • 632
  • 1
  • 7
  • 17
  • 1
    This is the least invasive, at least in my amateur-maven-opinion. Uses the end-user approach with a `none` phase which keeps the parent from executing, but keeps the children on track to use the plugin with it's own parameters and whatnot. I like this. – searchengine27 Sep 18 '19 at 20:51
11

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

matts
  • 6,738
  • 1
  • 33
  • 50
5

Personally I would go for solution 2. The repetition is minimal and you should try to avoid profiles if possible to avoid having to start documenting which profiles need to be activated for which projects.

A project should be able to be built correctly by simply doing a mvn (clean) install.

Stijn Geukens
  • 15,454
  • 8
  • 66
  • 101
  • 1
    Well, that was the benefit of the way I had defined the profile--it would automatically activate for children, but not the parent. It just didn't work when building the root project. I don't have to worry about multiple profiles, because the template parent project has only one purpose and the profile is not really being used as a profile (it isn't meant to be switched on or off on the command line). – matts Oct 17 '12 at 15:21
3

Here's another option I discovered here while looking for solution to the same problem. I'm posting it here because this is already a good collection of options - in the hope that it might help others not spend hours on solving this issue:

Given that the plugin has the option to skip execution, that option could be turned on in the parent pom ( section), with "inherit" flag set to false so that it's not propagated to children:

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

While I was quite skeptical about this solution when I first read it, it worked for me - at least for the assembly plugin.

Timi
  • 774
  • 9
  • 16
1

One more option: instead of using a "skip" property you can change the phase an execution is bound to a non existent value like never.

This works very nice together with the <inherited>false</inherited> approach suggested by @Timi

Jakub Bochenski
  • 3,113
  • 4
  • 33
  • 61