概要
この記事はG* Advent Calendar12日目の記事です。
少し前にJavaツール勉強会というものを福岡で行い、そのときに下記の資料を作ってSlideShareに公開しました。
勉強会用の資料ということもあり、あまり外向けで書いたものではなかったのですがありがたいことに40以上のはてブがついていました。
そこで本記事ではもう少し説明を加えながら改めてGradleの紹介をしたいと思います。
スライドを読んだ事ある方は新しい情報はないのであまり面白くないかもしれません。
同じようなタイトルの記事はQiita内外でいくつもあり何番煎じだよって感じですが比較的新しい内容にしているつもりなのでお許しください。
Gradleとは?
説明するまでもないことでしょうがJava言語用のビルドツールです。
JavaビルドツールとしてはGradle以外にもAnt、Mavenといったものがあります。
その中でも後発に当たるもので最近はやっている感じです。
人気が出た一因としてAndroid Studioで実質デフォルトになっているためだと思われます。
特徴としましてはAnt、MavenのようにXMLではなく独自のDSLでタスクを記述します。
GradleはGroovyで実装されておりこのDSLはGroovyスクリプトとして書くことができます。
Javaをベースとした言語なのでJavaエンジニアが比較的容易にかくことができます。(とは言ってもGroovyの作法を知らないとどういった構造になっているかわかりずらいでしょうが)
Java以外にもGroovy、ScalaなどJVM言語のビルドも定義でき、更には正式なサポートではなく実験的機能な扱いですがC/C++等のビルドも定義できます。
現在バージョン2.9でもう少しで3.0が出るとの話です。
3.0で多少文法が変わるそうです。
余談ですが最近ロゴが変わり象さんになりました。表記も全部小文字のgradleからGが大文字になってGradleになっています。
他のツールとの違い
上記にも書いたことですが大きな違いとしてはXMLではなくDSLです。
そのため設定ではなく処理として記述することができます。
またタグで囲うなど冗長なものがないので見てわかりやすいです。
そして書きやすくもあるため拡張性が高くなっています。
Gradleでプロジェクトを作成するときGradleWrapperというものが作成されます。これを使用するとGradleをインストールしていない環境でもGradleを使うことができます。
使用するGradleのバージョンも統一されることになるのでCIツールと組み合わせるときにとても便利です。
Gradleを使ってみる
導入方法
Gradleをインストールするには他の多くのツールと同じように下記DLページより一式DLしてパス等を設定することで使い始めることもできます。
http://gradle.org/gradle-download/
しかしこの方法より次の方法をお勧めします
SDKMANによる導入
Unix系のOSを使っている方はこちらを使いましょう。(私はWindowsですがCygwinに入れています)
SDKMANというものは最近までGVMと呼ばれていたツールで、Groovy関係のツールのバージョン管理ツールでした。(Rubyで言うところのRVMやrbenvのようなものです)
Go言語のバージョンマネージャと名前がかぶっていたせいかSDKMANに名前が変わりました。
名称変更後からGroovyに限らずJVM系のさまざまなツール(MavenやSpringBoot等)を導入、バージョン管理することができるようになりました。
詳しくは以前下記記事にて説明したのでよければ一緒に見てください。
JavaユーザーのためのパッケージマネージャーSDKMAN
導入法方法はシェル上で次のコマンドを打つだけです。
curl -s get.sdkman.io | bash
いろいろな文字が出力されると思いますがすべて動作するまで待ちます。
そしていったんシェルを閉じて新しいシェルを開くと導入が終わっています。
sdk --version
正常にインストールされている場合上記を入力するとSDKMANのバージョンが出力されます。
問題なければGradleのインストールを行います。
sdk install gradle
バージョン指定したい場合は最後にバージョン番号まで付加します。
たったこれだけでGradleの導入が終わります。
新しいバージョンが出た際にもすぐにリストに出てくるのでバージョン更新がとても楽になります。
Javaプロジェクトを作ってみる
Gradleで新規プロジェクトを作成するには次のようにコマンドを実行します
mkdir project-name
cd project-name
gradle init
以下のようにファイルが配置されます。
project-name
├── build.gradle
├── gradle
│ │
│ └── wrapper
│ │
│ ├── gradle-wrapper.jar
│ │
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
このうちwrapperディレクトリとgradlewはGradleWrapper用のものでとりあえず気にしなくてもかまいません。
settings.gradleは主にマルチプロジェクトのときプロジェクト攻勢を記述するのに使います。
一番重要なのはbuild.gradleでここにタスクや依存関係などを書くことになります。
Javaプロジェクトの場合下記のように記述します。
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.7'
testCompile 'junit:junit:4.11'
}
apply plugin java
ここでプラグインを読み込みます。
今回はJavaプロジェクトなのでJavaプラグインを使用します。
その他apply plugin: idea
とかくとIntelliJ用のプロジェクトファイルを自動生成してくれたりします。
必要に応じてプラグインを追加しましょう。
pluginの後にコロンを入れるのを忘れないように!
repositories
ここでは使用するリポジトリを記述します。
今回はJCenterというリポジトリを使用しています。これはGradleの標準的なリポジトリで基本的にMavenCentralと同じものが入っておりさらにJCenterにだけ登録されているものも使用できます。
もちろんmavenCentral()とすれば従来どおりMaven Central Repositoriesも使用できます。
またURLを指定することで独自のリポジトリを追加することももちろんできます。
dependencies
ここでコンパイル時テスト時などに必要なライブラリを記述します。
今回の例ではビルド時にSLF4J、テストフレームワークにJUit4をしています。
ソースディレクトリ
ソースコードはGradleプロジェクトのルート以下に次のような階層で配置します。
project-name
└── src
├── main
│ └── java
│ └── packagename
└── test
└── java
└── packagename
Gradleを実行する
Gradleでビルドを実行するには次のようにコマンドを実行します。
gradle build
正常にビルドされると「BUILD SUCCESSFUL」と表示されます。(失敗したときは「BUILD FAILED」)
テストのみ実行したいときは
gradle test
と実行します。
基本的な使い方はこのような形です。
Lazybonesを使ったプロジェクトのテンプレート化
上記で書いたとおりGradleでプロジェクトを作り始めるには何ステップか手作業が必要です。
とはいってもIntelliJとか使うのでしたら標準のGradle Javaプロジェクトでほとんど児童で作ってくれますが・・・・・・
しかしいつでもIDEが使えるとは限らないですし、Javaとしての最低限の構成ではなく社内などで決まったフォーマットがあるときわざわざ毎回作るのはだるいですよね。
それを提言できるツールとしてLazybonesというものがあります。
これに関しても以前記事を書いていますので詳細はそちらで
Gradleプロジェクト作るときLazybones使うと便利
これを使うと一発でディレクトリ構成や最低限のbuildスクリプトが記述されたプロジェクトが出来上がります。
導入はSDKMANで```sdk install lazybones'''と実行します。(ね?SDKMAN便利でしょ?)
Javaプロジェクトの場合
lazybones create java-basic project-name
と実行すればソースディレクトリや記述済みのbuild.gradleが出来上がります。
私はまだ試したことがないので今回は説明できませんが、自分で作った構成をテンプレートとして登録することもできるので社内リポジトリやいつも使うライブラリをあらかじめ書いておくといいでしょう。
タスクを定義する
よくGradleの入門記事で次のような例を見ます。
task hello<< {
println 'hello world'
}
build.gradleの中にこのタスクを記述してgradle hello
と実行すると「hello world」が実行されます。
Gradleでスクリプトが実行できるということを示す点ではいいんですけど、正直これができて何がうれしいの?って感じですよね。
そこで今回はもう少し高度なタスクを記述してGradleの強力さをアピールすることにします。
まずは一応helloタスクの説明
上記タスクはよく見るものですが、ちょっとわかり図ライトころがあるので一応説明します。
taskを定義するとき、シフト演算子みたいな「<<」があります。
これはgroovyでは演算子オーバーロードがサポートされており、まさにシフト演算子をオーバーロードしたものになります。
taskでこの演算子を使用したときdoLastというメソッドが呼び出されることになります。
つまり次のように書いたのと同等です。
task hello {
doLast() {
println 'hello world'
}
}
そしてdoLastメソッドはそのタスクの一番最後に実行されます。
今回helloタスクはそのほかに何も定義されていないのでgradle hello
が実行されるとdoLastだけが実行され「hello world」が出力されるというわけです。
ちなみにこのprintの仕方はGroovy独特のもので、System.outを省略できるのでいきなりprintlnメソッドが現れ、さらに括弧を省略できる、セミコロンを省略できるといった機能もあるのでこんなに短い書き方になっています。もちろんJavaっぽくSystem.out.println("hello world");とかいても動きます。
ここら辺の汎用性、わかりやすさはGroovyならではとなっています。
Groovy・Gradleに慣れていないうちはJavaとして書いて問題ありません。ほとんどのソースはそのまま動作します。(いくつか解釈の違うところもあります)
書いていくうちにどんどん怠けられるのでGroovyらしいソースになることでしょう。
高度なタスクを書いてみる
先に述べたようにもっと実践的なタスクを定義してみます。
今回はコマンドとして実行可能なJarを作成します。
前提
まずはタスクを作る前提として「コマンド実行可能なJar」とは何ぞ矢という話からします。
一般的にJarで固められた成果物を実行するときは次のようなコマンドを実行すると思います。
java -jar executable.jar
このjava -jarコマンドですが、結構優秀でして読み込むJarファイルの先頭にごみデータがあっても自動的に無視してくれて良しなに実行してくれます。
そのため下記のようなshellファイルがあるとします。
#!/bin/sh
java -jar "$0" "$@"
exit $?
自身のファイル名をjarファイル名として、受けた引数をそのままjavaコマンドの引数として渡すだけのスクリプトです。
これをbootstrap.shとした場合次のコマンドを実行します。
cat bootstrap.sh executable.jar > executable
すると「executable」というファイル名が出来上がります。
catして固めているのでファイルの先頭にbootstrap.shの内容、その次にJarの内容がそのまま書き込まれています。
これを実行するとあたかも普通のコマンドのように(Javaを意識せずに)実行することができます
/usr/local/bin/以下にでも入れておけば本当に何も意識することがなくなります。
参考
実行可能 jar をコマンドっぽく実行するために(java -jar 使いたくない)
スクリプトで定義する
さて、長々と前提を説明しましたが要はこれをGradleタスクで作ろうということです。
これを作るだけでしたら別にシェルスクリプトを組めばそう難しいことではありませんが、せっかくJavaというマルチプラットフォームなものを使っているのに環境依存してしまったり、またビルドと別に作業が必要となってしまいます。
というわけで全部まとめちゃいましょう。
コードは以前Gistのほうに挙げたのですが、こちらのほうにも転載します。
apply plugin: 'java'
apply plugin: 'application'
// プロジェクト設定
archivesBaseName = 'projectName'
group = 'packagename'
mainClassName = 'packagename.MainClass'
/*
依存Jarを全部突っ込んだJarファイルを
作成してシェルスクリプトをくっつけるタスク
*/
task jarsh(type: Jar, dependsOn: jar) {
baseName = "${project.name}-fullpack" //作成するJarファイルの名前
//マニフェストに記載する内容を指定
manifest {
attributes 'Main-Class': mainClassName //メインクラスを指定
}
//依存ライブラリをすべて固める
from configurations.compile.collect{ it.isDirectory()? it : zipTree(it) }
from "$buildDir/classes/main"
from "$buildDir/resources/main"
//タスクの最後に実行
doLast {
def jarshDir = new File("${buildDir}/jarsh")
jarshDir.mkdir()
def shell = new File("${jarshDir}/${archivesBaseName}")
def bat = new File("${jarshDir}/${archivesBaseName}.bat")
def jar = new File("${buildDir}/libs/${baseName}-${version}.jar")
//Unixシェルのブートストラップ
shell.text = """\
#!/bin/sh
java -jar "\$0" "\$@"
exit \$?
"""
//Jarをくっつける
shell << jar.bytes
//Windowsバッチのブートストラップ
bat.text = """\
@echo off
java -jar "%0" %*\r\n\
exit /B 1\r\n\
"""
//Jarをくっつける
bat << jar.bytes
}
}
jarshというタスク名です。依存するタスクとしてjarタスクを指定しており、このタスクを実行するとき先にjarタスクが走ります。
最終的にJarのようなものを出力するためtypeをjarにしています。
行っていることはまず依存するjarを全部パックしたfatjarを作成します。そしてdoLast部分でfatjarとbootstrap部分をくっつけて./build/jarsh/以下に吐き出しています。
Unixで動くものとWindowsのbatchで動くもの両方が出力されます。
一見複雑なように思われるかもしれませんが、最初にディレクトリの定義などがあるほか半分はfatjarを作るための処理で実質処理を行っている部分はわずか数行です。
そしてFileクラスににもシフト演算子のオーバーロードがされているので結合するところは直観的になっています。(jarをくっつけるとコメントしているところです)
このように他のビルドツールと比べ自由にタスクを記述できることを確認していただけたかと思います。
最後に
長々とした記事にお付き合いいただきありがとうございます。
スライドのほうにも書きましたが、私自身Gradle以外のビルドツールを使ったことがなく、Ant・Mavenとの比較などあまりできないため「○○なところが優れている」などあまり言うことができません。
ただもともとGroovy使いやっていたためとても便利に扱うことができ、もうGradleのない世界に戻ることなんてできません。
見ていただいたとおりJava風に処理を書くことができるので、Groovist以外の人たちにももっと流行ってほしいなと思います。
明日(13日)も引き続き私が記事を書きます。
次は今回みたいにまじめではなくネタ記事を書く予定です。