Gradleの基礎まとめ

Java
Java

ビルドツールのGradleについてまとめていきます。

Gradleとは

主にJavaのプロジェクトを管理するビルドツールの一つです。
ビルドツールとは、「コンパイルや実行時に必要な外部ライブラリを自動的にダウンロードしてきてくれる」「プロジェクトをビルドしてjarファイルやwarファイルを作ってくれる」といった働きをするツールです。

同様の役割を果たすビルドツールとして、他にAntMavenなどがありますが、これらはXMLで記述するため習得の難易度が高いことが難点です。一方GradleはGroovyというJVMで動くスクリプト言語を採用しています。可読性に優れ、より柔軟に処理を記述できる面が優れています。Groovyの書き方はJavaと共通する部分が多いので、Javaを理解していれば学習コストはさほどかかりません。

公式

Gradle 日本語ドキュメント

Gradleをインストール

筆者の環境はWindowsなので、ZIPファイルをダウンロード⇒解凍してインストールします。

導入環境
  • Windows 10 Home 64bit

ダウンロード

Gradleのダウンロードページにて、Zipファイルをダウンロードします。
特段理由がなければ、ページ一番上の最新バージョンをダウンロードでいいと思います。今回はv6.8.2のbinary-onlyを選択してダウンロードしました。

インストール

Zipファイルを解凍します。今回はCドライブ直下に解凍しました。

続いてパスを通します。「コントロールパネル」⇒「システム」⇒「システムの詳細設定」⇒「環境変数」で設定ページにたどり着けます。システム環境変数に下記の二つを設定します。

  • GRADLE_HOMEC:\gradle-6.8.2(新規)
  • Path%GRADLE_HOME%\bin(追加)
  • GRADLE_HOMEに設定するのはGradleを解凍したフォルダです。ご自身の環境にあわせて読み替えてください。

ここまで完了したら、コマンドで動作確認をしましょう。

c:\>gradle -version

Welcome to Gradle 6.8.2!

Here are the highlights of this release:
 - Faster Kotlin DSL script compilation
 - Vendor selection for Java toolchains
 - Convenient execution of tasks in composite builds
 - Consistent dependency resolution

For more details see https://docs.gradle.org/6.8.2/release-notes.html


------------------------------------------------------------
Gradle 6.8.2
------------------------------------------------------------

Build time:   2021-02-05 12:53:00 UTC
Revision:     b9bd4a5c6026ac52f690eaf2829ee26563cad426

Kotlin:       1.4.20
Groovy:       2.5.12
Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM:          1.8.0_242 ( 25.242-b08)
OS:           Windows 10 10.0 amd64

ダウンロードしたv6.8.2がインストールされたことが確認できました。

Gradleプロジェクトを作成

いろいろ試してみるためのGradleプロジェクトを作成します。今回はJavaのプロジェクトを作りますが、Scalaなど他の言語のプロジェクトを作成、管理することも可能です。

プロジェクトを作成するためのフォルダを事前に作成しておきます。今回は、後ほどEclipseから編集することを考えて、Eclipseのワークスペース内にGradleAppという名前でフォルダを作成しました。Eclipse等のIDEを利用している場合は、そのワークスペース内に作成することをオススメします。

コマンドでカレントディレクトリを作成したフォルダに移し、下記コマンドを実行します。

D:\workspace\GradleApp>gradle init --type java-application

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 4

Project name (default: GradleApp):

Source package (default: GradleApp): gradle.app


> Task :init
Get more help with your project: https://docs.gradle.org/6.8.2/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 43s
2 actionable tasks: 2 executed

gradle init --type java-applicationを実行すると、そのフォルダ内に新規プロジェクトを作成します。言語は--typeオプションにて指定できます。省略した場合は、対話形式で選択肢から選ぶ形になります。
作成が始まると対話形式で内容を指定する必要があります。上から、

  • Select build script DSLビルドスクリプトの言語を選択します。
  • Select test frameworkテストコードのフレームワークを選択します。
  • Project nameプロジェクト名を指定します。そのままでいいでしょう。
  • Source packagejavaソースのパッケージ名を指定します。大文字入りはマズいので変更します。

すべての質問に回答したら、プロジェクトが作成されます。

GradleApp
├─.gradle
├─app
│  ├─src
│  │   ├─main
│  │   │  ├─java
│  │   │  │  └─gradle
│  │   │  │      └─app
│  │   │  │         └─App.java
│  │   │  └─resources
│  │   └─test
│  └─build.gradle
└─gradle
    └─wrapper
  • 上記のツリーは一部省略しています。
  • どこかのバージョンからか、appディレクトリという存在ができたみたいです。(どのバージョンからか調べられてないけど、比較的最近っぽい?)srcディレクトリやbuild.gradleなんかはこちらのフォルダ内に移動したみたいです。
    なんか違和感。。。

とにかく、プロジェクトが作成されました。このプロジェクト内のbuild.gradleを編集しながら遊んでいきましょう。

build.gradle

build.gradleでHello World

病めるときも健やかなるときも、いついかなるときも始まりはHello Worldからです。

作成したプロジェクト内のapp/build.gradleというファイルを開き、下記のように記述します。

task hello {
    doLast {
        println 'Hello world!'
    }
}
  • 作成された初期状態でいろいろ書いてありますが、一旦消してしまって大丈夫です。

gradle -q helloを実行します。

D:\workspace\GradleApp> gradle -q hello
Hello world!

Hello Worldできました!

ざっと説明すると、①gradleコマンドを実行すると、カレントディレクトリ内のbuild.gradleファイルを探して参照する。②そのファイル内で定義されているhelloタスクを探して実行する。という流れで処理されていきます。
Gradleに仕事をさせるときは、基本的にこのタスクを定義して呼び出すという形になります。

doLastとdoFirst

先ほどのhelloタスクに、doLastというものがありましたね。これはタスク内の処理の実行順番を制御するために使われるものです。他にもdoFirstもあったりします。例を見てみましょう。

task hello {
    doLast {
        println 'Last!'
    }
    doFirst {
        println 'First!'
    }
}

このhelloタスクを実行すると、

D:\workspace\GradleApp>gradle -q hello
First!
Last!

このように、doFirstに定義した処理が先に行われ、doLastに定義した処理が後から行われました。処理を定義した順番と入れ替わっているので、処理の実行順番が制御されたことがわかりますね。

一つのdoLastに定義された処理は、一つのActionというものとして扱われます。タスクというのは、このActionの集合体(リスト)だと捉えてください。doFirstはこのリストの先頭にActionを差し込み、doLastはこのリストの最後尾にActionを差し込みます。このActionという単位でタスクを見ると、処理の流れが追いやすくなるかなと思います。
以下のタスクの実行結果を見ると、理解しやすくなるかもしれません。

task count {
    doFirst {
        println '1'
    }
    doFirst {
        println '2'
    }
    doLast {
        println '3'
    }
    doLast {
        println '4'
    }
}
D:\workspace\GradleApp>gradle -q count
2
1
3
4

見事にカウントに失敗していますね。1のdoFirstよりも後に、2のdoFirstが定義されているため、2の方が先に実行されてしまったということになります。doLastの方は3より4の方が後に定義されているので、ちゃんと順番通りになっていますね。

あまり経験が多くないので確かなことは言えないのですが、doLastの方にメインの処理を書くのが通説なのかなって認識です。自分の携わった案件でもメインの処理はdoLastで書いていたし、公式さんのHello WorldもdoLastだし。定義した順番に処理されるからわかりやすいし。困ったらdoLastに書いておけば間違いないと思います。

複数のタスクを連続で実行

単一のタスクだけでなく、複数のタスクを連続して実行させることも可能です。まずは2つのタスクを連続で実行させてみましょう。

task sleep {
    doLast {
        println 'Good night'
    }
}

task wakeUp {
    dependsOn 'sleep'
    doLast {
        println 'Good morning'
    }
}
D:\workspace\GradleApp>gradle -q wakeUp
Good night
Good morning

Gradleでは、タスクの前後関係は後に実行するタスクの方に定義します。dependsOnという語句で定義しています。考え方としては、「寝た後に起きる」ではなく「起きるためには寝なければいけない」となります。後に実行するタスクが前に実行するタスクに依存しているという依存関係をdependsOnで定義したことになります。

ビルドライフサイクル

build.gradleを書いてGradleを動かす際には、このビルドライフサイクルを意識しておく必要があります。簡単に言うとGradleが実行されるときには3つのフェーズに分かれるよというものです。表で説明しますね。

フェーズ概要
初期化Gradleを動かす準備をしています。特に意識する必要はありません。
設定build.gradle内のすべての設定フェーズのスクリプトを実行していきます。
実行指定されたタスクのActionを実行していきます。

上から下の順番で実行されていきます。サンプルコードでその流れを追っていきましょう。

println 'setting 1'

task hoge {
    println 'setting 2'
    doLast {
        println 'action 1'
    }
}

task fuga {
    println 'setting 3'
    dependsOn 'hoge'
    doLast {
        println 'action 2'
    }
}

task piyo {
    println 'setting 4'
    doLast {
        println 'action 3'
    }
}
D:\workspace\GradleApp>gradle -q fuga
setting 1
setting 2
setting 3
setting 4
action 1
action 2

先に設定フェーズの処理、その後実行フェーズの処理が行われていることがわかります。タスクの定義の外にあるsetting 1や、実行されていないはずのpiyoタスク内のsetting 4も実行されています。つまり、タスクの中だから安心と思ってdoLastdoFirstの中でないところにゴリゴリ処理を記述すると、想定外のタイミングでその処理が実行されて大変なことになります。気をつけてくださいませ。

Javaプロジェクトを操作してみる

長々と基礎の構文について学んできました。いよいよ、ビルドツールの神髄を味わいましょう!

プラグインを追加する

プラグインとは、ある目的に沿った設定やタスク定義などを集約したものです。Gradleはこのプラグインを使用する前提で考えられており、何もプラグインを使用していない状態ではどの言語のビルドもできません。

Javaで開発をするのであれば、Javaのプラグインを読み込んで使用しましょう。build.gradleにこのように記述します。

apply plugin: "java"

apply()メソッドに引数としてplugin: 'java'を渡しています。このメソッドは、プロジェクトで使用するプラグインまたはスクリプトを設定する働きをします。

apply(plugin: 'java')

と書かれていたほうが直感的にメソッドだとわかりやすいかもしれませんね。Groovyではメソッド呼び出しの括弧を省略することができます。また、引数を名前付きで渡しています。pluginが名前で'java'が引数の値です。名前を指定できることで、引数の順番を意識する必要がなくなります。

ちなみにこのapply()というメソッドは、GradleのProjectというオブジェクトで定義されています。Projectとは、Gradleの処理の中心的な機能を提供するクラスです。詳細は公式ガイドをご覧ください。
build.gradleでは、projectクラスが持つプロパティやメソッドを暗黙的に使用することができるようになっています。

タスクが追加されたことを確認するために、プラグイン追加前後でのgradle tasksコマンドの実行結果を比較してみましょう。

------Before------    ------After------

Build Setup tasks     Build tasks
-----------------     -----------
init                  assemble
wrapper               build
                      buildDependents
Help tasks            buildNeeded
----------            classes
buildEnvironment      clean
dependencies          jar
dependencyInsight     testClasses
help
javaToolchains        Build Setup tasks
outgoingVariants      -----------------
projects              init
properties            wrapper
tasks
                      Documentation tasks
                      -------------------
                      javadoc

                      Help tasks
                      ----------
                      buildEnvironment
                      dependencies
                      dependencyInsight
                      help
                      javaToolchains
                      outgoingVariants
                      projects
                      properties
                      tasks

                      Verification tasks
                      ------------------
                      check
                      test

ビルドタスクなど多くのタスクが追加されたことが確認できますね。

依存関係を定義する

Javaアプリは、例にもれず外部のライブラリに依存して作られるものです。依存先のライブラリはjarファイルに固められているのですが、使うjarファイルをすべてインターネットからダウンロードしてクラスパスの通ったフォルダに配置して、、なんてことはやってられません。その辺をGradleは良しなにやってくれます。AntやMavenでもしていたことですね。

依存関係の定義は リポジトリの設定依存ライブラリの設定 の流れで行います。
build.gradleを下記のように編集しましょう。

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
}

上記の例にあるrepositoriesdependenciesも、projectクラスに定義されたその名前のメソッドを呼び出しています。
では、上から順にみていきましょう!

リポジトリ設定:クロージャ

repositories {
    mavenCentral()
}

このrepositoriesもメソッドであり、それ以降の波括弧{}の中身の処理が引数という扱いになります。このように、処理自体を受け取るメソッドをスクリプトブロックといいます。波括弧の中の処理はクロージャといいます。Gradleでは、多くの設定がこのスクリプトブロックによって記述されます。ですので、この仕組みは覚えておきましょう。
クロージャについて理解を深めるためには、クロージャが持つdelegateという変数について知る必要があります。delegate変数にはオブジェクトを設定することができ、設定されたオブジェクトのプロパティやメソッドは、そのクロージャ内から参照することができます。

例えば、このrepositoriesスクリプトブロックのクロージャは、delegateRepositoryHandlerを持っています。
公式リファレンスのRepositoriesスクリプトブロック

delegateに入っているRepositoryHandlerに定義されているメソッドを見てみると、そこにはmavenCentral()があることが確認できます。このメソッドを呼び出す処理を記述していたのですね。
公式リファレンスのRepositoryHandlerオブジェクトのmavenCentral()

Mavenリポジトリを使って依存関係を解決するための定型文として覚えられがちですが、ちゃんと仕組みを理解しておくと応用が効くようになりますね。

mavenCentral()のような引数を受け取らないメソッドでは、括弧()を省略することはできません。

依存関係:Configration

dependencies {
    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
}

リポジトリと同様にdependenciesスクリプトブロックにクロージャを渡しています。
クロージャ内では、依存関係を指定するconfigurationと、依存する対象を指定しています。上の例でいうと、依存関係がimplementationで、その対象がgroup: 'org.apache.commons'...になります。対象は、そのファイルパスを指定するか、Mavenリポジトリ等を参照してダウンロードしてきます。上記はリポジトリからApacheのcommons-lang3というライブラリをダウンロードして参照する例となります。

Javaプラグインを使用すると、いくつかのコンフィグレーションをデフォルトで使用することができます。下表にていくつか代表的なものを紹介します。

コンフィグレーション動作
implementation依存するライブラリをコンパイル時のクラスパスに含め、さらにビルドされたjarファイルなどにこのライブラリを含めます。何を使うか迷ったらとりあえずこれを書いておけばいいと思います。
api基本的な動作はimplementationと同じですが、apiだとモジュールを関連する他のモジュールにここで指定したライブラリが伝播します。全モジュールにライブラリが反映される分、ビルド時間が長くなってしまいます。慎重に使いましょう。
なお、使用するにはjava-libraryプラグインが必要です。
compileOnlyコンパイル時のクラスパスにのみ含まれ、ビルド時には追加されません。実行時には不要だがコンパイルには必要(Lombokとか)なライブラリに使用します。
runtimeOnlyコンパイル時には追加されず、ビルド時にのみ追加されます。JDBCドライバなどが該当するかと思います。
testImplementation動作はimplementationと同じですが、こちらはテスト実行時にのみ反映される依存関係となります。Junitなどのテストライブラリの指定などに使います。
おそらく多くのbuild.gradleで使用されているコンフィグレーションとしてcompileというものがありますが、これはGradle3.4あたりで非推奨となりました。compileapiと同じで依存関係が伝播するコンフィグレーションです。伝播の必要性に応じてimplementationapiを使い分けるようにしましょう。
また、コンフィグレーションは自分で定義することもできます。
configurations {
    sampleImpl
}

dependencies {
    sampleImpl group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
}
  • 自分で定義したコンフィグレーションはimplementationと同様の動作になるのかな?非推奨のcompileと同じとかでなければいいけど、、、わかったら追記します。

依存する対象のライブラリの指定方法については、記法を省略することができます。また、ローカルにファイルがある場合は、そのパスを指定することもできます。

dependencies {
    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
    // 省略記法
    implementation 'org.apache.commons:commons-lang3:3.11'
    // ローカルファイルの指定
    implementation files('lib/commons-lang3-3.1.jar')
}

これで、自由に依存関係を定義できるようになりました!

EclipseからGradleを実行する

プロジェクトのインポート

せっかく好きなライブラリを参照できるようになったので、EclipseなどのIDEの入力補助が欲しくなってきますね。
というわけで、このJavaプロジェクトをEclipseにインポートできるようにしましょう!

これもGradleのプラグインを使用します。方法は簡単です。
build.gradleに下記の記述を追加してください。

apply plugin: 'java'
// eclipseプラグイン
apply plugin: 'eclipse'

追加したら、eclipseプラグインが持つeclipseタスクをコマンドから実行します。

D:\workspace\GradleApp>gradle eclipse

BUILD SUCCESSFUL in 7s
3 actionable tasks: 3 executed

実行が完了すると、Eclipseへのインポートに必要な.projectファイルや.classpathファイルが自動的に生成されます。

Eclipseを開き、このプロジェクトをインポートしましょう。
「パッケージ・エクスプローラー」を右クリックして、「インポート…」を選択します。

「既存プロジェクトをワークスペースへ」を選択し、「次へ」を押下します。

「ルート・ディレクトリの選択」で、現在参照しているワークスペースのフォルダを選択します。すると、下の「プロジェクト」ボックス内に対象のプロジェクトが表示されます。チェックがついていることを確認して「完了」を押下します。

プロジェクトがインポートできました!

ビルドして実行

先ほど定義した依存関係を利用して、Javaソースを編集して、ビルドして実行しましょう。

App.javaを下記のように編集します。

package gradle.app;

import org.apache.commons.lang3.RandomStringUtils;

public class App {
    public String getGreeting() {
        String randomStr = RandomStringUtils.randomAlphanumeric(30);
        return "Random String : " + randomStr;
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}

実行すると、コンソールに結果が表示されます。

Random String : QpqBEqBJNu37a2LY6OCtOGIhQDT3XV
Random String : yfpedc7kv6AP1f867TFoU2qygYOAVO
Random String : sqi0cegy67u5cJo8CTx3SVVjOnd1Ll
Random String : UxXy9uzsEZUxCRTgs7bYkXWfSqfWtr
Random String : l6mbDtcSrx5N045qpBreUZCUXv0BKm

依存関係に含めたcommons-lang3ライブラリのRandomStringUtilsがコンパイルされて動作していることが確認できました!

gradle wrapper

gradle wrapperとは、Gradleをインストールしなくてもタスクの実行などが可能になる仕組みです。これを利用することで、チーム開発で全員がGradleをインストールして環境構築をしなくても、誰かひとりがbuild.gradleを準備してGitにpushしておけば、それをpullした全員が同じタスクをすぐに使うことができるようになります。

使用方法は簡単です。コマンドでカレントディレクトリをGradleプロジェクト直下に移動し、下記のようにコマンドを実行します。

D:\workspace\GradleApp>gradlew -version

------------------------------------------------------------
Gradle 6.8.2
------------------------------------------------------------

Build time: 2021-02-05 12:53:00 UTC
Revision: b9bd4a5c6026ac52f690eaf2829ee26563cad426

Kotlin: 1.4.20
Groovy: 2.5.12
Ant: Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM: 1.8.0_242 ( 25.242-b08)
OS: Windows 10 10.0 amd64

D:\workspace\GradleApp>gradlew build

BUILD SUCCESSFUL in 5s
2 actionable tasks: 2 executed

今までgradleというコマンドを使用していたのをgradlewに変更するだけです。

gradle initにてプロジェクトを作成した際に、プロジェクト内にgradlew(Linux、Mac用)、gradlew.bat(Windows用)という2つのファイルが生成されていました。実はこれがコマンドの実行ファイルであり、これを呼び出すと後はどうにかしてくれるという優れものなのです。CIツール上でGradleでビルドをする場合などにも効果的ですね。


Gradleの基礎知識についてまとめてみました。ビルドツールというのは総じて学習コストが高いことものですが、GradleはJavaエンジニアには比較的親和性が高く、なんとか基礎は押さえられました。
これからも精進していきます。何卒。

参考

Groovyを知らない人のためのbuild.gradle読み書き入門 – Qiita
Gradle使い方メモ – Qiita
多分わかりやすいGradle入門
ビルドを設定する – Android デベロッパー
Gradle入門

コメント

タイトルとURLをコピーしました