僕とMavenの3650日

# Mavenって何

MavenはJavaのビルド・パッケージングツールです。誤解を恐れずにいうならばJavaプログラムの **「ビルドサイクルマネージャ」**ともいえます。今回はこのMavenについてまとめていきます。

いつものことですが、この文書には「本当ではないこと」が含まれています。理解しやすさを優先して経緯の時系列や仕様など丸めながら再考しています。読んでくれた方の興味関心に少しでも応えられたらうれしい限りです。

## Mavenと僕

僕の今の会社での最初の仕事は、Javaのプロジェクトをコンパイルすることだった。大規模な金融バックシステムをコンパイル、パッケージングするためのビルドツールとして僕はAntをを選んだ。Antはきちんと仕事をして、予定通り動作しリリースすることがきた。Subversionと連動したこの自動ビルド・リリースの仕掛けは、当時としてはちょっとした物だったけど、私のオリジナルだったことからなかなかに手離れが悪いものとなった。以後数年このビルドシステムの子守を僕がすることになってしまった。

**「Ant書けるなんてすごいじゃん。ところでMavenって知ってる？」**

今でも尊敬しているある先輩から、言われたときでも


* 出始めで、資料が少ない。Antに比べて実績がない
* 難しそう
* Antで十分

こんな理由から採用を見送った。もう少し勉強して、採用すればよかった。せっかくキーワードはもらっていたのに僕はその努力を怠った。


僕とMavenの出会いは、ちょっとほろ苦いものだった。

## さぁビルドを始めよう
### ビルドって何？

さて、ソフトウェアのビルドとはなんだろう。一言でいうと「ソースコードをかき集めて動作可能なモジュールとしてまとめてしまうこと」だと思う。

大規模開発だとこれが意外に大変。I/F勝手に変えるアホとか、I/F追加のタイミングが合わなくて、他のプログラムが先に実装したりとか。。主にI/F面ですごーく苦労する。

![ビルドフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/9f3376e1-adaa-0715-8469-5637d1edfef2.png)


ビルドの作業内容は大同小異あっても大体こんな感じじゃないだろうか。

|No.|作業項目|作業内容|検討事項など|
|--|--|--|--|
|1.|SRC収集|SCMもしくは（狂ってるけど）ファイルサーバから対象のソースをかき集める。|どうやってソースコードを集める？|
|2.|ライブラリ収集|プログラムが使うライブラリをコンパイルのために集めておく|ライブラリの管理方法を考える。<li>コンパイル時にだけ必要なライブラリ</li><li>実行時環境にも持ち込む必要があるライブラリ</li><li>テスト時だけ必要なライブラリ</li>|
|3.|コンパイル|ソースコードを実行可能コードに変換する。（ちょっと違うけど）|コンパイルする方法を考える。|
|4.|単体テスト|最低限の品質確保のために、ホワイトボックステストをする。|JUnitなどを実行。単体テストの実装方法などなど|
|5.|パッケージ作成|出来上がったプログラムを配布可能な状態にする。|Jarにまとめる。Jarファイルの単位|

**「こんなのEclipseがあれば余裕っち」** と考える人も多い。個人で使う分にはそうすればいい。（僕なら個人でもやらないが)
複数人での作業を前提とする場合、どれもそれなりに整理が必要。特に2はきちんと考えておかないとまずい。俗人的になりやすいので。。

## Javaのビルドツール

Javaは素晴らしい。開発に必要なツールはあらかた、JDKに入っている。よーし今日から俺もJavaプログラマ！！コンパイルはどうすればいいのかな。。。。

![JavaTools.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/5e44ff3e-608c-1314-b321-e55a91b8ae40.jpeg)


な、ない！！！ 統合開発環境がどこにもない！！！　ん？？？

```
jar: 複数のファイルを1つのJARファイルに結合します。
java: Javaアプリケーションを起動します。
javac: Javaクラスとインタフェースの定義を読み取り、バイト・コードのクラス・ファイルにコンパイルします。
```

あ、コンパイラとパッケージャは同梱しているんですね。。。じゃぁ、見てみましょう。大丈夫。僕は空気が読める男なので、ファイルをいきなりダブルクリックしたりはしません。コマンドプロンプトで。。。

```
> javac --help
使用方法: javac <options> <source files>
…
  --class-path <path>, -classpath <path>, -cp <path>
        ユーザー・クラス・ファイルおよび注釈プロセッサを検索する位置を指定する
…
```

えっと。source filesってことはファイルを一覧化して渡さないといけないんですね。依存ライブラリも一つ残らず、全部class-pathを列挙しなくちゃなんですね。

**千オーダーで存在するファイルを一つも抜けもれなくリストするってどういう罰ゲームですか？**

というわけで、javacコマンドを直接使うのは却下ですね。。。

ちなみにご参考まで。私、Windowsバッチで必至にJavacをループ回してるプロジェクト知っています。。控えめに言って**アタマオカシイ**です。

ほかには、--class-pathを環境変数%CLASSPATH%で解決して、**Jarのファイル名一覧が環境変数の上限を超えたとか。。。** もうなんだろうね。。ちゃんとやり方調べりゃいいのに。

### 第一世代 Makefile

最初期、Javaは言語専用のmakeが用意されていなかった。そこで、先人たちはmakeを流用した。makeの使い方は久しく忘れてしまったので、ここでの詳細や例示は避ける。ただ、個人的には

* 卒論用のシミュレータ(C)
* 卒論本体(Latex)
  
あたりはmakeで書いてたし。Javaも初期はmakeでビルドしていたような気がする。でもまぁやっぱり不便だった。それは主に

* Javaではソースコードがたくさん増えていく。しかもディレクトリも分かれていく
* Javaのライブラリ解決方がめっちゃ面倒くさい。(=CLASSPATHに全部入れる。)
  
に起因していて、結局「大量のファイルを一括して扱うための仕掛け」ってのが
必要というのが当時の感想。

そんな時、僕は出会ってしまうのだ。。運命の蟻に。。。。

### 第二世代 [Apache Ant](https://ant.apache.org/)


> Apache Ant is a Java library and command-line tool whose mission is to drive processes described in build files as targets and extension points dependent upon each other. The main known usage of Ant is the build of Java applications.

もう大好きant!! 登場時期は2000年代の頭だった気がする。それ以来ずっと使っている。なんと2014年の仕事で、antベースのAnsibleもどき作っちゃうぐらいには好き[^1]　何がいいって。

* filesetとmapperが超強力
  * ソースもライブラリも「集合」としてまるめて考えられる。
* Windows(\\)もLinux(/)にも対応しちゃう。疑似PATHがやっぱり強力
* ライブラリへのクラスパス設定がすごく楽
* わりと何でも書けるぐらい条件分岐がかけたりもする。
* 豊富なモジュール

主に、Java開発では肥大しがちな「ファイル」を手軽に、抽象化して扱い、ビルドの再現性を高めるためのツール。**makeの時に感じた課題感にピッタリはまる。。** ハイ。採用。

ことファイルを集合として扱う作業でant以上の強力なツールを僕はしらない。そのため、僕の使うPCには必ずantが入っている。[^2]

#### Antの課題

antは超便利なツールで、革命的だったけど。結局どうやって依存ライブラリを集めるのかとか、ソースコードをどう配置すべきなのかとか、出来上がったライブラリをどうやってバージョニングしながら共有するのか等といった課題には一切答えていない。**というかツールのスコープ外**　この「スコープ」をきっちり守ってそれ以上に広げようとしないのはすごく良いことだと思う。中途半端なもの作るぐらいなら…というね。[^3]

>実はこれ、あまり知られていませんが。この記事。ここからが本編なんですよ。


### 第三世代 [Apache Maven](https://maven.apache.org/)

**ねぇビルドって作る対象によってそんなに変わるもの?**

実は、何も変わらない。だったら。。。「型にハメて考えること減らそうぜ！！」というのが普通の発想。この「標準化」っていくつか利点があって。

* 考えることが減る(もう。考えるのはやめだ！！疲れるから。。。)
* 準備が楽になる。（画一化する）
* 画一化しているので、自動化可能

つまり、**標準化のゴールは自動化**とも言える。逆に**標準化なくして自動化なし**。Mavenが提供するの大きく以下の定義。

|No.|名前|
|--|--|
|1.|標準ビルドライフサイクルの定義|
|2.|標準ディレクトリ構成の定義|
|3.|標準ライブラリ依存解決方法の定義|

Mavenが提供するのはビルドに関する「定義」。定義することで、人は考えるのをやめる。ヒューマンリソースを生産的な方向にむけることができる。


### 標準ビルドライフサイクル（= Phase)の定義

Mavenの「jar」「ejb3」「war」に対するビルドのライフサイクルは以下の通りです。

|NO.|phase |役割|
|--|--|--|
|1|process-resource|パッケージングに備えて、リソースをtargetディレクトリにcopyする
|2|compile|src/main/java配下のソースをコンパイルしてJavaバイトコードに変換します。
|3|process-test-resource|テストに備えて、リソースを所定のディレクトリにコピーする。
|4|test-compile|testコードをコンパイルしてJavaバイトコードを生成する。
|5|test|テストを実行する
|6|package|テスト済みのコードを「jar/war/ear」にパッケージングする
|7|install|ローカルリポジトリに、生成したパッケージファイルを登録する。
|8|deploy|リモートリポジトリに、生成したパッケージファイルを登録する。

これを図で表すと、

![ProjectImages-MavenLifecycle.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/d1640838-355c-56b5-1f80-82d4900c6403.png)


ご覧の通り最初の図に綺麗にMapします。それもそのはずで、Mavenのライフサイクルに慣れてしまった後、私は基本的にこのサイクルで考えるようになったからです。標準化されて使いやすいし、概念としていれておけば他のあらゆる言語に応用が効きます


### 標準ディレクトリ構成の定義
Mavenでは1つのJarファイル=1プロジェクトとして整理する。

|ディレクトリ|役割|
|--|--|
|├─pom.xml |プロジェクト定義ファイル。依存ライブラリの定義や生成するライブラリ名などを定義するファイル|
|│ ||
|└─src |プロジェクトリソース格納ディレクトリ|
|&nbsp;&nbsp;├─main |稼動系リソース格納用。最終的に生成されるライブラリに入るのはここに格納されたリソースのみ|
|&nbsp;&nbsp;│&nbsp;├─java |java ソースファイル|
|&nbsp;&nbsp;│&nbsp;│ ||
|&nbsp;&nbsp;│&nbsp;└─resources |javaソース意外のファイル|
|&nbsp;&nbsp;└─test |テストリソース格納用。テストフェーズで優先的に利用される。|
|&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;├─java |javaのテストソース|
|&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;│ ||
|&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;└─resources |java意外のリソース|


このように定義することで、「ソースを集める先を決める」とか「テスト実行時のクラスパスの順序」への考慮とか面倒くさいだけで本質的ではない検討をすべてぶっとばせる。eclipseの.projectとか.settingsとかいらないものを管理する必要もない。
また、「標準」であることから、手順をいちいち記載する必要がない。10人いても100人いても**Maven標準ディレクトリなんでよろ**と言えばいい。**俺の仕事にはこいつが特に重要でね**

　 **「Maven 標準なんで手順は書きません」**
　 **「分からない人はどうするんですか？」**
　 **「その程度の人を雇っても別のところで苦労するだけです。いりません。」**

こんなやり取りをするわけです。標準を知らないってのはそういうことです。

### 標準ライブラリ依存解決方法の定義

Mavenはライブラリ依存解決の方法を定義した。これが画期的。Maven以前とMaven以降ではライブラリ管理方法に雲泥の差がある。
#### Maven以前のビルド風景
![ProjectImages-従来.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/67a8fd4d-c77e-cafe-918c-e12846100ea1.png)

全てのライブラリは、公開サイトもしくは製品パッケージから作業者が取得し、クラスパスに通す。超俗人的。つまり「秘伝のたれ」。ガッコ親父が30年煮詰めて作った汗と涙と埃の結晶。。本当にこれ食べれるの？？ってやつになる。またこの時ファイル名の変更などをしてしまうと、実際に使っているバージョンが容易にはわからず、環境再現するのが困難となる。ビルドの仕掛けを変えたくなくてファイル名変えるあれな人もいるので混乱する。**「あれ？Version 2ってなってるんだけど、3以後で削除されてるあのメソッドもう削除されてるよ。。。おい。誰だよファイル名変えやがったのはさぁ！！！！」**とか痛い痛い。いや、何度も使うネタで恐縮ですが。「これ、普通にあるんですよ？」

#### Maven以後のビルド風景
![ProjectImages-Maven.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/837176/3faa38d1-757b-3fb8-5da4-2da2c8a2b7a2.png)

Maven以後は、全てのライブラリは、Mavenリポジトリから取得される。Mavenリポジトリは各ライブラリの過去のバージョンも保持しているため、必要に応じて過去のバージョンも利用可能。利用するライブラリは**POM(Project Object Model)**に宣言的に保持される。Mevenはこの POMをもとに、ライブラリを取得し利用する。このためPOMはいわば**Maven Project 設計図**もと言える。
作業者は「利用するライブラリ」「利用したいバージョン」を決定しPOMに記載する。Mavenはこの情報をもとに勝手にビルド時に解決してくれる。これは「宣言的」な運用が可能となることを意味する。**宣言したものが宣言したとおりに出来上がる**。あたり前のように見えるがソフトウェア開発の管理の面ではすごくありがたい。

**助けて。15年前のプログラムをビルドすることができないの。。**

こんな問題もさくっと解決。だって、全てはPOMファイルに書かれているから。ソースコードと一緒に格納して管理するのだから、コードがあれば依存関係が分かる。こんなアホな（そして困ったことに時折ある）状況は消滅する。

なお、このMavenリポジトリは、Javaプロジェクトのビルドのデファクトスタンダートとなっている。GradleなどJavaベースの言語用のビルドツールの他、ScaleのためのSBT等、Javaベースの派生言語におけるビルドツールのライブラリリポジトリとしても利用されている。

### 第四世代 Gradle

前述のようにMavenはPomに記載された内容を、Mavenのライフサイクルで実行する。つまり基本的に作業可能なのは

```
mvn clena package deploy
```

だけとなる。たとえば、あるファイルが存在するか否か、現在の曜日などをもとに処理を分けたい場合とかかゆいところに手は届かない。そこでMavenのもつ強力な依存解決を利用しつつ、条件分岐等の記載の自由を獲得するために開発されたのがgradleである。つまり登場コンセプトは

**親の敷いたレールになんて乗るのは嫌だ！俺は、自由に生きるんだ**

なんですけど、自由って言ってもあれですよね。。やることビルドですよ？？同じですやん。

まぁ、親の敷いたレールから逃れるため、GradleはDSLとしてgroovy（今はkotlin)を採用し、Javaおよびその派生ライブラリを使ってなんでもできるいい子ではある。

プログラムの様に細かくいろいろとかける代わりに、事前の設計項目が多く、スピーディにPJを立ち上げたい場合等には向かない。大規模PJでは使いにくい。でもまぁ慣れれば何でもありだけどさ。

## じゃぁ、Gradle使っておけば正解？
時と場合による。Gradelのもつ強力なキャッシュ機能とか、より洗練された推移的依存解決やその他もろもろが欲しいのなら、Gradleかな。一方で、やること既に決まっていて、ビルド自体あまり考えたくないなら、脳死でいけるmavenがいいかと思う。僕は

**自由がないこと、制限、制約があることの素晴らしさ**

を享受するために、mavenを好んで使う。

あのさ、なんでも自由ならいいってもんじゃないのよ？300人参加する開発でさ。[^4]全員が自由にやってごらんなさいよ。すごいことなるから。あとさ、お前ら「自由が欲しい、やりたいことができない」って言うくせに、自由にどうぞっていうと「手順がない、やり方を教えろ」っていうじゃん。束縛大好きじゃん。結局自由を活用できないなら、束縛大好きッ子として、制限制約のある気楽さを選ぶのがいいんだよ。。ほんと。。

あ、なんか愚痴になっちゃった。。。

## IDEとビルドツール
序盤でもそっとふれたけど、**「IDEあればビルドツールいらないじゃん。俺、InteliJだしって。」って思うでしょ？** でもね、IDEだってコンパイルはする。コンパイルつまりビルドのためには、ビルドを実行する仕掛けが必要。わざわざ専用のツールを作るのは無駄なので、IDEツールではmaven/gradleを内部的に呼び出していたりもする。依存解決も楽だしね。つまり、知らないうちに使っているのです。ほぼ誰でも。

以下は一例である

|No.|IDE|デフォルトのビルドツール|他に利用できるツール|
|--|--|--|--|
|1|Eclipse|独自（すまんうろ覚え）|Maven/Ant|
|2|NetBeans|Maven|ANT|
|3|InteliJ|Gradle|-|
|4|AndroidStudio|Gradle|InteliJ派生だしね。|

参考まで、Gradleとか書きたかったら、InteliJが最高！でも僕は、Maven大好きっこなので、NetBeansが大好きです

## まとめ
- Antは便利だけど、いちいち全部をかかなくちゃいけないから不便だよ。
- Mavenはディレクトリ構成、ライフサイクルを標準化することで、作業効率と再現性を向上させているよ
- *Maven以後のJavaビルドツールはMavenリポジトリを利用可能だよ





[^1]: 当時Ansibleを知らなかった。後から知って、「しまった！！」と思ったのは言うまでもない。
[^2]: この点については別の記事を起こして語ろうと思う
[^3]: 依存性解決については[Apache Ivy](https://ant.apache.org/ivy/)で解決済み。連携プロジェクトとして独立しているのがよい。
[^4]: リアル「世紀末救世主伝説」を再現したいなら、止めはしない。