Gradle初心者によるGradle事始め

# はじめに
この記事は[G*Advent Calendar(Groovy,Grails,Gradle,Spock...) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/gastah) の22日目の記事です。

今回は、Gradleを使ってJavaのソースコードをコンパイルしてJARファイルにまとめてテストを実行するまでを書こうと思います。
（IDEに依存しないためにコンソールベースで記述しますのであしからず）

# Gradleとは？
Gradleとは、Groovyで記述するビルド自動化システムです。
Javaで古くから使われていたAntやMavenはXMLで記述するのに対し、GradleはGroovyで記述できるため柔軟で強力なビルド自動化システムと言えます。

## 「規約によるビルド」
MavenやGradleは「規約によるビルド」システムと言われています。
一方、古くからあるmakeやAntは「手続き的なビルド」システムと言われています。

「手続き的なビルド」はソースファイルがあるディレクトリや生成ファイルの出力先のディレクトリを逐次指定する必要がありましたが、「規約によるビルド」はシステム側に一定のルール（規約）があり、ビルドシステムを利用する我々はそのルールに則ってソースファイルなどを配置します。
ルールに則っていればスクリプトが簡潔だというのが、「規約によるビルド」を採用しているGradleの強みです。Groovyが使えるという点と併せれば、もう最強と言えるでしょう。

# Gradleのインストール
下記の手順でGradleのインストールと確認を行います。

## 必要な環境
現在（2016/12/22）のGradle最新版は3.2.1です。
GradleはJVM言語なのでJavaの環境が必要で、3.2.1はJava 7以上のJDKもしくはJREが必要です。
手元のPCは「java version "1.8.0_111"」なので、以下ではこの環境で試します。

## ダウンロード
[Gradleのダウンロードサイト](https://gradle.org/gradle-download/) から [Complete distribution](https://services.gradle.org/distributions/gradle-3.2.1-all.zip) もしくは [Binary only distribution](https://services.gradle.org/distributions/gradle-3.2.1-bin.zip)をダウンロードします。
Complete distributionにはドキュメント類も含まれているので、今回は [Complete distribution](https://services.gradle.org/distributions/gradle-3.2.1-all.zip) をダウンロードします。

## 解凍と環境変数設定
ダウンロードしたZIPファイルを適当なディレクトリに解凍して、解凍したディレクトリまでを `GRADLE_HOME` という環境変数に設定します。
併せてGRADLE_HOMEのbinディレクトリまでを `PATH` 環境変数に追加します。

## インストール出来たかテストする
コンソールを立ち上げて `gradle -version` を実行し、Gradleがインストール出来たかテストします。

```
# gradle -version

------------------------------------------------------------
Gradle 3.2.1
------------------------------------------------------------

Build time:   2016-11-22 15:19:54 UTC
Revision:     83b485b914fd4f335ad0e66af9d14aad458d2cc5

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_111 (Oracle Corporation 25.111-b14)
OS:           Windows 7 6.1 x86
```

このようにバージョン情報が表示されればOKです。
Gradleを入れると、一緒にGroovyとAntが入るようですね。

## プロキシ設定
仕事場でGradleを使う場合、プロキシサーバを設定しないと後々困るのでここで設定します。
今回はユーザごとにGradleの設定を施す `~/.gradle/gradle.properties` ファイルにプロキシ設定を記述します。

```gradle.properties
systemProp.http.proxyHost=xxx.xxx.xxx.xxx
systemProp.http.proxyPort=9999
systemProp.https.proxyHost=xxx.xxx.xxx.xxx
systemProp.https.proxyPort=9999
```

# Gradleのキホン
設定が完了したので、本題に入ります。
以下では適当なところに `GradleSample` というディレクトリを作成し、その中で各種Gradleのコマンドを実行します。

## Gradleの入り口
GradleはPATH環境変数に設定したディレクトリにある `gradle` コマンドからすべての操作を実行します。
（すでに `gradle -version` は実行しましたね）

手始めに引数なしの `gradle` コマンドを実行してみましょう。

```
# gradle
Starting a Gradle Daemon (subsequent builds will be faster)
:help

Welcome to Gradle 3.2.1.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 14.365 secs
```

引数なしのgradleコマンドは何もしないので、gradleコマンドの簡単なヘルプが出力されます。

ビルドをしたければ `gradle <task>` を実行し、
利用可能なタスクを表示したければ `gradle tasks` を実行し、
コマンドラインオプションを表示したければ `gradle --help` を実行し、
それぞれのタスクについて詳細が知りたければ `gradle help --task <task>` を実行しろと言っています。

\<task\>の部分には具体的なタスク名を記述します。
この「タスク」がGradleのキモとなる部分です。
次にこのタスクについて見てみます。

## タスク
いまいるGradleSampleディレクトリに `build.gradle` というファイルを作って、テキストエディタで以下の内容を書き込みます。

```build.gradle
task hello << {
    println 'Hello world!'
}
```

見て迷うことはないでしょうが、そのスクリプトにいま「hello」というタスクがあり、そのタスクは `Hello world!` を出力します。
これをgradleコマンドで実行します。
build.gradleはGradleがデフォルトで読み込むビルドスクリプトですので、gradleコマンドにはスクリプトファイル名を指定する必要はありません。
また今回はログ出力を抑制して内容をすっきりさせるために `-q` オプションを付けます。
実行するコマンドは `gradle -q hello` です。

```
# gradle -q hello
Hello world!
```

意図した結果が出力されました。

タスクには、ある処理のまとまりを記述出来ます。
このタスクを使って、「コンパイルをする」や「テストを実行する」などの処理を行ないます。

## タスクの依存関係
Gradleに限らず、ビルドシステムには「タスクの依存関係」は必要不可欠な概念です。
例えばテストを実行するためにはコンパイルが完了していなければなりません。
これは「テストを実行する」というタスクは「コンパイルをする」というタスクに依存している状態です。
この依存関係を、Gradleでは以下のように記述します。

```build.gradle
task runCompile << {
    println 'Run compile.'
}

task executeTest(dependsOn: runCompile) << {
    println 'Execute test.'
}
```

タスク名の後ろに `(dependsOn: xxx)` を記述し、`xxx` の部分に依存するタスク名を指定します。

このビルドスクリプトを実行します。
指定するタスクは「テストを実行する」ため `gradle -q executeTest` です。

```
# gradle -q executeTest
Run compile.
Execute test.
```

executeTestを実行する前に、依存しているrunCompileを実行しているのが分かると思います。

## タスクの一覧
Gradleはタスクを指定して実行することは分かっていただけたでしょうが、そのタスクはビルドスクリプトを開かなくても `gradle -q tasks` を実行すれば分かります。

```
# gradle -q tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Other tasks
-----------
executeTest

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>
```

`Other tasks` のところに今回作成したビルドスクリプトの内容があります。
おや？でも `executeTest` しかありません。 `runCompile` はどこにあるのでしょう？
今度は `gradle -q tasks --all` を実行し、すべての情報を出力します。

```
# gradle -q tasks --all

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Other tasks
-----------
executeTest
    runCompile
```

`executeTest` の次にインデントされて `runCompile` が出力されました。
これが「タスクの依存関係」を表しています。
ほかの依存関係の表現として行の最後の `[xxxx]` があるのですが、これは後ほど説明します。


# Gradleを使ったJavaのビルド
以下ではGradleを使ったJavaソースコードのビルド方法を記します。
先程まで使っていた `build.gradle` は不要なので削除しましょう。

## サンプルのプロジェクトを作成
Gradleは `init` タスクでJava/Groovy/Scalaの雛形プロジェクトを作成することが出来ます。

```
# gradle help --task init
:help
Detailed task information for init

Path
     :init

Type
     InitBuild (org.gradle.buildinit.tasks.InitBuild)

Options
     --type     Set type of build to create.
                Available values are:
                     basic
                     groovy-library
                     java-library
                     pom
                     scala-library

     --test-framework     Set alternative test framework to be used.
                          Available values are:
                               spock
                               testng

Description
     Initializes a new Gradle build. [incubating]

Group
     Build Setup

BUILD SUCCESSFUL

Total time: 1.498 secs
```

今回はJavaのプロジェクトを作成したいので、`gradle init --type java-library` を実行します。

```
# gradle init --type java-library
:wrapper
:init

BUILD SUCCESSFUL

Total time: 2.481 secs
```

実行すると、各種ファイルが生成されます。

```
│  build.gradle
│  gradlew
│  gradlew.bat
│  settings.gradle
│
├─.gradle
│  └─3.2.1
│      └─taskArtifacts
│              fileHashes.bin
│              fileSnapshots.bin
│              taskArtifacts.bin
│              taskArtifacts.lock
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    ├─main
    │  └─java
    │          Library.java
    │
    └─test
        └─java
                LibraryTest.java
```

色々なディレクトリやファイルが作成されますが、重要なのは `build.gradle` ファイルと、`src` ディレクトリです。
（他のディレクトリとファイルは今回説明を省略します）

## ビルドスクリプトの確認
作成された `build.gradle` ファイルを見てみます。

```build.gradle
/*
 * This build file was auto generated by running the Gradle 'init' task
 * by '???' at '16/12/21 23:19' with Gradle 3.2.1
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/3.2.1/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.21'

    // Declare the dependency for your favourite test framework you want to use in your tests.
    // TestNG is also supported by the Gradle Test task. Just change the
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
    // 'test.useTestNG()' to your build script.
    testCompile 'junit:junit:4.12'
}
```

コメントが多いので今回はコメントを削除します。

```build.gradle
apply plugin: 'java'

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    testCompile 'junit:junit:4.12'
}
```

すっきりしました。

## Javaプラグイン
Javaのビルドスクリプトで重要なのが、先頭の `apply plugin: 'java'` で、これを記述するとJavaのビルドに必要な各種タスクが読み込まれます。

他にも、.projectのようなEclipse固有の設定ファイルを生成する `apply plugin: 'eclipse'` や、Groovyの `apply plugin: 'groovy'` や、 Scalaの `apply plugin: 'scala'` などがありますが、今回は省略します。

## リポジトリの指定
Javaのプログラムを書く上で、Spring BootやJUnitやSLF4Jなどのライブラリを使用しないことはないでしょう。
Antの時代は各ライブラリの提供元のサイトから必要なJARファイルをダウンロードしてコンパイル時にローカルのパスを指定していましたが、Mavenの時代からそれはセントラルリポジトリと呼ばれるリモートに置かれ、必要に応じてビルドシステムが自動的にダウンロードするようになりました。
Mavenであれば[Maven Central Repository](http://search.maven.org/)からダウンロードします。

GradleはGradle固有のセントラルリポジトリがあるわけではなく、Mavenなど既存のセントラルリポジトリからライブラリをダウンロードします。
どのセントラルリポジトリを使用するかは、ビルドスクリプトの下記で指定します。

```build.gradle
repositories {
    jcenter()
}
```

これは「BintrayのJCenterにある依存関係を探すための定義済みリポジトリ」を意味します。
今回はMavenセントラルリポジトリを参照したいので、これを以下のように変更します。

```build.gradle
repositories {
    mavenCentral()
}
```

## 依存関係の定義
リポジトリからどのライブラリを使用するのかを定義するのが、ビルドスクリプトの下記の部分です。

```build.gradle
dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    testCompile 'junit:junit:4.12'
}
```

ここで言う「依存関係」はライブラリの「依存関係」です。
タスクの「依存関係」ではありません。

`compile` で指定するのはテスト時とコンパイル時に使用するライブラリ、
`testCompile` で指定するのはテスト時に使用するライブラリです。

ライブラリの指定は、コロン区切りで `'GroupId : ArtifactId : Version'` の3つが必要です。
この3つはMavenセントラルリポジトリにあるものを指定します。

## タスクの確認
この状態でタスクの一覧を確認します。

```
# gradle tasks --all
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project. [jar]
build - Assembles and tests this project. [assemble, check]
buildDependents - Assembles and tests this project and all projects that depend on it. [build]
buildNeeded - Assembles and tests this project and all projects it depends on. [build]
classes - Assembles main classes.
    compileJava - Compiles main Java source.
    processResources - Processes main resources.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes. [classes]
testClasses - Assembles test classes. [classes]
    compileTestJava - Compiles test Java source.
    processTestResources - Processes test resources.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code. [classes]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'.
components - Displays the components produced by root project 'GradleSample'. [incubating]
dependencies - Displays all dependencies declared in root project 'GradleSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'.
dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'GradleSample'. [incubating]
projects - Displays the sub-projects of root project 'GradleSample'.
properties - Displays the properties of root project 'GradleSample'.
tasks - Displays the tasks runnable from root project 'GradleSample'.

Verification tasks
------------------
check - Runs all checks. [test]
test - Runs the unit tests. [classes, testClasses]

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

BUILD SUCCESSFUL

Total time: 1.529 secs
```

Java用のプラグインを指定しているので、`Build tasks` などが追加されています。
ビルドする場合、`build` タスクを指定します。

ここで先程保留した依存するタスクの話をします。
`build` タスクの右にある `[assemble, check]` となっている部分が依存しているタスクです。
`build` タスクの前に `assemble` タスク（プロジェクトのすべてのアーカイブを構築）と `check` タスク（プロジェクトのすべての検証タスクを実行）が実行されます。
`assemble` タスクの前には `jar` タスク（当該プロジェクトのJARファイルを構築）が実行され、 
`jar` タスクの前には `classes` タスク（製品クラスディレクトリを構築）が実行され、
`classes`タスクの前には `compileJava` タスク（javacを使って製品Javaソースファイルをコンパイル）と `processResources` タスク（製品リソースを製品クラスディレクトリにコピー）が実行されます。

## 規約
Gradleは「規約によるビルド」をするビルドシステムだと言いましたが、Javaプラグインを使用する場合は下記のようなディレクトリの規約があります。

|ディレクトリ        |意味            |
|------------------|-----------------|
|src/main/java     |製品のJavaソース  |
|src/main/resources|製品のリソース    |
|src/test/java     |テストのJavaソース|
|src/test/resources|テストのリソース  |

ここでもう一度 `init` タスクで作成したディレクトリを見てみます。

```
└─src
    ├─main
    │  └─java
    │          Library.java
    │
    └─test
        └─java
                LibraryTest.java
```

`init` タスクで作成された「製品のJavaソース」ディレクトリと「テストのJavaソース」ディレクトリが、規約に則ったディレクトリだということがわかってもらえるかと思います。

## ビルドの実行
`gradle build` でビルドを実行しましょう。

```
# gradle build
:compileJava
Download https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.pom
Download https://jcenter.bintray.com/org/slf4j/slf4j-parent/1.7.21/slf4j-parent-1.7.21.pom
Download https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 10.611 secs
```

実行したタスクが列挙されています。
`compileJava` から始まり、`build` で終わりました。

最初は依存ライブラリのダウンロードが発生するかもしれません。
もしプロキシを使用する環境で実行した場合、設定に不備があるとダウンロードに失敗するはずなので、設定を見直してください。

ビルド時に生成されるファイルは `build` ディレクトリにあります。
今回のテンプレートソースのJARファイルは `build/libs/GradleSample.jar` です。

実行したタスクに `test` があるのでテストは実行されたはずですが、念のためにテストクラスでエラーを起こしてみます。
`test/java/LibraryTest.java` を修正します。
修正前は下記のソースでした。

```java:LibraryTest.java
public class LibraryTest {
    @Test public void testSomeLibraryMethod() {
        Library classUnderTest = new Library();
        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
    }
}
```

これにエラーとなる処理を入れます。

```java:LibraryTest.java
public class LibraryTest {
    @Test public void testSomeLibraryMethod() {
        Library classUnderTest = new Library();
        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());

        assertTrue(false);
    }
}
```

`assertTrue(false);` でテストは失敗するはずです。
`gradle build` を実行します。

```
# gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

LibraryTest > testSomeLibraryMethod FAILED
    java.lang.AssertionError at LibraryTest.java:14

1 test completed, 1 failed
:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:xxx/GradleSample/build/reports/tests/test/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.343 secs
```

テストが失敗しました。

なのでここまでで最初に言った「JavaのソースコードをコンパイルしてJARファイルにまとめてテストを実行するまで」が達成しました。

駆け足ですが、Gradle事始めは以上です。

# 参考にすべきサイト・書籍・スライド
最後に、自分はGradleを使う上で下記を参考にしています。

## 参考にすべきサイト
* [オフィシャル](https://gradle.org/documentation/)
* [日本語訳](http://gradle.monochromeroad.com/docs/)

今回の記事のほとんどは[日本語訳](http://gradle.monochromeroad.com/docs/)を基に書きました。

## 参考にすべき書籍
* [Gradle徹底入門](http://www.shoeisha.co.jp/book/detail/9784798136431)

## 参考にすべきスライド
* Gradle徹底入門の著者の一人、綿引さんの[スライド](http://www.slideshare.net/bikisuke)