Java
Maven

Maven 3.5のビルドライフサイクルを学ぶ

概要

Maven3のビルドライフサイクル、フェーズ、ゴールについて学んだ内容をまとめた記事です。
なお、依存関係の解決やアーティファクトのリポジトリへのインストール、プロファイルの仕組み等については扱いません。

環境

  • Windows 10 Professional
  • Java 1.8.0_144
  • Maven 3.5.2

参考

インストール

Mavenのdownloadページよりバイナリのアーカイブファイルをダウンロードし適当な場所へ展開します。
展開したディレクトリにあるbinを環境変数pathに追加します。

バージョン確認

> mvn --version
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T16:58:13+09:00)
Maven home: D:\dev\apache-maven-3.5.2\bin\..
Java version: 1.8.0_144, vendor: Oracle Corporation
Java home: C:\Program Files\Java\jdk1.8.0_144\jre
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Usage

mvn [options] [<goal(s)>] [<phase(s)>]

主な用語

Mavenの仕組みを理解するうえでよく出てくる用語についてまとめます。

ビルドライフサイクル

Maven3には下記の3つのbuilt-inビルドライフサイクルがあります。

  • default : プロジェクトのアーティファクトの作成およびデプロイ
  • clean : プロジェクトのクリーニング
  • site : プロジェクトサイトの作成およびデプロイ

それぞれのビルドライフサイクルは決められた一連のフェーズから構成されています。
たとえばcleanビルドライフサイクルは下記の3つのフェーズから構成されています。

  • pre-clean
  • clean
  • post-clean

ビルドライフサイクルがどのようなフェーズで構成されているかはcomponents.xmlで定義されていて、maven-3.5.2の場合ではmaven-core-3.5.2.jarのMETA-INF/plexus/components.xmlで確認することができます。
下記はcleanビルドライフサイクルの定義をcomponents.xmlから抜粋した内容です。

<?xml version="1.0" encoding="UTF-8"?>
<component-set>
  <components>

    <component>
      <role>org.apache.maven.lifecycle.Lifecycle</role>
      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
      <role-hint>clean</role-hint>
      <configuration>
        <id>clean</id>

        <phases>
          <phase>pre-clean</phase>
          <phase>clean</phase>
          <phase>post-clean</phase>
        </phases>
        <default-phases>
          <clean>
            org.apache.maven.plugins:maven-clean-plugin:2.5:clean
          </clean>
        </default-phases>

      </configuration>
    </component>

  </components>
</component-set>

ちなみにmvnコマンドの引数にビルドライフサイクルを指定して実行することはできません。
下記のmvnコマンドはcleanビルドライフサイクルを実行するという意味ではなく、cleanビルドライフサイクルのcleanフェーズを実行するように指定しています。なので実行されるのはpre-cleanとcleanフェーズでpost-cleanフェーズは実行されません。

> mvn clean

試しにdefaultビルドライフサイクル(の全体)を実行するつもりで下記のmvnコマンドを実行するとエラーになります。

> mvn default

// 省略

[ERROR] Unknown lifecycle phase "default". You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]

// 省略

フェーズ

フェーズとは、ビルドライフサイクルで実行する各工程のことです。あるフェーズを実行すると、そのフェーズのビルドライフサイクルの先頭フェーズから指定したフェーズまで順番に実行されます。

たとえばdefaultビルドライフサイクルのcompileフェーズを実行すると、defaultビルドライフサイクルの先頭フェーズであるvalidateからはじまりinitialize → generate-sources → process-sources → generate-resources → process-resources → compileの順で実行されます。

それぞれのフェーズは0個以上のゴールから構成されています。ゴールは後述しますが実態はMavenプラグインの特定の処理(を実装したJavaクラス)です。
フェーズに対してゴールが紐づけられている(以下、バインドと表記します)場合に、ゴールが実行されます。バインドの方法はいくつかありますがプラグインがデフォルトのフェーズを設定したり、pom.xmlで指定するなどの方法があります。

mvnコマンドでフェーズを指定するにはフェーズ名を指定します。
下記はcleanフェーズを実行します。

> mvn clean

フェーズはフェーズ名を半角スペースで区切って複数個指定することができます。
下記はcleanライフサイクルのcleanフェーズを実行し、続けてdefaultライフサイクルのpackageフェーズを実行します。

> mvn clean package

ゴールがバインドされていないフェーズもありますが、そのようなフェーズでは何の処理も実行されません。
たとえばdefaultビルドライフサイクルのvalidateフェーズにはデフォルトではなんのゴールもバインドされていないため、下記のmvnコマンドを実行しても何も実行されません。

> mvn validate

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.120 s
[INFO] Finished at: 2017-11-27T20:05:18+09:00
[INFO] Final Memory: 6M/276M
[INFO] ------------------------------------------------------------------------

このような、ゴールがバインドされていないフェーズはデフォルトでは意味を持っていませんが、サードパーティや自作のプラグインをバインドしてビルドライフサイクルを拡張するような使い方ができます。

コマンドラインから通常は実行しないフェーズ

pre-post-process-などのprefixが付いたフェーズは、通常コマンドラインから実行しないようですが、指定してはいけないというものでもないようです。

以下は公式ページの関連個所からの引用です。

Introduction to the Build Lifecycle

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. These phases sequence the build, producing intermediate results that are not useful outside the build. In the case of invoking integration-test, the environment may be left in a hanging state.

ゴール

ゴールとは、Mavenプラグインに実装されている特定の処理を実行するJavaクラスにつけられた名前です。Mavenはフェーズとこの名前を紐づけます。
1つのMavenプラグインには複数のゴールを実装することができ、1つのゴールはmaven-plugin-apiのAbstractMojoクラス(AbstractMojoはMojoインターフェースをimplementしている)を継承しています。

たとえばmaven-compiler-pluginの場合、compileとtestCompileというゴールを持っていますが、それぞれのゴールは下記のように実装されています。
MojoアノテーションのdefaultPhase属性で、そのゴールをバインドするデフォルトフェーズを指定しています。

compileゴール

CompilerMojo.java
@Mojo( name = "compile", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true, 
    requiresDependencyResolution = ResolutionScope.COMPILE )
public class CompilerMojo
    extends AbstractCompilerMojo
{

    //...省略

    public void execute()
        throws MojoExecutionException, CompilationFailureException
    {
        if ( skipMain )
        {
            getLog().info( "Not compiling main sources" );
            return;
        }

        super.execute();

        if ( outputDirectory.isDirectory() )
        {
            projectArtifact.setFile( outputDirectory );
        }
    }

    //...省略

}

testCompileゴール

TestCompilerMojo.java
@Mojo( name = "testCompile", defaultPhase = LifecyclePhase.TEST_COMPILE, threadSafe = true,
                requiresDependencyResolution = ResolutionScope.TEST )
public class TestCompilerMojo
    extends AbstractCompilerMojo
{

    //...省略

    public void execute()
        throws MojoExecutionException, CompilationFailureException
    {
        if ( skip )
        {
            getLog().info( "Not compiling test sources" );
            return;
        }
        super.execute();
    }

    //...省略

}

ゴールが実行されるタイミング

ゴールが実行される状況はいくつかありますが主だったものに下記があります。

  1. フェーズにバインドされ、そのフェーズが実行された場合
  2. mvnコマンドから明示的にゴールを指定された場合
    1. 明示的に指定したゴールが依存しているフェーズが実行された場合
  3. 他のプラグインから呼び出された場合(例えばTimMoore/mojo-executorを使って)

たとえばソースコードをコンパイルするためにmaven-compiler-pluginのcompileゴールを実行したい場合は

フェーズを指定してコンパイル

compiler:compileゴールはデフォルトでcompileフェーズにバインドされているのでcompileフェーズを指定するとcompileゴールを実行します。

> mvn compile

ゴールを指定してコンパイル

このようにゴールを指定することでもcompileゴールを実行することができます。

> mvn compiler:compile

この2つの実行方法はソースコードをコンパイルするということでは同じですが、厳密に言うと実行結果が違う場合があります。

compileフェーズを実行した場合は、defaultビルドライフサイクルのvalidateフェーズからcompileフェーズまで順に実行されます。またpom.xmlでなんらかのプラグインのゴールをcompileフェーズにバインドしている場合は、そのゴールも実行されます。

compiler:compileゴールを実行した場合は、単にこのゴールの処理だけが実行されます。(後述しますが、ゴールの実装によっては、そのゴールが他のフェーズに依存している場合がありますので、そのときは依存するフェーズも実行されます)

ゴールが依存するフェーズ

ゴールを指定して直接実行する場合、そのゴールが依存しているフェーズがあれば、まずそのフェーズが実行されます。

たとえばspring-boot-maven-pluginのrunゴールは、test-compileフェーズに依存しているので、下記のように実行した場合、まずtest-compileフェーズまで実行された後に、runゴールが実行されます。

> mvn spring-boot:run

以下は公式ドキュメントからの関連個所の引用です。

spring-boot:run

Attributes:
Invokes the execution of the lifecycle phase test-compile prior to executing itself.

ゴールのスキップ

ゴールの中にはプロパティによってゴールの実行をスキップさせることができるものがあります。

ゴール プロパティ
resources:resources maven.resources.skip
resources:testResources maven.test.skip
compiler:compile maven.main.skip
compiler:testCompile maven.test.skip
surefire:test maven.test.skip
skipTests
failsafe:integration-test maven.test.skip
skipITs
failsafe:verify maven.test.skip
skipITs
install:install maven.install.skip
deploy:deploy maven.deploy.skip

testフェーズのスキップについて

以下のプロパティはtestフェーズに加え、integration-test、verifyフェーズをスキップします。

> mvn clean install -DskipTests

以下のプロパティはtestフェーズに加え、test-compile、integration-test、verifyフェーズをスキップします。

> mvn clean install -Dmaven.test.skip=true

プラグイン

プラグインにはMaven Projectがサポートする公式のプラグインと、それ以外(例えばサードパーティが開発するプラグインやユーザーが自作するプラグイン)に分けることができます。
公式のプラグインはAvailable Pluginsで確認することができます。

プラグインの命名には規則があり、この命名規則で公式プラグインかどうかを判別することができます。
公式のプラグインの名前は下記のルールになります。(自作のプラグインにこのルールで名前を付けてはいけません。)

maven-{plugin-name}-plugin

公式以外のプラグインの名前は下記のルールになります。

{plugin-name}-maven-plugin

補足

各ビルドライフサイクルのデフォルトのゴール

cleanビルドライフサイクル

pre-clean、post-cleanフェーズにはゴールがバインドされていないので何も実行されません。

phase plugin goal
pre-clean
clean maven-clean-plugin clean
post-clean

defaultビルドライフサイクル

defaultビルドライフサイクルでは、どのフェーズでどのゴールが実行されるかはパッケージの種類によって変わります。詳細はPlugin Bindings for default Lifecycle Referenceで確認できます。
下表はパッケージの種類がjarの場合です。
ちなみにパッケージの種類はpom.xmlのpackaging要素で定義します。明示的に定義しない場合はデフォルトとしてjarが選択されます。

phase plugin goal
validate
initialize
generate-sources
process-sources
generate-resources
process-resources maven-resources-plugin resources
compile maven-compiler-plugin compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources maven-resources-plugin testResources
test-compile maven-compiler-plugin testCompile
process-test-classes
test maven-surefire-plugin test
prepare-package
package maven-jar-plugin jar
pre-integration-test
integration-test
post-integration-test
verify
install maven-install-plugin install
deploy maven-deploy-plugin deploy

siteビルドライフサイクル

phase plugin goal
pre-site
site maven-site-plugin site
post-site
site-deploy maven-site-plugin deploy

Toolsプラグイン

Maven Projectがサポートするプラグインには、Toolsプラグインのようにデフォルトではビルドライフサイクルにバインドされていないプラグインがあります。このようなプラグインは特定の処理を実行するタスクとして利用します。

Toolsプラグインには以下のようなものがあります。

Maven Archetype Plugin

generateゴール

アーキタイプと呼ぶプロジェクトのテンプレートからMavenプロジェクトのひな型を生成します。

> mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId=com.example \
-DartifactId=my-project \
-Dversion=1.0-SNAPSHOT

Mavenプラグインのプロジェクトのひな型を生成するにはrchetypeArtifactIdにmaven-archetype-pluginを指定します。

> mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-plugin \
-DgroupId=com.example \
-DartifactId=my-maven-plugin \
-Dversion=1.0-SNAPSHOT

Maven Dependency Plugin

treeゴール

プロジェクトの依存関係をツリー表示します。

> mvn dependency:tree

結果をファイルに保存するにはoutputFileでファイル名を指定します。
このファイルをバージョン管理することで依存関係の変化を把握しやすくなります。

> mvn dependency:tree -DoutputFile=dependecy.txt

ビルド時に自動的に出力させるにはライフサイクルに組み込みます。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>tree</id>
            <phase>compile</phase>
            <goals>
                <goal>tree</goal>
            </goals>
            <configuration>
                <outputFile>dependency.txt</outputFile>
            </configuration>
        </execution>
    </executions>
</plugin>

Maven Help Plugin

effective-pomゴール

デフォルトの設定を含めたpom.xmlの内容を表示します。

> mvn help:effective-pom

systemゴール

環境変数やシステムプロパティを表示します。

> mvn help:system

describeゴール

pluginプロパティで指定するプラグインのhelpメッセージを表示します。

> mvn help:describe -Dplugin=compiler

より詳細な情報を得るにはdetailプロパティを指定します。

> mvn help:describe -Dplugin=compiler -Ddetail

cmdプロパティにフェーズを指定するとそのフェーズを含むビルドライフサイクルの各フェーズにバインドされているプラグインを調べることができます。

> mvn help:describe -Dcmd=package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building my-project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-help-plugin:2.2:describe (default-cli) @ my-project ---
[INFO] 'package' is a phase corresponding to this plugin:
org.apache.maven.plugins:maven-jar-plugin:2.4:jar

It is a part of the lifecycle for the POM packaging 'jar'. This lifecycle includes the following phases:
* validate: Not defined
* initialize: Not defined
* generate-sources: Not defined
* process-sources: Not defined
* generate-resources: Not defined
* process-resources: org.apache.maven.plugins:maven-resources-plugin:2.6:resources
* compile: org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
* process-classes: Not defined
* generate-test-sources: Not defined
* process-test-sources: Not defined
* generate-test-resources: Not defined
* process-test-resources: org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
* test-compile: org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
* process-test-classes: Not defined
* test: org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
* prepare-package: Not defined
* package: org.apache.maven.plugins:maven-jar-plugin:2.4:jar
* pre-integration-test: Not defined
* integration-test: Not defined
* post-integration-test: Not defined
* verify: Not defined
* install: org.apache.maven.plugins:maven-install-plugin:2.4:install
* deploy: org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.147 s
[INFO] Finished at: 2017-12-03T13:34:00+09:00
[INFO] Final Memory: 11M/276M
[INFO] ------------------------------------------------------------------------

それぞれのプラグインでhelpゴールを実行することで同様のhelpメッセージが取得できます。

> mvn compiler:help

サードパーティのプラグイン

MojoHaus Maven Plugins Project

たくさんのプラグインを公開しているプロジェクトです。Codehasが2015年で運営を停止したため、MojaHausはそれを引き継ぐ形で発足しました。(詳しくはThe Demise of Open Source Hosting Providers Codehaus and Google Codeをご覧ください)

メジャーなプラグインではFindBugs Maven Pluginがあります。

Spring Boot Maven Plugin

spring-projects/spring-boot

Spring Bootアプリケーションで使用するプラグインです。

Goals

Goal Binds by default to the lifecycle phase
run validate
repackage package
start pre-integration-test
stop post-integration-test
build-info generate-resources

runゴール

spring bootアプリケーションを実行します。

> mvn spring-boot:run

プロファイルを指定するにはrun.profilesパラメータを使用します。

> mvn spring-boot:run -Drun.profiles=dev,test

なお、Spring Boot 2.0以降は下記のようになる予定です。(記2018/2/10)

> mvn spring-boot:run -Dspring-boot.run.profiles=dev,test

build-infoゴール

ビルド情報をtarget/classes/META-INF/build-info.propertiesに出力します。この情報はspring-boot-actuatorから参照されます。
プロパティファイルにどのような情報を出力するかはカスタマイズ可能です。
このゴールはデフォルトで、defaultビルドライフサイクルのgenerate-resourcesフェーズにバインドされています。

デフォルトでは以下の情報が出力されます。

#Properties
#Fri Dec 01 14:01:25 JST 2017
build.time=2017-12-01T14\:01\:25+0900
build.artifact=singleds
build.group=com.example
build.name=singleds
build.version=0.0.1-SNAPSHOT

spring-boot-actuatorを有効にしていなくてもClass BuildPropertiesを介して取得できます。

@Autowired
private BuildProperties prop;

Dependency-check-maven Plugin

脆弱性を持つ依存関係があるかチェックしてくれるプラグインです。

Goals

Goal Binds by default to the lifecycle phase
aggregate
check
update-only
purge

checkゴール

プロジェクトが依存するライブラリに脆弱性があるか検査します。

> mvn dependency-check:check