Filed under: Automation, Maven, — Tags: ${project.build.directory}, POJO, mojo — Thomas Sundberg — 2014-05-21
How can you get the build directory in your maven plugin?
The answer to this question may seem simple, just refer to ./target
in your plugin and everything
should work. This works in the simple case. It does, however, not work when you are using you plugin in a multi
module project. Your plugin will not know where it is executed and cannot refer to a subdirectory relative to the
directory where Maven is invoked. It may be executed in a multi module build or a single module.
The directory .
will refer to the execution directory and therefore will ./target
refer to
a target directory in the execution directory. This is not where the target directory lives in a multi module
project. The target directory will typically be ./moduleName/target
. Maven have the build directory, it
is stored in the property ${project.build.directory}
. So far so good, but how do you access this
property from the Java code you are writing your plugin in? It turns out that you can specify a parameter, with any
name you wish, and set its default value to ${project.build.directory}
. The example below will log the
build directory to the console when it is executed.
src/main/java/se/thinkcode/BuildDirMojo
package se.thinkcode; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; /** * Will log the absolute path to the target directory in the execution log */ @Mojo(name = "get-build-dir", defaultPhase = LifecyclePhase.VALIDATE) public class BuildDirMojo extends AbstractMojo { @Parameter(defaultValue = "${project.build.directory}") private String projectBuildDir; @Override public void execute() { Log log = getLog(); log.info(""); log.info("Project build dir: " + projectBuildDir); log.info(""); } }
When Maven populates the parameter projectBuildDir
, it will expand the expression ${project.build.directory}
to the actual value. This may be very reasonable if your background is shell scripting. As a developer I found the
syntax a bit strange. There is no apparent reason why a String
that should contain a default value is
expanded into something else. If I set something to default then I expect to see that value when I look at it later,
given that I haven't changed the value.
The implication of this is that if you need access to any Maven property in your plugin, just define a parameter and set its default value to the property you need.
If you are new to Maven plugin development, there are a few things that could be worth mentioning.
I subclass an AbstractMojo
that will give me access to a Maven logger.
I am using two specific Maven annotations to define some metadata about the plugin.
First I define that this class should be a Mojo
. A Mojo is the Maven equality to a a Pojo, Plain Old
Java Object. Mojo stands for "Maven plain Old Java Object". Using the Mojo annotation I
define that this plugin should be called using the goal "get-build-dir" from another Maven project. Using the same
annotation, I define that the default execution phase should be validate
. An example is defined later.
Second, I define a parameter that this Mojo should use. This parameter will be populated by Maven before the execution of the goal. In this case, it will be populated by expanding the default value defined into the current build directory using a Maven property.
The last part of this class is defining a method that will be executed. It is called execute
and will
be called when the goal get-build-dir
is called.
To be able to build the example above, I used the pom below:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode.blog</groupId> <artifactId>parameter-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.2</version> <configuration> <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound> </configuration> <executions> <execution> <id>mojo-descriptor</id> <phase>process-classes</phase> <goals> <goal>descriptor</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.2</version> </dependency> </dependencies> </project>
I define that the packaging type should be maven-plugin
. I also define that the maven-plugin-plugin
should be used. It will help me to package the plugin and expand the annotations used above to something Maven can
interpret and use. I also need two dependencies to get access to the AbstractMojo
and the annotations.
To test the plugin, I defined a pom like the one below and call it using: mvn validate
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode.blog</groupId> <artifactId>test-plugin</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>se.thinkcode.blog</groupId> <artifactId>parameter-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <executions> <execution> <goals> <goal>get-build-dir</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
You should now be able to get any Maven property into your plugin and be able to expand the Maven functionality with a Maven plugin when you find a need for it.
Think of one thing before you start writing you own plugin,though. There may be a Maven way to do things that you should use instead. It is probably easier and many of the habits Maven try to enforce onto your project are actually really good.