Gradleに入門する

## Gradle概要

* GroovyベースまたはKotolinベースのDSLで書く
* [プラグイン](https://docs.gradle.org/current/userguide/plugin_reference.html)を活用することで車輪の再発明の防止というか一般的な流れの踏襲ができる
    * 当然だが、自らの環境にあわせてカスタマイズする分は自作する必要がある
    * [サードパーティプラグイン](https://plugins.gradle.org/)
* ビルドタスクを管理する
* 依存関係を管理する
* Antのラッパーになる
* **Gradleラッパーにより個別インストール不要**
    * アップデートは下記のようにコマンド実行すると、次回コマンド実行時に必要なバージョンをダウンロードしてキャッシュされる。

    ```shell:sample_upgrade_wrapper
    # Version 6.1.1は2020/02/07時点最新
    ./gradlew wrapper --gradle-version=6.1.1 --distribution-type=bin
    ```

## 目的

* CIサーバーでビルドに必要なタスクを定義し、実行できるようにする。
    * 取り扱う言語はJava
    * Gradle自体はプラグインによって多種のビルド環境に対応しているが必要になったら学習する方向で放置。他言語でデファクトになっているツールがある場合はそちらを優先するか、ビルドツールを統一するかは状況によるため。

## 環境セットアップ

* [インストール](https://docs.gradle.org/current/userguide/installation.html)
    * Javaセットアップ
    * Gradleダウンロード＆解凍
    * 環境変数へのパス追加
* ~~ズルをして~~課金してIntellij IDEA Ultimateによるプロジェクト作成を行うとGradleプロジェクト構成でスタートでき、その中にGradleラッパーも含まれているのでホントにもうセットアップ不要。
  * この場合はGradle自体ではなくGradleラッパーを使うのでマニュアルなどのコマンドを適宜`gradle`から`./gradlew`などに読み替える必要がある。

## プロジェクト初期化

* DSLはGroovyとKotlinを選択可能。
    * 学習目的ならGroovyのほうが古いドキュメントも参考にしやすいと思われる。
    * 英語に苦手意識がなければ公式にはどちらもサンプルが載っているので問題ないだろう。
    * 実運用ではチームスキルと相談。
* 例えばJUnit5を使うアプリケーション開発なら以下。[Build Init Plugin](https://docs.gradle.org/current/userguide/build_init_plugin.html#sec:java_gradle_plugin)

    ```shell:gradle_init
    gradle init --type java-application --test-framework junit-jupiter
    ```
* プラグインを選択しないでinitした場合にはbuild.gradleに追記すればいいらしい？
    * 単純に`gradle init`しても対話的インターフェースで選択できるので安心。
<details><summary>対話的インターフェース例</summary><div>

    ```shell:対話的インターフェース
    $ gradlew init

    Select type of project to generate:
    1: basic
    2: application
    3: library
    4: Gradle plugin
    Enter selection (default: basic) [1..4] 2

    Select implementation language:
    1: C++
    2: Groovy
    3: Java
    4: Kotlin
    5: Swift
    Enter selection (default: Java) [1..5] 3

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

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

    Project name (default: test): testapp
    Source package (default: testapp): 
    > Task :init
    Get more help with your project: https://docs.gradle.org/6.1.1/userguide/tutorial_java_projects.html

    BUILD SUCCESSFUL in 4m 44s
    2 actionable tasks: 2 executed
    ```
</div></details>
<details><summary>プロジェクト初期構造例</summary><div>

    ```shell:プロジェクト初期構造
    .
    ├── build.gradle.kts
    ├── gradle
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew
    ├── gradlew.bat
    ├── settings.gradle.kts
    └── src
        ├── main
        │   ├── java
        │   │   └── testapp
        │   │       └── App.java
        │   └── resources
        └── test
            ├── java
            │   └── testapp
            │       └── AppTest.java
            └── resources

    11 directories, 8 files
    ```
</div></details>

## 依存関係の管理

* リポジトリはMavenの公開リポジトリがデフォルトで設定されている。
    * もともとあるMaven2を利用する場合は`Pom.xml`と連携してくれるようだが未確認。マニュアルではツールも紹介されている。
* 製品用の依存関係とテスト用の依存関係をそれぞれ定義する。

    ```gradle:build.gradle
    dependencies {
        testImplementation group: 'junit', name: 'junit', version: '4.12'
    }
    ```
* 参考[Gradle の compile, api, implementation とかについて](https://qiita.com/opengl-8080/items/6ad642e0b016465891de)

## フォーマッタ

* checkタスクで実行される。
* GoogleJavaFormatを適用するフォーマッタプラグインを利用。
    * [google-java-format-gradle-plugin](https://github.com/sherter/google-java-format-gradle-plugin)


    ```gradle:build.gradle
    plugins {
      id 'com.github.sherter.google-java-format' version '0.8'
    }
    ```
## 本題：静的コード解析タスク

* checkタスクによって実行する。
* checkstyleプラグインを導入。
    * 主に使用されない変数やプログラムの複雑さなどをチェックする。
    * 一言で言えばコーディング規約を遵守しているかチェックする。
    * [参考](https://takemikami.com/2019/01/20/GradleCheckstyle-SpotBugs-PMDversion.html)を参照してバージョンを指定
    * [参考](https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml)を参照して、google-java-styleを導入

    ```gradle:build.gradle
    plugins {
      id 'checkstyle'
    }
    checkstyle {
      toolVersion = '8.29'
      configFile = rootProject.file(new File(tollSettingsDirectory.toString(), "checkstyle/checkstyle.xml").toString())
    }
    ```
* [SpotBugsプラグイン](https://plugins.gradle.org/plugin/com.github.spotbugs)を導入。
      * バグになりやすい構造をチェックする。

    ```gradle:build.gradle
    plugins {
      id "com.github.spotbugs" version "3.0.0"
    }
    spotbugs {
      toolVersion = "3.1.12"
      excludeFilter = rootProject.file(new File(tollSettingsDirectory.toString(), "spotbugs/exclude_filter.xml").toString())
    }
    ```

## 本題：単体テスト

* testタスクで実行。

## テストカバレッジ

* jacocoTestReportタスクで実行。
* jacocoでリポートを作成
    * jacocoプラグインを導入。
    * Gradleユーザーマニュアルを参照して設定。

    ```gradle:build.gradle
    plugins {
      id 'jacoco'
    }
    jacoco {
      toolVersion = "0.8.5"
      reportsDir = file("${buildDir}/JacocoReports")
    }
    jacocoTestReport {
      reports {
        html.destination file("${buildDir}/JacocoReports/html")
      }
    }
    jacocoTestReport.shouldRunAfter(test)
    ```

## サンプル：フル

```gradle:build.gradle
plugins {
  id 'java'
  id 'com.github.sherter.google-java-format' version '0.8'
  id 'checkstyle'
  id "com.github.spotbugs" version "3.0.0"
  id 'jacoco'
}

File tollSettingsDirectory = new File("${rootProject.projectDir}/config/")

group 'org.example'
version '1.0-SNAPSHOT'

//noinspection GroovyUnusedAssignment
sourceCompatibility = 1.11

repositories {
  mavenCentral()
}

dependencies {
  implementation "org.slf4j:slf4j-api:1.7.30"
  testImplementation "org.slf4j:slf4j-api:1.7.30"
  runtimeOnly "ch.qos.logback:logback-classic:1.2.3"
  testRuntimeOnly "ch.qos.logback:logback-classic:1.2.3"

  testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.0"
  testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.6.0"
}

test {
  useJUnitPlatform()
}

checkstyle {
  toolVersion = '8.29'
  configFile = rootProject.file(new File(tollSettingsDirectory.toString(), "checkstyle/checkstyle.xml").toString())
}

spotbugs {
  toolVersion = "3.1.12"
  excludeFilter = rootProject.file(new File(tollSettingsDirectory.toString(), "spotbugs/exclude_filter.xml").toString())
}

jacoco {
  toolVersion = "0.8.5"
  reportsDir = file("${buildDir}/JacocoReports")
}
jacocoTestReport {
  reports {
    html.destination file("${buildDir}/JacocoReports/html")
  }
}
jacocoTestReport.shouldRunAfter(test)

```

## Jarファイルビルドについて

2020/02/28追記：

Jarファイルをビルドすると依存先が含まれないのでリリースには使いづらいという話があったため追記する。
せっかくmaven2で管理しているのに手動でライブラリ配置するのは面倒。
というわけで参考情報を参照して[Gradle Shadow Plugin](https://imperceptiblethoughts.com/shadow/)を導入する。
（WARの場合は公式のWARプラグインで良さそう。EARはわからん）
この場合はライセンス（再頒布）に引っかからないのかな？っていうのを
きちんと調べておきましょう。

```gradle:build.gradle
plugins {
    id 'com.github.johnrengelman.shadow' version '5.2.0'
}
```

エントリポイントとなる`main()`を実装したクラスをManifestに追加します。

```java:エントリポイントを持つクラスの例
public class Main {
    public static void main(String[] args){
        // なにがしかする。
        new HelloWorld().execute();
    }
}
```

```gradle:build.gradle
jar {
  manifest {
    attributes "Main-Class" : "Main" // フルパッケージ名.クラス名
  }
}
```

shadowJarタスクでビルドする。デフォルトでJarタスクの設定を引き継ぐようだ。

```sh
$ java -jar ./build/libs/sample_tdd-1.0-SNAPSHOT-all.jar 
Hello, World!
```

参考：

* [Gradle: 依存ライブラリ入りのjarを作る](https://qiita.com/suin/items/641c1c1ec9ab5447221e)
* [jar - DSL](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar)

## リファレンス

* [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html)
* [日本語化されたユーザーズガイド](http://gradle.monochromeroad.com/docs/userguide/userguide_single.html)
    * 2012年あたりで更新が止まっているようでバージョンも2.2あたりと古くなっているが、英語が苦手なら概要をつかむのに役立つだろう。大筋を掴んだあと英語の公式ユーザーマニュアルを参照していけばいいと思う。
* [SpotBugs](https://github.com/spotbugs/spotbugs)
* [CheckStyle](https://github.com/checkstyle/checkstyle)