気をつけたいGradleの推移的依存関係とその解決


Gradleはライブラリの依存関係を上手に管理してくれるすぐれものですが、まれに突拍子もない不具合が発生する場合があります。しばらく前にGradleの推移的依存関係でハマったことがあったので、その具体的現象と原因、解決方法についてまとめておきました。

#推移的依存関係とは？
[Gradleのドキュメント](http://gradle.monochromeroad.com/docs/userguide/dependency_management.html)読んでも、ほへー、位の感想なんで、[こちらのサイト](http://kkoudev.github.io/blog/2014/03/30/gradle-tips/)がわかりやすかったです。端的に言うと、複数のライブラリがあるライブラリ(A)に依存している場合、Aはバージョンが一番最新のものが適用されるということです。  
なお推移的(transtive)とは言葉として？？って感じでしたが、数学用語なんですね。[wikipedia](https://ja.wikipedia.org/wiki/%E6%8E%A8%E7%A7%BB%E9%96%A2%E4%BF%82)で説明を読むとなるほどなーという感じです。

#具体的な事象
ある時、Pureeのバージョンアップ(3.3.0 -> 4.1.0)したところ、releaseのみビルドがfailしました。理由はPuree内で `javax.annotation.ParametersAreNonnullByDefault` が参照できないからとのこと。

```ssh
/home/travis/build/project/testProject/app/src/main/java/com/testProject/test/log/OutBufferedLogcat.java:54: error: cannot find symbol @Nonnull
error: cannot access ParametersAreNonnullByDefault
  class file for javax.annotation.ParametersAreNonnullByDefault not found
```

`ParametersAreNonnullByDefault` から参照しているライブラリを確認すると、なぜか Crashlytics内の`annotation`を参照しています。Crashlyticsのannotationには `ParametersAreNonnullByDefault` がないため、エラーが発生しているというわけです。

![Screen Shot 2015-12-11 at 18.36.43.png](https://qiita-image-store.s3.amazonaws.com/0/39433/9ffbcce9-611f-530e-dc59-aa7b01e97d1f.png "Screen Shot 2015-12-11 at 18.36.43.png")


なお以下がbuild.gradleの依存関係(抜粋)です。

```groovy:build.gradle
dependencies {
    compile('com.crashlytics.sdk.android:crashlytics:2.5.0@aar') {
        transitive = true;
    }
    devCompile 'com.facebook.stetho:stetho:1.1.1'
    devCompile 'com.facebook.stetho:stetho-urlconnection:1.1.1'
    compile 'com.cookpad.puree:puree:4.1.1'
    configurations.all {
        resolutionStrategy.force 'com.android.support:support-annotations:23.0.1'
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
    }
}
```

resolutionStrategyで `support-annotations`と`findbug`については強制的にバージョン適用されているとわかります。

#原因
結論から言いますと、ReleaseビルドではGradleが推移的関係にあるライブラリを探した結果、 **"該当するものあったけどちょっと違うやつだわー、ゴメン!"** というエラーになっているということです。

依存関係は以下のコマンドで調べられます。

```
./gradlew :app:dependencies
```
gradleの:app:dependenciesを丹念に調べた結果、以下のことがわかりました。

* `javax.annotation.ParametersAreNonnullByDefault`は`com.google.code.findbugs:jsr305:3.0.0` 内のannotationが利用されている

* releaseビルドには`com.google.code.findbugs:jsr305:3.0.0` が入ってない。理由としては
   * Release以外の場合はStethoを導入している。Stethoは内部的に `com.google.code.findbugs:jsr305:3.0.0` に依存しているため、推移的依存が適用されて`javax.annotation.ParametersAreNonnullByDefault`が参照できる。  
    
  ```cpp:dependencies
+--- com.facebook.stetho:stetho:1.1.1
|    +--- commons-cli:commons-cli:1.2
|    \--- com.google.code.findbugs:jsr305:2.0.1 -> 3.0.0
+--- com.facebook.stetho:stetho-urlconnection:1.1.1
|    \--- com.google.code.findbugs:jsr305:2.0.1 -> 3.0.0
  ```  
  * build.gradle内ではconfigurations.allで`com.google.code.findbugs`のversionの強制が設定されているものの、compileには入ってないため、Releaseには`javax.annotation`が入っていないです。


  ```groovy:build.gradle
configurations.all {
    resolutionStrategy.force 'com.android.support:support-annotations:23.0.1'
    resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
}
  ```

* `com.google.code.findbugs`が入っていないReleaseビルドは推移的依存性によりCrashlyticsにあるannotation配下が参照されます。結果、そこには`javax.annotation.ParametersAreNonnullByDefault`がないのでエラーになります。  

#解決策
`compile 'com.google.code.findbugs:jsr305:3.0.0'` をdependenciesに追加すれば、依存するバージョンが正しくなり、`javax.annotation.ParametersAreNonnullByDefault`が参照されるようになりました。  

#まとめ
わけのわからない依存関係のエラーがでた場合は、

* なにはともあれ`dependencies tree`で丹念に依存状況とバージョンを確認する
* 場合によってはライブラリを追加、あるいはライブラリから推移的依存関係となってしまうライブラリをexcludeする

ことをオススメします。