Edited at

Android plugin for Gradle 2.0.0 に更新したら Data Binding ライブラリ周りで ProGuard が警告を吐いてビルドできなくなった問題

More than 3 years have passed since last update.

アプリが依存するライブラリと com.android.databinding:compiler の依存先ライブラリがかぶっていると ProGuard が警告を吐いてビルドできなくなることがある。 多くのプロジェクトではこの問題は起こらないと思う。

最終的にどうするのがいいかまではわかってないし、詳しいこともわかっておらず原因について推測してる箇所が多いけど、メモ程度に書き残しておく。


発生する問題

例えば、compile configuration の依存に com.google.guava:guava:19.0 が含まれていると、ProGuard の処理で以下のような警告が出て、:app:transformClassesAndResourcesWithProguardForRelease タスクが失敗する。 (Android Plugin for Gradle 2.0.0 を使って、Data Binding 有効で、ProGuard 有効な場合。)

Warning: library class android.databinding.tool.util.SourceCodeEscapers$1 extends or implements program class com.google.common.escape.CharEscaper

Warning: library class android.databinding.tool.util.SourceCodeEscapers$JavaCharEscaper extends or implements program class com.google.common.escape.ArrayBasedCharEscaper
Warning: library class android.databinding.tool.util.SourceCodeEscapers$JavaCharEscaperWithOctal extends or implements program class com.google.common.escape.ArrayBasedCharEscaper

下記でも同様の問題が書かれている。


原因に関する前提


Data Binding ライブラリと provided configuration への依存関係の追加

Data Binding ライブラリを有効にしている (build.gradle に android { dataBinding { enabled = true } } を記述) と、provided configuration に com.android.databinding:compiler の依存が追加される。 これは、ビルド時にのみ使われるツールで、レイアウトファイルを基に YourLayoutBinding クラスみたいなやつを生成するなどの処理に使われるっぽい。 (詳細は追いかけてない。)

Android Plugin for Gradle 1.5.0 のころは、以下のような依存関係になっていた。

$ ./gradlew :app:dependencies

(略)
provided - Classpath for only compiling the main sources.
\--- com.android.databinding:compiler:1.0-rc5
+--- com.android.databinding:baseLibrary:1.0-rc5
\--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
(略)

Android Plugin for Gradle 2.0.0 では、com.android.databinding:compiler のバージョンも更新されており、以下のようになる。

$ ./gradlew :app:dependencies

(略)
provided - Classpath for only compiling the main sources.
\--- com.android.databinding:compiler:2.0.0
+--- com.android.databinding:compilerCommon:2.0.0
| +--- com.android.databinding:baseLibrary:2.0.0
| +--- com.tunnelvisionlabs:antlr4:4.5
| | +--- com.tunnelvisionlabs:antlr4-runtime:4.5
| | | +--- org.abego.treelayout:org.abego.treelayout.core:1.0.1
| | | \--- com.tunnelvisionlabs:antlr4-annotations:4.5
| | +--- com.tunnelvisionlabs:antlr4-annotations:4.5
| | +--- org.antlr:antlr-runtime:3.5.2
| | \--- org.antlr:ST4:4.0.8
| | \--- org.antlr:antlr-runtime:3.5.2
| +--- commons-io:commons-io:2.4
| +--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
| \--- com.google.guava:guava:17.0
+--- com.android.databinding:baseLibrary:2.0.0
+--- org.jetbrains.kotlin:kotlin-stdlib:1.0.0
| \--- org.jetbrains.kotlin:kotlin-runtime:1.0.0
+--- commons-io:commons-io:2.4
+--- commons-codec:commons-codec:1.10
+--- com.tunnelvisionlabs:antlr4:4.5 (*)
\--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
(略)


ProGuard と provided configuration

ProGuard が入力として受け付ける JAR (や WAR、AAR など) ファイルには、input jarlibrary jar の 2 種類がある。

input jar は、アプリケーションに含まれるクラスファイルを含むもので、これらが処理されて出力される。

library jar の方は出力には含まれない。 「input jar の中のクラスがが依存するが、出力には含めない」というようなものを library jar として指定するようになっている。

さて、Android Plugin for Gradle には ProGuard の処理を行うタスクが定義されるが、その ProGuard の設定としては provided configuration に含められている依存関係を ProGuard の library jar として渡すようになっているっぽい。

ProGuardTransform クラス のコードをちょっと見たけど、追いかけられてない。


原因

上に書いたように、provided configuration に含まれる com.android.databinding:compiler:2.0.0com.google.guava:guava:17.0 に依存するが、多分 compile configuration の com.google.guava:guava の方が新しくて、そっちを使うという風に ProGuard がみなすのだと思う。 そうすると、library jar 側の android.databinding.tool.util.SourceCodeEscapers が input jar 側の Guava に依存することになって、『library class ... depends on program class ...』 の警告が発生するのだと思う。


対応


とりあえずの対応

com.android.databinding:compiler:2.0.0 はビルド時のみに使われるものなので、単に警告を無視してやれば良さそう。 うちのプロジェクトでは以下のような記述を proguard-rules.pro に追加した。

# Data Binding library

# ====================
# * Data Binding ライブラリを有効にすると、ビルド時に使われる
# (`com.android.databinding:compiler:2.0.0`) が `provided` configuration に追加される。
# * さらに `provided` configuration の依存が ProGuard の library jars に追加されるっぽい。
# * `com.android.databinding:compiler:2.0.0` は `com.google.guava:guava:17.0` などに依存して
# いるが、それらのライブラリのより新しいバージョンが `compile` configuration にも含まれて
# いると、ProGuard の処理時に 「library class ... depends on program class ...」 という
# ような警告が発生してビルドが止まってしまうっぽい。

-dontwarn android.databinding.tool.util.**


根本的には?

根本的には、以下のどちらかが必要そう?


  • Data Binding ライブラリのビルド時用のツールを provided configuration の依存に含めない。


  • provided configuration の依存を ProGuard の library jar に含めない。

provided configuration がどういう目的のものかによって上記のどちらがいいかが違ってくると思うけど、本来の provided configuration というのは 「実行環境により提供されるのでプログラム側には含めなくていいけどコンパイル時に参照する必要があるもの」 を含めるところだと思うので、それだとすると前者の対応ができるといいのかなぁと思う。