Maven の provided のような依存指定を Gradle でやってみる

# 概要
Maven の provided スコープのような、「コンパイル時には含めるが Jar には入れない」という依存を記述したかったので、どうやるか調べてみました。

## 目的
あるアプリケーションAに別のアプリケーションBのコードをjarにして同梱する際、BのjarにAでも利用しているライブラリが入っていると、Aをfat jarにした際に重複してしまって無駄が発生するので、B の依存解決で provided スコープでの依存指定ができるようにしたい、という事情がありました。

```
 ------------A-----------         ------------B-----------
| lib:lib.commons:0.0.1 |         | lib:lib.commons:0.0.1 | <---この lib.commons-0.0.1 を B.jar から除外したい
|          B.jar        |         ------------------------
 ------------------------
```

## 作業環境

| Java SE | 1.8.0_102
|:---|:---
| OS      | Windows 10 
| Gradle  | 3.0

----
# 作業

## provided
まず configurations ブロックで provided を宣言しておきます。

```groovy:configurations
configurations {
    provided
}
```

## 依存の記述
続いて、dependencies で provided の依存を記述します。

```groovy:dependencies
dependencies {
    provided 'com.jfoenix:jfoenix:1.0.0'
    provided 'org.slf4j:slf4j-api:1.7.21'
    provided 'org.slf4j:slf4j-log4j12:1.7.21'
……
```

## compileJava の記述
provided をコンパイルのクラスパスに含めます。

```groovy:compileJava
compileJava {
    sourceSets.main.compileClasspath += configurations.provided
    sourceSets.test.compileClasspath += configurations.provided
}
```

## テストのクラスパスに追加
忘れるとテスト実行時に大量の ```java.lang.NoClassDefFoundError``` が出ます。

```groovy:test
test {
    classpath += configurations.provided
}
```

## (任意) application プラグインを使っている場合
run にも入れておきます。

```groovy:run
run {
    classpath += configurations.provided
}
```

## (任意) Eclipse 上で依存解決したい場合
下記を追加してください。

```groovy:build.gradleに追記
apply plugin: 'eclipse'

……

eclipse {
    classpath {
        plusConfigurations += [ configurations.provided ]
    }
}
```

### 参考
- [EclipseClasspath](https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseClasspath.html)

## jar タスクの修正
compile のものだけ jar に含めるように指定します。

```groovy:jar
jar {
……
    from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
……
```

----
# 実践
今回は [JVM 言語のスクリプトを実行するアプリケーション](https://github.com/toastkidjp/ScriptRunner/tree/qiita) のコードを使って試してみます。

## (共通) jar生成
修正前後で同じコマンドを使います。

```:jar生成
$ gradle clean jar
Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details
:clean
:compileJava
:processResources
:classes
:jar

BUILD SUCCESSFUL

Total time: 12.758 secs
```

## 修正前
jar のサイズは 24.58MB、jar のファイル数は 15,850 でした。

```
$ ls -l ScriptRunner-0.0.1.jar
-rw-r--r-- 1 John Kay 197121 24580947 Oct 30 15:31 ScriptRunner-0.0.1.jar

$ ls -lh ScriptRunner-0.0.1.jar
-rw-r--r-- 1 John Kay 197121 24M Oct 30 15:31 ScriptRunner-0.0.1.jar

$ jar tf ScriptRunner-0.0.1_.jar | wc -l
15850
```

## 修正
jar に含めない依存を compile から provided に変更します。

https://github.com/toastkidjp/ScriptRunner/commit/f3249eab157fc615ec64c25a58be9ce366660520

## 修正後
```$ gradle clean jar``` で jar を作り直します。

```
$ ls -l ScriptRunner-0.0.1.jar
-rw-r--r-- 1 John Kay 197121 10647049 Oct 30 15:34 ScriptRunner-0.0.1.jar

$ ls -lh ScriptRunner-0.0.1.jar
-rw-r--r-- 1 John Kay 197121 11M Oct 30 15:34 ScriptRunner-0.0.1.jar

$ jar tf ScriptRunner-0.0.1.jar | wc -l
8018
```

生成された jar はファイルサイズが 10.647MB、ファイル数が 8,018 でした。確実に小さくなっているのがわかります。

----
# まとめ
プラグイン等は必要なく、 Gradle 標準の仕組みだけで、 Maven の provided スコープのような依存指定ができました。フレームワークの依存が必要なアプリケーション開発(Storm等)で使えると思います。

----

# 参考
- [3. Maven 入門 (2) 3.1.1. 依存性の指定とスコープ](http://www.techscore.com/tech/Java/ApacheJakarta/Maven/3/#maven-3-1-1)