Lesson 1: Analyzing the Build Lifecycle

1. Overview

In this lesson, we'll learn about the concept of the Maven lifecycles and phases, and how to run them for our project.

The relevant module we need to import when we're working with this lesson is: analyzing-the-build-lifecycle.

This lesson only needs a single reference codebase, so there is no end version of the project.

 

2. Phases and Lifecycles

Maven is based around the concept of a lifecycle, which is a collection of phases that accomplish some end goal. For most software projects the end goal is to deliver all artifacts to some final destination such as a server or repository for others to use.

To start, let's look at phases. A phase is a discrete and well-defined task, typically involving inputs and outputs. Some example phases include:

  • Compiling .java files into .class files
  • Executing unit tests
  • Creating a .jar file

A lifecycle, on the other hand, is a collection of phases. Within a lifecycle, phases are sequential and depend on the previous phases. This means the phases that execute later in the lifecycle require the previous phases to have already been executed first.

As an example, a test phase often depends on the compile phase because the tests cannot be run unless the .class files exist. Likewise, a package phase depends on the test phase to ensure we don't create artifacts that have known problems.

Because of these phase dependencies, Maven halts execution if a phase fails. This means if we execute the test phase but the compile phase fails, Maven won't run the test phase.

Finally, Maven can skip a phase in a lifecycle when it determines it isn't necessary. For example, if the .java files in a project haven't changed since the last time they were compiled into .class files, Maven can skip that step and move on to the next phase.

 

3. The Default Lifecycle

Maven includes a default lifecycle that is sufficient for many Java software projects. The default lifecycle contains over 20 phases that can take a project from a directory of source code and resources and generate a .jar file that is ready to run in production.

For simplicity, we'll only discuss the most important phases of the default lifecycle. These are presented below in the order in which they execute within the default lifecycle:

  • validate: Performs basic checks on the project such as the POM file being valid
  • initialize: Loads the POM file and ensures all the required directories are created if necessary
  • compile: Compiles the main source code of our project into .class files
  • test-compile: Analogous to the compile phase, except it works on the test source code
  • test: Executes all of the tests and checks if they completed successfully
  • package: Gather the compiled code and package it in a distributable format, such as a .jar or .war
  • verify: Perform any checks to verify the package is valid and meets quality criteria
  • install: Install the package into the local repository only
  • deploy: Copies the package to its final destination such as a remote system or artifact repository

As mentioned previously, these are the most commonly used phases of the default lifecycle. The other phases are for less common tasks such as generating dynamic source code, copying resource files, annotation processors, and so on. These additional phases often have prefixes such as generate- or process-. It's good to be aware of their existence because we'll see them mentioned by other Maven phases. However, rarely will we ever need to execute them directly.

 

4. Other Maven Lifecycles

In addition to the default lifecycle, Maven includes two other lifecycles that are available for all projects. We'll briefly discuss them here.

 

4.1. Clean

As the name suggests, this lifecycle has an end goal of removing dynamically created files from the default lifecycle. It has one primary phase that we'll use:

  • clean: Removes all dynamic output from the previous build

Like the default lifecycle, the clean lifecycle also includes some lesser-known phases that we don't need to work with directly.

 

4.2. Site

The site lifecycle has an end goal of generating documentation for a project. It has two phases that are commonly used:

  • site: Generates the documentation for a project
  • site-deploy: Copies the generated documentation to a location outside the project

Typical uses for the site lifecycle are to create Javadoc or API documentation for a project. It can also generate various reports or metrics. As with the two other lifecycles, there are additional phases not mentioned above because they aren't typically used directly.

 

5. Common Maven Commands

To execute a Maven phase we simply provide the name of the phase to the Maven command line.

For example, let's run the validate phase for our Java project:

This phase doesn't produce much output, but the lack of any error messages lets us know the project is valid.

Now let's run the compile phase on the project: 

The output confirms that our main source code files are being compiled and where the .class files will go.

Next, let's run the test phase to execute our unit tests:



The output from each test is included, along with a final summary indicating how many tests ran and if any failed.

And finally, let's run the install phase on the project:

Notably, Maven ran the test phase again, created a .jar file as part of the package phase, and then copied it to the local Maven repository. If any of these prior phases failed, Maven would exit with a failure code and indicate which phase had the failure.

This is really helpful for automated processes like pipelines or Git hooks. For example, we can reject commits to the primary development branch that cause test failures right away, rather than waiting until later in the delivery cycle to discover the issue.

It's also possible to invoke multiple phases from different lifecycles. For example, it may be beneficial to run the clean lifecycle prior to compiling our project. In this case, we can combine multiple phase names into a single command:


5.1. Command Line Flags

The mvn command accepts a variety of flags that alter the normal behavior. Below is a list of the most commonly used flags:

  • -h: Displays a list of all Maven command line flags
  • -X, --debug: Enables additional debug output when running a Maven command. Helpful for troubleshooting failures.
  • -q, --quiet: Prevents all output except for errors
  • -D: Defines custom user properties

The -D flag works just like it does for the java and javac commands. It allows us to pass arbitrary properties that certain phases or lifecycles might be interested in.

As an example, we can pass -DskipTests to the Maven command to force the default lifecycle to skip the test phase. This is useful in cases where we want to generate a final package for running locally even if we know a test is failing.

Complete and Continue