多分わかりやすいGradle入門

こんにちは。サイオステクノロジー技術部 武井です。今回は、ビルドツールのGradleについて、書きたいと思います。

Gradleとは?

Gradleは、オープンソースのビルドシステムです。似たようなツールにMavenというものがあり、こちらが現在ではデファクトスタンダードですが、Gradleの人気もジワジワ上がりつつあります。で、Gradleが何をしてくれるかというと、例えば、Javaのjarファイルやwarファイルといったものを作ってくれます。

では、Mavenと何が違うのでしょうか?Mavenはビルドの手順を定義するための言語として、XMLを用います。しかし、XMLでは、ぱっと見ただけでは、どのような処理をしているかなかなかわかりにくいです。

しかしながら、GradleはGroovyという言語によってビルドの手順を定義します。つまり、Gradleではビルドの手順を設定ではなく、Groovyという言語によって処理として記述します。Gradleの設定ファイルはDSLなのです。GroovyはJavaと互換性のある言語で、Javaを知っている人であればスラスラと書けるのではないかと思います。

そんなGradleですが、以下のデメリットもあるというのが個人的所感であります。

  • Groovyは、シンタックスシュガー(省略した書き方)が多くわかりにくい。
  • そもそもGroovyを知らないと理解できない

同じようなことを思っていらっしゃる方のため、本記事では、そのあたりの不便さも含めて、Gradleをわかりやすく解説したいと思います。

インストール方法

Gradleは、sdkmanというパッケージマネージャーを使ってインストールします。まず、そのsdkmanをインストールします。

上記のように表示されればOKです。上記の/path/toは、コマンドを実行したユーザーのホームディレクトリと読み替えてください。

Gradleをインストールします。

以上で、Gradleのインストールは完了です。

まず使ってみる

手っ取り早く、Gradleで、ハロワのJavaアプリケーションをビルドするものを作ってみます。

Javaのプロジェクトをビルドするための、テンプレートを作成します。

gradle initはテンプレートを作成するためのコマンドで、それに引き続いて、–typeのあとに、アプリケーションのタイプを指定します。今回はJavaなので、java-applicationとしましたが、例えば、Scalaの場合は、scala-libraryと指定します。

上記のコマンドを実行すると、以下のファイル及びディレクトリが作成されます。

重要なものが2つあります。

一つはbuild.gradleです。これは、ビルドの手順を定義するファイルで、Groovyで記述します。Mavenでいうところのpom.xmlのようなもので、ビルド対象やビルドしたものの出力先など、ビルドの動作に関する全てを司る非常に重要なファイルです。

もう一つは、srcディレクトリです。ここにビルド対象のソースコードを格納します。このディレクトリ構成、どこかに見覚えのある方もいらっしゃるかもしれませんが、Mavenのソースディレクトリ構成と全く同じです。ここのところ、GradleはビルドツールのデファクトスタンダードであるMavenとの互換性を意識していると言えます。

src/main/java/App.javaのソースコードを見てみます。

すでにハロワっぽいものが出来上がっています。

まずコンパイルしてみましょう。以下のコマンドを実行します。

するとbuildディレクトリが作成され、コンパイルしたクラスApp.classが以下のディレクトリ構成で作成されます。

次に実行してみましょう。以下のコマンドを実行します。

Hello world.と表示れました。

では、次に、jarを作ってみましょう。以下のコマンドを実行します。

するとbuildディレクトリが作成され、jarファイルが以下のディレクトリ構成で作成されます。

このjarを実行してみましょう。ただし、Manifestファイルを定義していないので、単体で実行することはできません(Manifestファイルを作成する方法もGradleにはありますが、ここでは割愛します)。なので、以下のようにクラスパスを指定する形で実行します。

ヮ(゚д゚)ォ!Hello world.と表示されました。

build.gradleについて

先程手っ取り早くハロワ作ったのですが、その中 で一番大切なファイルbuild.gradleについて見てみたいと思います。

まずプラグインの定義です。

こちらの記述はプラグインを定義しています。Gradleは様々なプラグインを利用します。例えば「gradle complieJava」として、Javaのソースをコンパイルできたのは、ここで、「java」のプラグインを定義しているからです。また「gradle run」として、Javaアプリケーションを実行できたのは、「application」プラグインのおかげです。

次に依存性の定義です。

これは、ビルドするために必要なライブラリを定義しています。アプリケーションをビルドするためには様々なライブラリが必要になります。dependenciesに記載しておけば、ライブラリをインターネット上のリポジトリ(リポジトリについては後述)からダウンロードしてきてくれます。定義の書式がちょっとわかりにくいですが、以下のような形式なんですね。

グループ名は、そのライブラリを開発したり所有したりしている企業や団体、名前はその名の通りライブラリの名前、バージョンはライブラリのバージョンです。com.google.guava:guava:23.0はcom.google.guavaという団体が所有しているguavaという名前のライブラリで、バージョンは23.0という意味になります。

気になるのが「グループ名:名前:バージョン」の前にある「compile」とか「testCompile」です。

compileは、アプリケーションをコンパイルするときに参照・ダウンロードされます。

testCompileは、テストのときだけ参照・ダウンロードされるライブラリを定義します。ここでは、junitが定義されていますが、これはテストのときだけ必要で、アプリケーションビルド時には不要なので、testCompileとしています。

次に、リポジトリの定義です。

これは、JCenterというリポジトリからライブラリをダウンロードしますよという意味です。

これはMavenセントラルリポジトリというリポジトリからライブラリをダウンロードしますよという意味です。

JCenterとMavenセントラルリポジトリのどちらを使っても問題ありません。自分のほしいライブラリがあるリポジトリを使うとよいでしょう。

タスク

Gradleはタスクというのものを定義できます。タスクは関数やメソッドのようなもので、複数の処理をひとまとめにしたものです。今まで「run」とか「compileJava」など実行してきたものは、みんなタスクと呼ばれるものです。このタスクは自ら定義することもできます。

書式は以下のとおりです。

基本のハロワからいきましょう。

以下のようにして実行します。

つまりこんな感じです。

ちゃんとハロワが表示されました。

doFirstとdoLast

Gradleでタスクを定義するときは、だいたいdoFirstもしくはdoLastを定義することが多いようです。doFirstはタスクの一番最初の行う処理、doLastはタスクの一番最後に行う処理です。書式は以下のような感じです。

記述例は以下になります。

実行してみます。

最初にFirst!!が表示されて、次にLast!!が表示されました。

以下のように書いても同じ結果になります。

タスク同士の依存関係

あのタスクを実行するときには、必ずこのタスクの実行後でないとダメといった記述も可能です。以下が書式です。タスク2を実行すると、必ずタスク1が実行された後に、タスク2が実行されるようになります。

実際にやってみましょう。

実行してみます。

greeting2というタスクを実行すると、先に依存関係にあるgreeting1というタスクが実行された後にgreeting2が実行されているのがわかります。

タスクの中で、自身のタスクの情報を取得

タスクを実行するとき、自身のタスクの情報を取得することができます。例えば、実行しているタスク名を取得するには以下のように定義します。

上記のようにタスクのクロージャーの中にtaskを引数としたラムダ式を定義することで実現できます。実行すると以下のような結果になります。

ちなみに以下のように書いても同義です。(型を明示的に指定しただけです)

タスクを定義するときの<<について

タスクを定義するときに、よく<<っていのが出てきます。以下のような感じです。

上記の記法は以下と同義なのです。

ふーん、そうなんだって感じです。Groovyはこんなシンタックスシュガーが多くて、ちょっとわかりにくいです(´・ω・`)

ちなみに<<をつけないでタスクを実行すると、タスクの実行を指定してないにもかかわらず、実行されてしまいます。どういうことかといいますと、

このように定義したタスクを実行してみると、

fugaのタスクを指定してないのに、なぜか実行されています。

以下のように書き直してみます。

hogeタスクを実行してみると・・・

うん、想定通り、hogeしか実行されてないです。

 

カスタムタスク

Gradleではタスクを定義する方法がも一つあります。それがこのカスタムタスクです。前述したtask {}というように、クロージャーの中で定義する方法では、外部からプロパティを与えてタスクの挙動を変えるといったことができません。そんなことを実現するのが、このカスタムタスクです。

書式は以下のとおりです。DefaultTaskというクラスを継承して、カスタムタスクを定義した新たなクラスを作成します。

とりあえず実装してみます。あいさつをするカスタムタスクを定義してみます。

定義したカスタムタスクを使うためには以下のようにします。

つまり、こんな感じになります。

実行してみます。

カスタムタスクで定義したメソッド名greetingを呼び出して、引数に定義した文字列が、そのまま標準出力に表示されます。

Gradleにはいくつかこのようなタスクが標準で実装されています。その中の一つであるCopyタスクを使ってみたいと思います。以下のようにbackupディレクトリを作成して、そこにsrc/main/java/App.javaをコピーします。

build.gradleファイルに以下のタスクを追加します。なんとなくわかると思いますが、fromにコピー元、intoにコピー先を指定します。

以下のように実行すると、backupディレクトリにApp.javaがコピーされます。

includeでコピー対象ファイルを指定することもできます。

excludeでコピー対象から除外することもできます。以下のように書くと、何もコピーされません。

ソースディレクトリと出力先の変更

ソースディレクトリと、ソースをコンパイルしたクラスの出力ディレクトリを変更してみます。以下のような構成のディレクトリを作成します。ソースディレクトリはsrc(デフォルトだとsrc/main/java)、ソースをコンパイルしたクラスの出力ディレクトリはout/classesとします。

build.gradleに以下のように記載します。java.srcDirsにソースディレクトリ、output.classesDirにソースをコンパイルしたクラスの出力ディレクトリを指定します。

コンパイルしてみますと、out/classesにApp.classが出来上がります。

依存関係の管理

ところで、build.gradleに以下の記述があったのを覚えてますでしょうか?

このcompileとかtestCompileというのは、confugrationsオブジェクトというもので管理されています。以下のタスクを実行してみてください。

んで、実行してみてください。

なんか色々出てきました。おなじみのcompileとかtestCompileとか出てきました。では、compileオブジェクトの中には何が入っているのでしょうか?以下のタスクをbuild.gradleに書いてください。

実行してみると・・・

dependenciesのcompileのあとに指定したライブラリ本体及びそれに依存するライブラリのパスが取得できました。つまり、configurationsオブジェクトは依存ライブラリの様々な情報を持っているのです。

ちなみにconfigurationsは自分で定義できたりもします。build.gradleに以下のように書いてください。

加えて以下のように定義をしてください。

以下のタスクを定義してください。先のcompileの部分をconf1に変更しただけです。

上記のタスクを実行します。

dependenciesのところで定義したライブラリの情報が表示されました。compileやcompileTestと同様のものを独自に定義できることがわかりました。

ちなみに依存ライブラリを取得してきて、他のディレクトリにコピーすると言ったことも可能です。以下のタスクを定義することで実現できます。compile時に必要なライブラリ(dependenciesのcompileで指定されたライブラリ)をlibディレクトリにコピーします。

先述したCopyタスクを使っています。「dependsOn: configurations.compile」は、dependenciesのcompileで指定されたライブラリをダウンロードした後に、Copyタスクを実行してくださいという意味です。これを入れないと、当然ながら、ライブラリをダウンロードする前にコピーが走ってしまい、何も起こりません。

実行可能なjarを作ってみる

「まず使ってみる」でもjarを作りましたが、単体では実行できませんでした。今回は単体で実行出来るjarを作ります。

先程のハロワをビルドすることを想定しています。一番最初にご紹介したハロワをビルドするスクリプトと違う点は、jar{}の部分ですね。一つずつ説明します。

jarに含める依存ライブラリを指定します。つまりdependenciesで指定したライブラリをjarに含めたい場合に指定します。configurations.compileは、dependenciesのcompileで指定した依存ライブラリのリストが含まれています。collectメソッドでそれを一つずつ取り出し、続くクロージャーのの中で処理を記載します。

クロージャーの中のitはconfigurations.compile.collectで取り出した依存ライブラリのフルパスが入っています。以下の書き方と同義になります。

引数を省略すると、itという変数にリストの中身が入ってくるようです。

it.isDirectory() ? it : zipTree(it)は、よくある三項演算子で、もしディレクトリだったら何もせず、ディレクトリでなかったら展開する、つまりjarの中身をバラすということになります。

manifestでは、jarのマニフェストファイルに記載する内容を定義します。attributes(‘Main-Class’: ‘App’)はマニフェストファイルの中にMain-Class: Appと記載するのと同じ処理になります。つまりjarを実行したときにmainメソッドがあるクラスを定義しています。

baseName “App”は出来上がるjarファイルの名前を定義します。この場合、App.jarというjarファイルが出来上がります。いつものようにgradle buildしてみますとApp.jarが出来上がります。そのApp.jarを解凍してみると、以下のようなディレクトリ構成になっています。

なるほど、dependenciesで指定したcommons-lang3の中身がバラされています。

MANIFEST.MFの中身を見てみると、

確かにMain-Classが指定されています。実行してみるとたしかにハロワが表示されます。

マルチプロジェクト

MavenではおなじみのマルチプロジェクトをGradleでもやってみます。以下のような構成を考えてみます。

ビルドを統合する親プロジェクト、APIの処理が実装してあるapiプロジェクト、全てのプロジェクトが参照する共通処理が実装してあるcommonプロジェクトという、よくある構成をビルドしてみます。

ファイルの構成はこんな感じです。

まずは、apiプロジェクトのApp.javaから見ていきます。

commonプロジェクトにあるhogeメソッドを呼び出しています。

次に、commonプロジェクトのCommon.javaを見てみます。

apiプロジェクトが参照しているhogeメソッドは、Hello Java World !を標準出力に表示します。

親プロジェクトのbuild.gradleを見てみます。

subproject内に記載された処理は、全てのサブプロジェクトに反映されます。つまり、これは、apiプロジェクトとcommonプロジェクトは、依存関係ライブラリの参照先リポジトリとして、Maven Central Repositoryを見に行きなさいということを表しています。

親プロジェクトのsettings.gradleを見てみます。

サブプロジェクトとして、commonプロジェクトとapiプロジェクトを参照するという設定です。この記述によって、プロジェクトの親子関係を表しています。ちなみにここで指定するのは、サブプロジェクトのTOPのディレクトリ名です。

apiプロジェクトのbuild.gradleを見てみます。

dependenciesの中でcompile project(‘:common’)という記述があります。これは、コンパイル時に、commonプロジェクトを参照しますよという設定です。これをしないと、APIプロジェクトのApp.javaでCommon.hoge()としたときに、メソッドが見つからないというエラーになります。

commonプロジェクトのbuild.gradleを見てみます。

特にこのプロジェクトは特別なことしないので、これだけです。

では、親プロジェクトのディレクトリに移動して、gradle buildしてみましょう。

無事ビルドできました。各プロジェクトのbuild/libsの下にjarができていると思います。

実行してみましょう。

おおヮ(゚д゚)ォ!無事ハロワが表示されました(`・ω・´)シャキーン

最後に

いかがでしょうか?GradleよりもやっぱMavenがいいかなって思うときも、ときどきありますが、きっとそれは私がうまく使いこなせてないからなんです。もっと精進しようと思います。

>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

15人がこの投稿は役に立ったと言っています。

コメント投稿

メールアドレスは表示されません。


*