0

I currently am using the Exec Maven Plugin and it works fine with:

<plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>1.4.0</version>
      <executions>
        <execution>
          <id>myExec</id>
          <goals>
            <goal>exec</goal>
          </goals>
          <phase>generate-sources</phase>
          <configuration>
            <executable>myExec</executable>
            <arguments>
              <argument>--foo=${basedir}/src/test/resources/test.xml</argument>
              <argument>--output-directory=target/generated-sources/output</argument>
            </arguments>
          </configuration>
        </execution>
      </executions>
    </plugin> 

I am also using the build helper plugin as follows:

<plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <version>1.10</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>add-source</goal>
            </goals>
            <configuration>
              <sources>
                <source>${project.build.directory}/generated-sources/output</source>
              </sources>
            </configuration>
          </execution>
        </executions>
      </plugin>

This is incredibly verbose however and I want multiple maven modules to be able to use this program and not have to retype all the exec plugin specific XML along with the builder XML.

Question: How do I possibly combine these 2 into another plugin?

I have used the maven archetype generator to generate a sample maven plugin and have a Mojo class:

@Mojo(name = "touch", defaultPhase = LifecyclePhase.PROCESS_SOURCES)
public class MyMojo
        extends AbstractMojo
{


    public void execute()
            throws MojoExecutionException
    {
        ExecMojo exec = new ExecMojo();
    }
}

And have figured out how to create a new ExecMojo.

Question How do I add the arguments here as I would in the XML above? And how can I integrate these arguments into my plugin?

A_Di-Matteo
  • 26,902
  • 7
  • 94
  • 128
Lucas
  • 2,514
  • 4
  • 27
  • 37
  • [Guide to Creating Archetypes](https://maven.apache.org/guides/mini/guide-creating-archetypes.html) –  Jun 09 '16 at 17:57
  • I don't think an archetype will work. We have existing modules that need this functionality by combining the functionality found in these 2 plugins. – Lucas Jun 09 '16 at 19:25

1 Answers1

2

Instead of creating your own Maven plugin, which may reduce portability and maintenability of your project, you may consider the following approach instead:

  • Have a common parent pom
  • Configure the given plugins configuration, optionally in a Maven profile
  • In the concerned module, point to this parent. Optionally (in case of profiled configuration) activate it on demand when required.

A simple parent pom would look like the following:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample</groupId>
    <artifactId>sample-maven-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <path.to.myexec>path</path.to.myexec>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.4.0</version>
                <executions>
                    <execution>
                        <id>myExec</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <executable>${path.to.myexec}\myExec</executable>
                            <arguments>
                                <argument>--foo=${basedir}/src/test/resources/test.xml</argument>
                                <argument>--output-directory=${project.build.directory}/generated-sources/output</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>1.10</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/generated-sources/output</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>myexec-profile</id>
            <build>
                <plugins>
                    <!-- optionally move here the configuration above -->
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

Note the path.to.myexec property I added, to be overriden in children projects if required, in order to point to the correct relative path.

Then, once installed in your machine (or deployed in your company Maven repository), it can be referenced as following in any concerned Maven projects:

<parent>
    <groupId>com.sample</groupId>
    <artifactId>sample-maven-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

No need to re-declare the plugin configurations above and its verbose approach. They will be automatically part of the defaul build as part of the inherited build configuration from this parent.


The profiled approach can also be a solution if:

  • You want to reuse an existing parent pom already used by projects which don't need this configuration
  • You don't always need this behavior on your build and you want to activate it on demand

In such a case, you can then activate it via, as an example:

mvn clean package -Pmyexec-profile

Given that you already set the parent accordingly and you moved into the profile the configuration above.


Advantages of this approach:

  • lighter option than writing a new maven plugin (which needs to be written, tested, maintained, distributed, etc.)
  • easier for consumer modules to customize something: at any moment they can override parent's configuration as an exception
  • less fragile: just imagine what if another version of one of these plugins provides a bug fix important for you, that's easy to configure an XML, much less easy to change the customized maven plugin etc.
  • configuration remains centralized, transparently accessible and entry point for further governance
  • easier troubleshooting: at any moment a consumer module can run mvn help: effective-pom and see the merged full effective pom (as aggregate of parent and current pom) and check what it's actually running

How to skip parent plugin executions in certain modules

A simple (and often used) approach to execute this plugins only in certain modules while having the parent in common with other modules is the following:

  • Define a new property, let's call it skip.script.generation, with default value to true, defined in the parent pom.
  • Use this skip property in the skip configuration entry of the plugins above.
  • Re-define the property only in the concerned modules and set it to false. This will be the only configuration required for their pom.xml files, hence reduced to one line (keeping verbosity really low).

The exec-maven-plugin provides such a skip option, unfortunately the build-helper-maven-plugin doesn't. But that's not blocking us. We can still skip the two executions playing with their phase element, setting it to a non existing phase, like none and as such skipping them. This is suitable because the two executions are actually already attached to the same phase, generate-sources.

For this approach, let's rename our new property to script.generation.phase.

As an example:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample</groupId>
    <artifactId>sample-maven-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <path.to.myexec>path</path.to.myexec>
        <script.generation.phase>none</script.generation.phase>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.4.0</version>
                <executions>
                    <execution>
                        <id>myExec</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>${script.generation.phase}</phase>
                        <configuration>
                            <executable>${path.to.myexec}\myExec</executable>
                            <arguments>
                                <argument>--foo=${basedir}/src/test/resources/test.xml</argument>
                                <argument>--output-directory=${project.build.directory}/generated-sources/output</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>1.10</version>
                <executions>
                    <execution>
                        <phase>${script.generation.phase}</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/generated-sources/output</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>myexec-profile</id>
            <build>
                <plugins>
                    <!-- optionally move here the configuration above -->
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

Note the <phase>${script.generation.phase}</phase> changes for both plugins. With its default value to none, this property is effectively disabling their executions by default.

In another module you would then have the following:

<parent>
    <groupId>com.sample</groupId>
    <artifactId>sample-maven-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

<properties>
    <script.generation.phase>generate-sources</script.generation.phase>
</properties>

And nothing else. That is. Maven during the build will re-define the property for a certain module and automatically replace it in the configuration inherited from its parent and as such enabling again the two executions above.

A_Di-Matteo
  • 26,902
  • 7
  • 94
  • 128
  • Great answer but I have a couple follow up questions. If I use the parent pom approach is this executable automatically called in every maven sub-module? I would like to configure it such that this executable is only called in certain sub-modules. Also is there a way to get a profile to be run during every build? It seems like that might be the better solution for us, but this is something we want baked in to specific sub-module poms and always run since this generates required code for the module. – Lucas Jun 15 '16 at 16:00
  • 1
    @lnunno yes, by default they will be called by all modules, but there are ways to only have them executed in certain modules, the simplest one is to skip them by default via a property and to re-define this property (and only this property) in the modules where you want them to be executed. I will update my answer with an example. – A_Di-Matteo Jun 15 '16 at 16:07
  • 1
    @lnunno I added at the bottom of my answer an approach on how to enable these executions only for certain modules, with really minimal verbosity in children modules, only one line of additional configuration would be required. – A_Di-Matteo Jun 15 '16 at 16:25
  • Our build process ended up being complex enough that we started down a purely XML implementation and hit a wall quickly, I ended up creating a Maven plugin and it wasn't too complicated or a lot of code to get the functionality that we were looking for. – Lucas Jul 06 '17 at 04:36