Introduction to the Build Lifecycle

Build Lifecycle Basics

Maven is based around the central concept of a build lifecycle. What this means is that the process for building and distributing a particular artifact (project) is clearly defined.

There are three built-in build lifecycles: default, clean and site. The default lifecycle handles your project deployment, the clean lifecycle handles project cleaning, while the site lifecycle handles the creation of your project’s site documentation.

A Build Lifecycle is Made Up of Phases

Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.

The clean lifecycle comprises of the following phases:

  • pre-clean - execute processes needed prior to the actual project cleaning
  • clean - remove all files generated by the previous build
  • post-clean - execute processes needed to finalize the project cleaning

Here are some of the most important phases in the default lifecycle:

  • validate - validate the project is correct and all necessary information is available
  • compile - compile the source code of the project
  • test - test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
  • package - take the compiled code and package it in its distributable format, such as a JAR.
  • verify - run any checks on results of integration tests to ensure quality criteria are met
  • install - install the package into the local repository, for use as a dependency in other projects locally
  • deploy - done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.

These lifecycle phases (plus the other lifecycle phases not shown here) are executed sequentially to complete the default lifecycle.

The Site lifecycle comprises of the following phases:

  • pre-site execute processes needed prior to the actual project site generation
  • site generate the project’s site documentation
  • post-site execute processes needed to finalize the site generation, and to prepare for site deployment
  • site-deploy deploy the generated site documentation to the specified web server

Usual Command Line Calls

Phases are executed in a specific order. This means that if we run a specific phase using the command:

1
mvn <PHASE>

This won’t only execute the specified phase but all the preceding phases as well. When a phase is given, Maven will execute every phase in the sequence up to and including the one defined.

In a build environment, use the following call to cleanly build and deploy artifacts into the shared repository.

1
mvn clean deploy

Maven executes clean, then executes deploy (including all of the prior build phase steps).

A Build Phase is Made Up of Plugin Goals

A plugin goal represents a specific task (finer than a build phase) which contributes to the building and managing of a project. It may be bound to zero or more build phases. A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation. The order of execution depends on the order in which the goal(s) and the build phase(s) are invoked.

1
mvn clean dependency:copy-dependencies package

Some Phases Are Not Usually Called From the Command Line

The phases named with hyphenated-words (pre-*, post-*, or process-*) are not usually directly called from the command line.

Setting Up Your Project to Use the Build Lifecycle

Packaging

The first, and most common way, is to set the packaging for your project via the equally named POM element <packaging>. Some of the valid packaging values are jar, war, ear and pom. If no packaging value has been specified, it will default to jar.

Each packaging contains a list of goals to bind to a particular phase.

Note that for some packaging types to be available, you may also need to include a particular plugin in the <build> section of your POM and specify <extensions>true</extensions> for that plugin.

Plugins

The second way to add goals to phases is to configure plugins in your project. Plugins are artifacts that provide goals to Maven. Furthermore, a plugin may have one or more goals wherein each goal represents a capability of that plugin, plugins can contain information that indicates which lifecycle phase to bind a goal to.

The goals that are configured will be added to the goals already bound to the lifecycle from the packaging selected. If more than one goal is bound to a particular phase, the order used is that those from the packaging are executed first, followed by those configured in the POM. Note that you can use the <executions> element to gain more control over the order of particular goals.

For example, let’s say you have a goal display:time that echos the current time to the commandline, and you want it to run in the process-test-resources phase to indicate when the tests were started. This would be configured like so:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
...
 <plugin>
   <groupId>com.mycompany.example</groupId>
   <artifactId>display-maven-plugin</artifactId>
   <version>1.0</version>
   <executions>
     <execution>
       <phase>process-test-resources</phase>
       <goals>
         <goal>time</goal>
       </goals>
     </execution>
   </executions>
 </plugin>
...

We can use the following command to list all goals in a specific plugin:

1
mvn <PLUGIN>:help

For example, to list all goals in the Failsafe plugin:

1
mvn display:help

To run a specific goal, without executing its entire phase (and the preceding phases) we can use the command:

1
mvn <PLUGIN>:<GOAL>

Building a Maven Project

To build a Maven project, we need to execute one of the life cycles by running one of their phases:

1
mvn deploy

This will execute the entire default lifecycle. Alternatively, we can stop at the install phase:

1
mvn install

But usually we’ll use the command:

1
mvn clean install

To clean the project first – by running the clean lifecycle – before the new build.

We can also run only a specific goal of the plugin:

1
mvn compiler:compile

Introduction to the POM

What is a POM?

A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details used by Maven to build the project. It contains default values for most projects.

Super POM

The Super POM is Maven’s default POM. All POMs extend the Super POM unless explicitly set, meaning the configuration specified in the Super POM is inherited by the POMs you created for your projects.

Minimal POM

The minimum requirement for a POM are the following:

  • project root
  • modelVersion - should be set to 4.0.0
  • groupId - the id of the project’s group.
  • artifactId - the id of the artifact (project)
  • version - the version of the artifact under the specified group

Project Inheritance

Elements in the POM that are merged are the following:

  • dependencies
  • developers and contributors
  • plugin lists (including reports)
  • plugin executions with matching ids
  • plugin configuration
  • resources
The Scenario 1

And let us specify their directory structure as the following:

1
2
3
4
.
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

com.mycompany.app:my-module:1’s POM

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
  </parent>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

With this setup, our module can now inherit some of the properties of our parent POM.

The Scenario 2
1
2
3
4
5
.
 |-- my-module
 |   `-- pom.xml
 `-- parent
     `-- pom.xml

To address this directory structure (or any other directory structure), we would have to add the <relativePath> element to our parent section.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>../parent/pom.xml</relativePath>
  </parent>
 
  <artifactId>my-module</artifactId>
</project>

Project Aggregation

Project Aggregation is similar to Project Inheritance. But instead of specifying the parent POM from the module, it specifies the modules from the parent POM. By doing so, the parent project now knows its modules, and if a Maven command is invoked against the parent project, that Maven command will then be executed to the parent’s modules as well. To do Project Aggregation, you must do the following:

  • Change the parent POMs packaging to the value “pom”.
  • Specify in the parent POM the directories of its modules (children POMs).
The Scenario 1

directory structure

1
2
3
4
.
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

If we are to aggregate my-module into my-app, we would only have to modify my-app.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>my-module</module>
  </modules>
</project>

Now, whenever a Maven command processes com.mycompany.app:my-app:1, that same Maven command would be ran against com.mycompany.app:my-module:1 as well.

The Scenario 2

directory structure:

1
2
3
4
5
.
 |-- my-module
 |   `-- pom.xml
 `-- parent
     `-- pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<project>
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>../my-module</module>
  </modules>
</project>

Project Inheritance vs Project Aggregation

If you have several Maven projects, and they all have similar configurations, you can refactor your projects by pulling out those similar configurations and making a parent project. Thus, all you have to do is to let your Maven projects inherit that parent project, and those configurations would then be applied to all of them.

And if you have a group of projects that are built or processed together, you can create a parent project and have that parent project declare those projects as its modules. By doing so, you’d only have to build the parent and the rest will follow.

But of course, you can have both Project Inheritance and Project Aggregation.

Project Interpolation and Variables

One of the practices that Maven encourages is don’t repeat yourself. However, there are circumstances where you will need to use the same value in several different locations. To assist in ensuring the value is only specified once, Maven allows you to use both your own and pre-defined variables in the POM.

For example, to access the project.version variable, you would reference it like so:

1
  <version>${project.version}</version>

Available Variables

Project Model Variables

Any field of the model that is a single value element can be referenced as a variable. For example, ${project.groupId}, ${project.version}, ${project.build.sourceDirectory} and so on.

Special Variables

${project.basedir}, ${project.baseUri}, ${maven.build.timestamp} and so on.

Properties

You are also able to reference any properties defined in the project as a variable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<project>
  ...
  <properties>
    <mavenVersion>3.0</mavenVersion>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-artifact</artifactId>
      <version>${mavenVersion}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-core</artifactId>
      <version>${mavenVersion}</version>
    </dependency>
  </dependencies>
  ...
</project>

Reference

  1. Introduction to the Build Lifecycle
  2. Maven Goals and Phases