2

I have created a new compiler for the maven-compiler-plugin. This compiler likes to look for compile sources in both src/main/groovy and src/main/java. Similarly, test sources are in both src/test/groovy and src/test/java.

I am aware of the build-helper-maven-plugin that allows users to augment their pom to specify new source folders for a build, but using this plugin is not ideal since it requires more than 20 lines of extra configuration in the pom.

I would like to create a mojo that configures the extra source folders automatically.

Here is the mojo that I have created, but I do not know how to ensure that the mojo is executed at the right time.

/**
 * @goal add-groovy-source
 * @phase generate-sources
 * @requiresDependencyResolution compile
 * @execute phase="compile"
 */
public class AddGroovySourceFolders extends AbstractMojo {
    /**
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info("Adding /src/main/groovy to the list of source folders");
        this.project.addCompileSourceRoot(project.getBasedir() + "/src/main/groovy");
    }
}

Again, my goal is to ensure the minimal amount of configuration in the user's pom. By this I mean that there should only be a declaration of a dependency to the plugin that contains this mojo and no further configuration.

Andrew Eisenberg
  • 28,387
  • 9
  • 92
  • 148

1 Answers1

2

It is possible by defining a custom lifecycle.

Step 1: Define the MOJO to add paths. You did this already with AddGroovySourceFolders.

Step 2: Create a META-INF/plexus/components.xml file in src/main/resources:

<component-set>
    <components>
        <component>
            <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
            <role-hint>jar</role-hint>
            <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
            <configuration>
                <lifecycles>
                    <lifecycle>
                        <id>default</id>

                        <phases>
                            <!-- I added this one, use appropriate plugin groupId/artifactId instead-->
                            <initialize>groovyadd:maven-groovyadd-plugin:add-build-paths</initialize>
                            <process-resources>org.apache.maven.plugins:maven-resources-plugin:2.4.3:resources</process-resources>
                            <compile>org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile</compile>
                            <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:2.4.3:testResources</process-test-resources>
                            <test-compile>org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile</test-compile>
                            <test>org.apache.maven.plugins:maven-surefire-plugin:2.7.2:test</test>
                            <package>org.apache.maven.plugins:maven-jar-plugin:2.3.1:jar</package>
                            <install>org.apache.maven.plugins:maven-install-plugin:2.3.1:install</install>
                            <deploy>org.apache.maven.plugins:maven-deploy-plugin:2.5:deploy</deploy>
                        </phases>
                    </lifecycle>
                </lifecycles>
            </configuration>
        </component>
    </components>
</component-set>

The element specifies the packaging the lifecycle is for. Apparently you can override the jar lifecycle (I got this working with Maven 3.0.3). I copied everything apart from the initialize phase from the appropriate component in maven-core-3.0.3.jar\META-INF\plexus\components.xml.

The side effect of overriding Maven's JAR lifecycle is that you have now hardcoded the compile and other plugin's versions what is in your plugin instead of what is in Maven. I'm not sure whether that's a bad or good thing.

Step 3: When using your plugin from another project, all you need is:

<plugins>
    ...
    <plugin>
        <groupId>groovyadd</groupId>
        <artifactId>maven-groovyadd-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
        <extensions>true</extensions>
    </plugin>
    ...
</plugins>

The important part is the extensions element. Without it, the custom lifecycle from your plugin will not be picked up.

You can also add other plugins to other phases in the lifecycle (e.g. compile groovy code in the compile phase in a separate plugin rather than add a compiler for the maven-compiler-plugin).

References: Overriding the Default Lifecycle from the Maven book

prunge
  • 22,460
  • 3
  • 73
  • 80
  • Thanks for this. I am going to try this out. – Andrew Eisenberg Dec 08 '11 at 19:40
  • Excellent. I am starting to get things working. A few more questions, though. I'm not happy that the versions of the goals in each phase are explicit. I removed them from my component.xml and things seem to work. Do you see any problems with that? Also, these components are specific to the jar lifecycle. I'm guessing that I would need to copy things over for the other interesting lifecycles (eg- ear, war, maven-plugin, etc). Do you see any way around that? – Andrew Eisenberg Dec 08 '11 at 21:36
  • 1
    @AndrewEisenberg removing the versions means I think that the latest version of the plugin will get picked, which might or might not be what you want. A positive is that it will work and use reasonably appropriate plugin versions no matter what the version of Maven being used is. A negative is if the user has a later version of, say, the compiler plugin, it will get used when your plugin is active instead of Maven's default one. If you need to support more packaging types (war, ejb, etc.) you will need to override the lifecycle configuration for each one. – prunge Dec 08 '11 at 21:49
  • @AndrewEisenberg [POM Mixins](http://stackoverflow.com/questions/2456375/how-to-use-maven-3-mixins) [(more info)](http://jaxenter.com/maven-3-0-the-future-of-maven-10580.html) is probably exactly what you want, but they didn't make it into Maven 3.0, it is possible they will get into Maven 3.1. – prunge Dec 08 '11 at 21:52
  • Thanks, @prunge. The problem that I see is that if I do use versions, then someone running with (say) maven 2.x will use still be using the versions that are appropriate for maven 3.0.3 (because that's what is specified in the components.xml). Also, I guess I will have to copy a whole bunch of lifecycle mappings from maven-core's components.xml. – Andrew Eisenberg Dec 08 '11 at 22:25
  • Thanks for the help. I awarded you the bounty. There are still some unanswered questions, like whether or not to include version numbers in the custom lifecycle, but this general approach is working. And I'll bring up the remaining issues with the users. – Andrew Eisenberg Dec 09 '11 at 16:41