Kotlin + Dagger2 + Orma + Databinding + Android Studio 2.1 に嵌まる

めちゃくちゃ嵌ったので記録しておきます。まとまらないけど。
あと、Kotlinはあまり関係ないかも。

## TL;DR

- dependenciesに書く順番が大事
- Dagger 2.2以降とOrmaを使う場合は`com.google.dagger:dagger-compiler:2.x`より前に`'com.squareup:javapoet:1.6.1'`をapt/kaptに指定する
- Kotlin + databinding + Dagger2 + Android Studio 2.1 Preview 5 以降の組み合わせのとき、`com.google.dagger:dagger-compiler:2.x`や`'com.android.databinding:compiler:2.1.0-[preview|beta]x'`より前に`'com.google.guava:guava:[18.0|19.0]'`をapt/kaptに指定する

## 事の始まり

以下の構成となっているプロジェクトで作業していました。

- Android Studio 2.1 Preview 4 (`com.android.tools.build:gradle:2.1.0-alpha4`)
- Kotlin 1.0.1-2
- Databinding
- Dagger 2.0.2
- Orma 2.4.0

Android Studio 2.1 Preview 5 がリリースされたタイミングで作業環境もアップデート。
すると、エラーが出てコンパイルできなくなりました。

```
java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;
```

Android Studio 2.1 Beta 3がリリースされるまで修正を期待して待ったのですが解決される気配がないので調べました。

## Dagger 2.0.2 + Android Studio 2.1 Preview 5 or after

```
java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;
```

見つからないメソッドはGuava 18.0から

```java
  /**
   * Returns a fluent iterable whose iterators traverse first the elements of this fluent iterable,
   * followed by those of {@code other}. The iterators are not polled until necessary.
   *
   * <p>The returned iterable's {@code Iterator} supports {@code remove()} when the corresponding
   * {@code Iterator} supports it.
   *
   * @since 18.0
   */
  @Beta
  @CheckReturnValue
  public final FluentIterable<E> append(Iterable<? extends E> other) {
    return from(Iterables.concat(iterable, other));
  }
```

[このissue](https://code.google.com/p/android/issues/detail?id=205589)によるとDaggerで使われているGuavaは18.0だが、それより下のバージョンが使われているらしい。
なるほど？

```
$ ./gradlew app:dependencies --configuration=kapt

kapt
+--- org.jetbrains.kotlin:kotlin-annotation-processing:1.0.1-2
|    \--- org.jetbrains.kotlin:kotlin-stdlib:1.0.1-2
|         \--- org.jetbrains.kotlin:kotlin-runtime:1.0.1-2
+--- com.android.databinding:compiler:2.1.0-beta3
|    +--- com.android.databinding:baseLibrary:2.1.0-beta3
|    \--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
+--- com.github.gfx.android.orma:orma-processor:2.4.0
|    +--- com.github.gfx.android.orma:orma-annotations:2.4.0
|    \--- com.squareup:javapoet:1.6.1
+--- com.google.dagger:dagger-compiler:2.0.2
|    +--- com.google.dagger:dagger:2.0.2
|    |    \--- javax.inject:javax.inject:1
|    +--- com.google.dagger:dagger-producers:2.0-beta
|    |    +--- com.google.dagger:dagger:2.0 -> 2.0.2 (*)
|    |    \--- com.google.guava:guava:18.0
|    \--- com.google.guava:guava:18.0
(以下省略)

(*) - dependencies omitted (listed previously)
```

確かにDaggerが依存しているGuavaは18.0だ。

```
$ ./gradlew app:assembleDebug --debug | grep guava > temp.txt

01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder] Visiting dependency com.android.tools.build:gradle-api:2.1.0-beta3(runtime) -> com.google.guava:guava:17.0(runtime)
01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder] Selecting new module version com.google.guava:guava:17.0
01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainComponentMetaDataResolver] Attempting to resolve component for com.google.guava:guava:17.0 using repositories [BintrayJCenter, maven, maven2]
...
```

どうやら、build tools の gradle plugin が Guava 17.0に依存していて、どういうわけかビルド時にもそちらが使われてしまうらしい。

issueにはDaggerを2.1以上にすれば解決すると書かれているので最新の2.3で試してみることにする。

## Dagger 2.3 + Orma 2.4.0

しかし、ビルドエラーになる。

```
java.lang.NoSuchMethodError: com.squareup.javapoet.TypeSpec.classBuilder(Lcom/squareup/javapoet/ClassName;)Lcom/squareup/javapoet/TypeSpec$Builder;
```

今度はJavaPoetのメソッドが見つからないらしい...
これについても[issueが上がっていた](https://github.com/google/dagger/issues/349)。

DaggerがJavaPoetを内蔵しているせいで古いJavaPoetが使われてしまうらしい。
Orma作者の\_\_gfx__さんもこう言ってた。

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">OrmaとDagger 2.2を一緒に使うとJavaPoetでNoMethodErrorがでるの、dagger-compiler.jar にある古いJavaPoetが参照されるせいです。Daggerをダウングレードすると動くはず。 <a href="https://t.co/laMQuq98O8">https://t.co/laMQuq98O8</a></p>&mdash; FUJI Goro (@__gfx__) <a href="https://twitter.com/__gfx__/status/716931965950173184">April 4, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

ただ、こんなTweetも見つけたので

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">お、kaptにjavapoet直接指定したら最新のdaggerでormaビルドできるっぽいぞ</p>&mdash; せーい(yshrsmz) (@_yshrsmz) <a href="https://twitter.com/_yshrsmz/status/719534212110426112">April 11, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

試しに以下のようにkaptでjavapoetを明示してみた。

```groovy
dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    kapt "com.android.databinding:compiler:2.1.0-beta3"

    compile("com.github.gfx.android.orma:orma:2.4.0") {
        exclude group: 'com.tunnelvisionlabs', module: 'antlr4-runtime'
        exclude group: 'com.tunnelvisionlabs', module: 'antlr4-annotations'
    }
    kapt "com.github.gfx.android.orma:orma-processor:2.4.0"
    kapt 'com.squareup:javapoet:1.6.1'

    compile 'com.google.dagger:dagger:2.3'
    kapt 'com.google.dagger:dagger-compiler:2.3'
...
```

すると…

```
java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSetMultimap$Builder.putAll(Ljava/lang/Iterable;)Lcom/google/common/collect/ImmutableSetMultimap$Builder;
```

さっきまでとは違うエラーだ...
ちなみに、Dagger 2.2に下げると

```
java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;
```

最初の問題、解決してなかったじゃんか...

## Dagger 2.3 + Databinding

**(2016/04/21追記)**
このエラーが出る原因については[こちらのコメント](http://qiita.com/chibatching/items/e5a0f5016093abec884b#comment-db4f7a63993d10c5ab47)参照
**(追記ここまで)**

こちらの記事([AndroidでKotlinを使ったDagger2](http://qiita.com/kamedon39/items/d530cf69369cc4371a3f))にもあるように、`ImmutableSetMultimap$Builder.putAll`が見つからないのはDagger 2.3からのようです。

[issueのコメント](https://github.com/google/dagger/issues/356#issuecomment-207989795)によると、`dagger-compiler`より先に`guava`を明示するとよいとのこと。
ただ、自分の構成ではkaptのdatabindingより先に明示しないと動きませんでした。

```groovy
dependencies {

    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

    kapt 'com.google.guava:guava:19.0'
    kapt "com.android.databinding:compiler:2.1.0-beta3"

    compile("com.github.gfx.android.orma:orma:2.4.0") {
        exclude group: 'com.tunnelvisionlabs', module: 'antlr4-runtime'
        exclude group: 'com.tunnelvisionlabs', module: 'antlr4-annotations'
    }
    kapt "com.github.gfx.android.orma:orma-processor:2.4.0"
    kapt 'com.squareup:javapoet:1.6.1'

    compile 'com.google.dagger:dagger:2.3'
    kapt 'com.google.dagger:dagger-compiler:2.3'
...
}
```

ここまでやってついにビルドが成功しました...
この状態からDaggerを2.2や2.0.2にダウングレードしても問題なくビルドできます。今までの苦労はいったい...

## JavaPoetの指定場所

ビルドには成功したのですが、dependenciesに書く順番によって有効になったりならなかったりするのが割と衝撃でした。
そして、気になってDaggerとOrmaの問題で追加した`JavaPoet`の指定を`dagger-compiler`より後ろにしたらどうなるのか試してみると...

```
java.lang.NoSuchMethodError: com.squareup.javapoet.TypeSpec.classBuilder(Lcom/squareup/javapoet/ClassName;)Lcom/squareup/javapoet/TypeSpec$Builder;
```

やはりこちらも指定の順番が重要なようです。

消耗した...

## おわりに

dependenciesに書く順番が結果に影響するのが意外すぎました。aptこわい。