最近 Gradle を触っています。(遅っ!)
新しいものに飛びつかない私としては 2 系がリリースされたこともあるし、一部の界隈で盛り上がってきているようなので。昔々一部では「愚 Ruby」なんて揶揄されていたこともある Groovy でしたが。
Ant, Maven, そして Gradle へ
Ant はビルドツールのデファクトがなかった Java の世界で急激に普及しましたが、
- ビルド職人にしか読めない build.xml が横行
- ライブラリーの依存関係はライブラリアンが支配
- プロジェクト毎に異なるディレクトリー構成
といった問題や課題を残しました。そこに Maven がやってきて「規約」という概念を持ち込み、これらの問題を解決してくれました。しかしその先には、pom 地獄と XML の限界が待ち受けていました。
Maven は、自身が持つ強い規約を XML とプラグインだけで解決しようとしているので、ちょっとだけ手を加えるということが非常に苦手です。そしてプラグイン開発の敷居の高さもあって、ほとんどの場合は「Maven で出来ることをやる」と言ったビルドツール支配型になりました。
そこで Gradle が登場しました。Gradle は Maven がもたらした規約を尊重しつつも、XML ではなくスクリプトを導入したことでビルド周辺の諸問題を解決しようとしています。
何はともあれ Java プロジェクトを作ってビルド
インストールや設定はググれば出てくるので割愛。
とりあえず Java プロジェクト作るには以下のコマンドを実行するだけでオケー。Maven と大差ないですね。
$gradle init --type java-library
出力はこんな感じ。src 以下は Maven の規約が適用されています。Maven の良い所はきちんと継承されています。
<ROOT>
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
│ └── java
│ └── Library.java
└── test
└── java
└── LibraryTest.java
Maven で言う pom.xml の代替になる build.gradle はこんな感じ。Maven を知っていれば雰囲気で読めますが、これは設定ではなく Groovy スクリプト。Groovy は多くの省略記法やシンタックスシュガーを持っているので、スクリプトでありながら設定のように書けるところが魅力。
Java で同じことをしようとすると記述量も多いし、プログラム感満載...
// Java っぽく書くと
// Map<String, String> arg = new HashMap<>();
// arg.put("plugin", "java");
// apply(arg);
apply plugin: 'java'
// Java っぽく書くと
// repositories(new Delegate() { public void jcenter() {...} });
// void repositories(Delegate delegate) {
// delegate.jcener();
// }
repositories {
jcenter()
}
dependencies {
// compile('org.slf4j:slf4j-api:1.7.7') と同等
// groupId が org.slf4f で artifactId が slf4j-api、version が 1.7.7
// であることを表している
compile 'org.slf4j:slf4j-api:1.7.7'
testCompile 'junit:junit:4.11'
}
ビルドします。成功すると ./build/libs に jar が出力されます。
gradle build
~ snip ~
BUILD SUCCESSFUL
Task を作る
Gradle は Groovy で書かれた Task を実行する機能を持っており、Task の実行順序は gradle コマンドの引数順で制御されるので、Maven より柔軟にビルドが制御できます。
build.gradle に Task を追加して実行。
task helloGradle << {
println "--- Hello Gradle. ---"
}
gradle helloGradle build
:helloGradle
--- Hello Gradle. ---
:compileJava UP-TO-DATE
~ snip ~
BUILD SUCCESSFUL
gradle build helloGradle
:compileJava UP-TO-DATE
~ snip ~
:helloGradle
--- Hello Gradle. ---
BUILD SUCCESSFUL
引数順に実行されました。直感的でイイ。
同じことを Plug-in で
build.gradle に直接 task を定義するのは再利用性に欠けるので Plug-in で。(今回は簡便にするために build.gradle で Plug-in を実装していますが、良い子のみなさんはマネしてはいけません。)
e {
name = 'Gradle'
message = 'Hello'
}
class HelloPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.extensions.create("e", HelloPluginExtension)
project.task('helloPlugin') << {
println "--- ${project.e.message} ${project.e.name} ---"
}
}
}
class HelloPluginExtension {
String name;
String message;
}
まとめ
Task を作ったり Plug-in を作ったりしていろんなことが出来るので便利〜と思う反面、Ant 時代の闇に陥る可能性も秘めている気がしました。結局、良いビルドであることは、良いコードであることと同じなんですね。
そして、一回 Grandle の世界に足を踏み入れたら二度と Ant や Maven に戻ることはない気もしました。どっかのプロジェクトに入れたいなぁ。