MavenでGroovyをコンパイルして、Eclipse のm2eとも統合(2016年版)

なんか最近はみんなGradle使ってるんですかね？なんか時代から置き去りにされた感じもありますが、2016年の今、MavenでGroovyとJavaを同時にコンパイルできて、しかもEclipseからもm2eでソースフォルダ自動認識 & lifecycle mapping 不要でコンパイルできちゃう、という、すごい地味な話です。

以下本記事での動作環境などです。

```
$ mvn -version
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T01:41:47+09:00)
Maven home: /home/msakamoto/devtools/apache-maven-3.3.9
Java version: 1.8.0_77, vendor: Oracle Corporation
Java home: /opt/jdk1.8.0_77/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-327.13.1.el7.x86_64", arch: "amd64", family: "unix"

Eclipse Mars.2 (4.5.2)
（導入プラグインなどは後述）
```

Eclipse Mars.2 (4.5.2) で本記事でいろいろ実験するときに導入したプラグイン：
![20160409_eclipse_groovy_plugins.png](https://qiita-image-store.s3.amazonaws.com/0/18618/2c40fff6-2c7f-8bc3-1179-ef0728be507c.png)

# Groovy-Eclipse plugin をインストールするときの注意点

**Eclipse Marketplaceからのインストールはしないでください。 まず公式Wikiを確認し、手動でアップデートサイトを登録してそこからインストールしてください。（要するに Help -> Install New Software...経由が正解）**

https://github.com/groovy/groovy-eclipse/wiki

**Eclipse MarketplaceからだとJunoやIndigo対応バージョンしか表示されませんが、公式WikiからならちゃんとそれぞれのEclipseバージョンに対応したアップデートサイトのURLが載ってます。**

# サンプルコード

基本的なバリエーションは大体以下で網羅できてるはずです。

- https://github.com/msakamoto-sf/maven-java-groovy-conjunction-demo1
    - Groovyだけ、Groovy + Java (Groovy -> Java), Groovy + Java (Groovy <> Java) の3パターンで、antrun, GMaven(1.4時代), Groovy Eclipse Maven plugin の3種類の 3x3 パターンのMavenプロジェクトのデモです。
- https://github.com/msakamoto-sf/maven3-junit-spock-testng-mixin
    - Groovy Eclipse Maven plugin の方法だけですが、Mavenで JUnit(Java, Groovy) + TestNG(Java, Groovy) + Spock (Groovy) を全部コンパイルしてテストするという地味なサンプルです。
    - Eclipse 4.5.2 + m2e + Groovy Eclipse + TestNG Plugin で動作確認できてるのが唯一ほめてほしいところです。

# Maven で Java と Groovy をコンパイルする

何種類かあります。ただ厄介なのが「JavaとGroovy」という部分で、どっちがどっちを参照してるかの依存性で注意点があったりします。

* Groovy ソースから Javaソースのクラスを参照してる。
    * これは楽です。基本的にJavaのクラスをコンパイルすれば、そのままGroovyのコンパイラがclasspathから参照できるはずなので、後述のどのパターンも問題ありません。
* Java ソースから Groovy ソースのクラスを参照してる。
    * これが問題で、最初にGroovyソースをコンパイルしないとJavaソースのコンパイルが出来ません。解決策としては、デフォルトのJavaコンパイラは使わず、.javaも.groovyも全部まとめてGroovyコンパイラに処理してもらう方法があります。

また、ソースフォルダの構成ですが、大体以下がググるとよく見つかり、ツールも大体対応してますし、人間にもわかりやすいと思います。

```
src/main/java : Javaソース
src/main/groovy : Groovyソース
src/test/java : Javaテストソース
src/test/groovy : Groovyテストソース
```

以下、サンプルコード作ってみて、Eclipse 4.5.2 とかでも読み込ませてみた感想というかもう、結論です。

- GMavenはもうコンパイラ機能はサポートしてないので、使えない。
    - http://groovy.github.io/gmaven/ に "No Compilation Support" と注意書きが出てます。1.4時代は出来たのですが、やっぱり難易度高かったみたいです。
    - Guillaume Laforge さんもGMavenじゃなくて後述の Groovy Eclipse の方を推してます。
        - http://glaforge.appspot.com/article/building-your-groovy-2-0-projects-with-maven
- antrun は今でも有効。多分原始的だからだと思います。
    - JavaとGroovyが相互参照してる場合も、MavenだけならGroovyコンパイラに両方同時にコンパイルさせることで解決できます。(Eclipseだとダメかも。Java <> Groovy 相互参照をantrunでビルドするパターンは、Eclipseでは動作確認してないです。)
    - ただしEclipseにインポートすると、m2e のlifecycle mapping が無いので怒られます。あと、ソースフォルダの設定も自分でやらないといけません。lifecycle mapping と antrun の問題についてはググればたくさん類似問題で悲鳴を上げてなんとか解決した人たちがいますのでググります。
- 2016年4月時点で一番の安定解が、Groovy Eclipse Maven plugin を使う方法だと思います。
    - https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-Maven-plugin
    - Groovyだけ、Java + Groovy (Groovy -> Java), Java + Groovy (Java <> Groovy 相互参照) 全部のパターンで、全く同じpom.xmlの書き方でカバー出来たのはこれだけです。
    - しかも Groovy Eclipse のEclipseプラグイン導入すれば、Mavenプロジェクトとしてインポートすればソースフォルダ自動認識 & lifecycle mapping に悩まされないという最強タッグが組めます。
    - 注意点として、想定Groovyソースフォルダが `src/main/java`, `src/test/java` な点です。 `src/main/groovy`, `src/test/groovy` にも対応してますが、それらを見てくれるのは `src/main/java`, `src/test/java` にダミーファイルがある場合のみです。要するにGroovyだけの、`src/main/groovy`, `src/test/groovy` だけのプロジェクトだと全スルーされるので、対処法は 上記公式Wikiを読んでください。

# Eclipse で Java と Groovy が混在したMavenプロジェクトをインポートする

上でも若干言及してしまいましたが、EclipseでJavaとGroovyが混在したMavenプロジェクトをインポートする時のポイントとして以下があります。

- Groovyエディタに何を使うか？
    - Grailsまで使うならGGTS一択だと思いますが、Grails使うわけじゃなくてちょっとSpock書きたい位なら Groovy-Eclipse pluginで問題ないと思います。
    - https://github.com/groovy/groovy-eclipse/wiki
    - 同じ所で Groovy Eclipse Maven Pluginも出してますので、Eclipseコンパイラが関わる点に深く突っ込まなければ、この組み合わせがベストでしょう。
- m2eのlifecycle mapping 問題はどうなるか？
    - https://www.eclipse.org/m2e/documentation/m2e-execution-not-covered.html
    - antrunだと必須です。まだantrunのlifecycleはデフォルトではサポートしてくれてないっぽい。
    - Groovy Eclipse Maven Plugin を使ったpom.xml + Groovy-Eclipse plugin導入済みEclipseならこの問題は解消されてる感じなので、やっぱりこの組み合わせがベストですね。

結局、Grails使わないのであればエディタとしては Groovy-Eclipse Plugin一択。pom.xml側も Groovy Eclipse Maven Plugin使うのが一番楽チンですが、antrunをどうしても使いたい(「なんでMavenからコンパイルするのにEclipseが入ってくるんや！？んなプラグインで生成されたクラスファイルなんざ信用できるか！ワイはantrunで、何が動くのかびっちり明示して作ったほうが安心できるんや！」)場合は、antrunでのlifecycle mapping の問題は他でも発生してるのでググれば解決しますので、それでもいけると思います。

なお、以下のプロジェクトはGroovy Eclipse Maven Plugin使ったMavenプロジェクトですが、 Groovy-Eclipse plugin導入したEclipse Mars.2(4.5.2) で Maven プロジェクトとしてそのまま読み込め、groovyのソースフォルダも自動で認識してくれました。

https://github.com/msakamoto-sf/maven3-junit-spock-testng-mixin

# antrun使ってGroovyコンパイルするMavenプロジェクトをEclipseにインポートするときのハマりどころ

サンプルコードまでは用意できなかったのですが、手元で試してみた時のハマりどころです。

## インポートするとJavaプロジェクトになるが、その状態だと Groovy-Eclipse plugin のGroovyのエディタが、コードの自動補完で例外を上げまくってしまう。

**解決策：**

* `Package Explorer` などからプロジェクト右クリックして `Configure` -> `Convert to Groovy Project` を選ぶ。
* これにより Groovy Nature が `.project` に組み込まれ、Groovy エディタが正常に動くようになります。
* 「Groovyプロジェクトに変換したら、Javaはどうなるの・・・？」ですが心配要りません。Java用の機能もちゃんと残ります。

## Groovyソースを右クリックして `Run` や `Debug` しようとすると `groovy.lang.GroovyRuntimeException: Conflicting module versions. Module [groovy-all is loaded in version (majorA).(minorA).(relA) and you are trying to load version (majorB).(minorB).(relB)` が発生し、クラスファイルのロードに失敗してしまう。

**原因（推測含む）：**

* `Convert to Groovy Project` すると、自動でGroovyのライブラリとDSLサポートが `Java Build Path` の `Libraries` に追加されるのですが、このバージョンが Groovy-Eclipse plugin が現在 `Window` -> `Preferences` -> `Groovy` -> `Compiler` -> `Groovy compiler settings:` で選択してる Groovy のバージョンになってしまいます。
* つまり、 Groovy-Eclipse plugin 側のGroovyのバージョンと pom.xml で指定しているMaven 依存ライブラリのGroovyバージョンが異なることが、どこかでこの例外の原因となっています。
* 以下の組み合わせでこの例外の発生を確認してます。
    * antrunのMavenプロジェクト + pom.xml では groovy-all:2.3.11 <> Groovy-Eclipse plugin 側は 2.3.7 の compiler
    * Groovy Eclipse Maven Plugin のMavenプロジェクト + pom.xml では groovy-all:2.4.6 <> Groovy-Eclipse plugin 側は 2.4.3 の compiler
        * （後述の通り Groovy Eclipse Maven Plugin のMavenプロジェクトをインポートすると Groovy-Eclipse plugin のビルドパスは追加されないが、プロジェクト右クリック -> `Groovy` -> `Add Groovy libraries from classpath` で後から追加してみたところ発生）

**解決策：**

Groovy-Eclipse plugin の `Groovy compiler settings:` で選べるコンパイラバージョンは、Groovy-Eclipse plugin のお仕着せになってしまい、自由に選択できません。
そのため、Groovy-Eclipse plugin と pom.xml のバージョンを一致させる方式は採れませんので、代わりに、Groovy-Eclipse plugin が追加した余計なビルドパスを除去することで解決します。

* プロジェクトを右クリックし、`Groovy` -> `Remove Groovy libraries from classpath` を選びます。これにより Groovy-Eclipse plugin が追加したGroovyライブラリが取り除かれます。
* Groovyライブラリ自体は、pom.xmlで指定していれば `Maven Dependencies` の方に存在しますので、以降、そちらだけが使われ、`Run` や `Debug` も正常に動作するようになりました。

補足：

* Groovy Eclipse Maven Plugin を使ったMavenプロジェクトをインポートした場合は、以下のようになったためか、このトラブルには遭遇しませんでした。
    * 自動でGroovyプロジェクトとしてインポートされる。
    * Groovy-Eclipse plugin の Groovy ライブラリがビルドパスに追加されない。`Maven Dpendencies` のGroovyライブラリが有効になる。

# 注意点

Java開発もいろんなタイプがありますので、Groovyを導入してみたい全てのパターンで、その時その時のJava/Groovy/Maven/Eclipseおよび必要なManven plugin/Eclipse plugin のあらゆる組み合わせを事前に検証するのは不可能です。

本記事で参照してる拙作のサンプルコードでも、非常に単純なJUnitテストケースの実行しか試してません。しかも典型的なwarパッケージングのみです。

本格的なJavaEE開発でEJBが絡んできた時どうなるか、Springと組み合わせた時は？DIコンテナやモダンなフレームワークではどうなるか？など、実際の開発ではおそらく無数のハマりどころと地雷が広がってると思いますので、皆様の健闘を祈ります。

こういう地味な部分を、安定して提供するのって実は結構大変で、それぞれのプロジェクトで大抵はIDEにプロジェクトインポートするのって最初の一度くらいなので、何かメンバーがトラブっても「あ、これってこうすればOKだよ～」と口頭でサポートしてそれで解決しておしまいになりやすい部分です。さらに、迂闊に新しもの好きが最新のEclipse使ってみたりすると、プラグインのインストールはできるけれどインストールの仕方が間違っててトラブったり、そもそもプラグインがまだ最新のEclipseで安定してないとか未対応だったりと、ものすごい地雷原が広がってて、正直一個前のバージョンで動かすくらいが一番安全だったりします。

今回紹介した Groovy-Eclipse pluginにしても、Eclipse Mars.2 (4.5.2) の標準のEclipse Marketからのインストールで、一応検索すればJuno対応バージョンが出てくるんですよ。でもそこからインストールするのは実は不正解で、正解はちゃんと公式ページ読んで、手動でアップデートサイトのURL登録してそこからインストールしないとダメなんです。

そういう、地味なところをちまちまと地雷除去してる全てのエンジニアに祝福を。


# 参考

- GMaven 公式サイト
    - http://groovy.github.io/gmaven/
- Groovy Eclipse plugin 公式Wiki
    - https://github.com/groovy/groovy-eclipse/wiki
- Groovy Eclipse Maven plugin 公式Wiki
    - https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-Maven-plugin